SDL  2.0
SDL_pulseaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 
22 /*
23  The PulseAudio target for SDL 1.3 is based on the 1.3 arts target, with
24  the appropriate parts replaced with the 1.2 PulseAudio target code. This
25  was the cleanest way to move it to 1.3. The 1.2 target was written by
26  Stéphan Kochen: stephan .a.t. kochen.nl
27 */
28 #include "../../SDL_internal.h"
29 #include "SDL_assert.h"
30 
31 #if SDL_AUDIO_DRIVER_PULSEAUDIO
32 
33 /* Allow access to a raw mixing buffer */
34 
35 #ifdef HAVE_SIGNAL_H
36 #include <signal.h>
37 #endif
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <pulse/pulseaudio.h>
41 
42 #include "SDL_timer.h"
43 #include "SDL_audio.h"
44 #include "../SDL_audio_c.h"
45 #include "SDL_pulseaudio.h"
46 #include "SDL_loadso.h"
47 #include "../../thread/SDL_systhread.h"
48 
49 #if (PA_API_VERSION < 12)
50 /** Return non-zero if the passed state is one of the connected states */
51 static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
52  return
53  x == PA_CONTEXT_CONNECTING ||
54  x == PA_CONTEXT_AUTHORIZING ||
55  x == PA_CONTEXT_SETTING_NAME ||
56  x == PA_CONTEXT_READY;
57 }
58 /** Return non-zero if the passed state is one of the connected states */
59 static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
60  return
61  x == PA_STREAM_CREATING ||
62  x == PA_STREAM_READY;
63 }
64 #endif /* pulseaudio <= 0.9.10 */
65 
66 
67 static const char *(*PULSEAUDIO_pa_get_library_version) (void);
68 static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto) (
69  pa_channel_map *, unsigned, pa_channel_map_def_t);
70 static const char * (*PULSEAUDIO_pa_strerror) (int);
71 static pa_mainloop * (*PULSEAUDIO_pa_mainloop_new) (void);
72 static pa_mainloop_api * (*PULSEAUDIO_pa_mainloop_get_api) (pa_mainloop *);
73 static int (*PULSEAUDIO_pa_mainloop_iterate) (pa_mainloop *, int, int *);
74 static int (*PULSEAUDIO_pa_mainloop_run) (pa_mainloop *, int *);
75 static void (*PULSEAUDIO_pa_mainloop_quit) (pa_mainloop *, int);
76 static void (*PULSEAUDIO_pa_mainloop_free) (pa_mainloop *);
77 
78 static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
79  pa_operation *);
80 static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
81 static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
82 
83 static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
84  const char *);
85 static int (*PULSEAUDIO_pa_context_connect) (pa_context *, const char *,
86  pa_context_flags_t, const pa_spawn_api *);
87 static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_list) (pa_context *, pa_sink_info_cb_t, void *);
88 static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_list) (pa_context *, pa_source_info_cb_t, void *);
89 static pa_operation * (*PULSEAUDIO_pa_context_get_sink_info_by_index) (pa_context *, uint32_t, pa_sink_info_cb_t, void *);
90 static pa_operation * (*PULSEAUDIO_pa_context_get_source_info_by_index) (pa_context *, uint32_t, pa_source_info_cb_t, void *);
91 static pa_context_state_t (*PULSEAUDIO_pa_context_get_state) (pa_context *);
92 static pa_operation * (*PULSEAUDIO_pa_context_subscribe) (pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *);
93 static void (*PULSEAUDIO_pa_context_set_subscribe_callback) (pa_context *, pa_context_subscribe_cb_t, void *);
94 static void (*PULSEAUDIO_pa_context_disconnect) (pa_context *);
95 static void (*PULSEAUDIO_pa_context_unref) (pa_context *);
96 
97 static pa_stream * (*PULSEAUDIO_pa_stream_new) (pa_context *, const char *,
98  const pa_sample_spec *, const pa_channel_map *);
99 static int (*PULSEAUDIO_pa_stream_connect_playback) (pa_stream *, const char *,
100  const pa_buffer_attr *, pa_stream_flags_t, pa_cvolume *, pa_stream *);
101 static int (*PULSEAUDIO_pa_stream_connect_record) (pa_stream *, const char *,
102  const pa_buffer_attr *, pa_stream_flags_t);
103 static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state) (pa_stream *);
104 static size_t (*PULSEAUDIO_pa_stream_writable_size) (pa_stream *);
105 static size_t (*PULSEAUDIO_pa_stream_readable_size) (pa_stream *);
106 static int (*PULSEAUDIO_pa_stream_write) (pa_stream *, const void *, size_t,
107  pa_free_cb_t, int64_t, pa_seek_mode_t);
108 static pa_operation * (*PULSEAUDIO_pa_stream_drain) (pa_stream *,
109  pa_stream_success_cb_t, void *);
110 static int (*PULSEAUDIO_pa_stream_peek) (pa_stream *, const void **, size_t *);
111 static int (*PULSEAUDIO_pa_stream_drop) (pa_stream *);
112 static pa_operation * (*PULSEAUDIO_pa_stream_flush) (pa_stream *,
113  pa_stream_success_cb_t, void *);
114 static int (*PULSEAUDIO_pa_stream_disconnect) (pa_stream *);
115 static void (*PULSEAUDIO_pa_stream_unref) (pa_stream *);
116 
117 static int load_pulseaudio_syms(void);
118 
119 
120 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
121 
122 static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
123 static void *pulseaudio_handle = NULL;
124 
125 static int
126 load_pulseaudio_sym(const char *fn, void **addr)
127 {
128  *addr = SDL_LoadFunction(pulseaudio_handle, fn);
129  if (*addr == NULL) {
130  /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
131  return 0;
132  }
133 
134  return 1;
135 }
136 
137 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
138 #define SDL_PULSEAUDIO_SYM(x) \
139  if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
140 
141 static void
142 UnloadPulseAudioLibrary(void)
143 {
144  if (pulseaudio_handle != NULL) {
145  SDL_UnloadObject(pulseaudio_handle);
146  pulseaudio_handle = NULL;
147  }
148 }
149 
150 static int
151 LoadPulseAudioLibrary(void)
152 {
153  int retval = 0;
154  if (pulseaudio_handle == NULL) {
155  pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
156  if (pulseaudio_handle == NULL) {
157  retval = -1;
158  /* Don't call SDL_SetError(): SDL_LoadObject already did. */
159  } else {
160  retval = load_pulseaudio_syms();
161  if (retval < 0) {
162  UnloadPulseAudioLibrary();
163  }
164  }
165  }
166  return retval;
167 }
168 
169 #else
170 
171 #define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
172 
173 static void
174 UnloadPulseAudioLibrary(void)
175 {
176 }
177 
178 static int
179 LoadPulseAudioLibrary(void)
180 {
181  load_pulseaudio_syms();
182  return 0;
183 }
184 
185 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
186 
187 
188 static int
189 load_pulseaudio_syms(void)
190 {
191  SDL_PULSEAUDIO_SYM(pa_get_library_version);
192  SDL_PULSEAUDIO_SYM(pa_mainloop_new);
193  SDL_PULSEAUDIO_SYM(pa_mainloop_get_api);
194  SDL_PULSEAUDIO_SYM(pa_mainloop_iterate);
195  SDL_PULSEAUDIO_SYM(pa_mainloop_run);
196  SDL_PULSEAUDIO_SYM(pa_mainloop_quit);
197  SDL_PULSEAUDIO_SYM(pa_mainloop_free);
198  SDL_PULSEAUDIO_SYM(pa_operation_get_state);
199  SDL_PULSEAUDIO_SYM(pa_operation_cancel);
200  SDL_PULSEAUDIO_SYM(pa_operation_unref);
201  SDL_PULSEAUDIO_SYM(pa_context_new);
202  SDL_PULSEAUDIO_SYM(pa_context_connect);
203  SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
204  SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list);
205  SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index);
206  SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index);
207  SDL_PULSEAUDIO_SYM(pa_context_get_state);
208  SDL_PULSEAUDIO_SYM(pa_context_subscribe);
209  SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback);
210  SDL_PULSEAUDIO_SYM(pa_context_disconnect);
211  SDL_PULSEAUDIO_SYM(pa_context_unref);
212  SDL_PULSEAUDIO_SYM(pa_stream_new);
213  SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
214  SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
215  SDL_PULSEAUDIO_SYM(pa_stream_get_state);
216  SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
217  SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
218  SDL_PULSEAUDIO_SYM(pa_stream_write);
219  SDL_PULSEAUDIO_SYM(pa_stream_drain);
220  SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
221  SDL_PULSEAUDIO_SYM(pa_stream_peek);
222  SDL_PULSEAUDIO_SYM(pa_stream_drop);
223  SDL_PULSEAUDIO_SYM(pa_stream_flush);
224  SDL_PULSEAUDIO_SYM(pa_stream_unref);
225  SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
226  SDL_PULSEAUDIO_SYM(pa_strerror);
227  return 0;
228 }
229 
230 static SDL_INLINE int
231 squashVersion(const int major, const int minor, const int patch)
232 {
233  return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
234 }
235 
236 /* Workaround for older pulse: pa_context_new() must have non-NULL appname */
237 static const char *
238 getAppName(void)
239 {
240  const char *verstr = PULSEAUDIO_pa_get_library_version();
241  if (verstr != NULL) {
242  int maj, min, patch;
243  if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
244  if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
245  return NULL; /* 0.9.15+ handles NULL correctly. */
246  }
247  }
248  }
249  return "SDL Application"; /* oh well. */
250 }
251 
252 static void
253 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
254 {
255  /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
256  if (mainloop && o) {
257  SDL_bool okay = SDL_TRUE;
258  while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
259  okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
260  }
261  PULSEAUDIO_pa_operation_unref(o);
262  }
263 }
264 
265 static void
266 DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
267 {
268  if (context) {
269  PULSEAUDIO_pa_context_disconnect(context);
270  PULSEAUDIO_pa_context_unref(context);
271  }
272  if (mainloop != NULL) {
273  PULSEAUDIO_pa_mainloop_free(mainloop);
274  }
275 }
276 
277 static int
278 ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
279 {
280  pa_mainloop *mainloop = NULL;
281  pa_context *context = NULL;
282  pa_mainloop_api *mainloop_api = NULL;
283  int state = 0;
284 
285  *_mainloop = NULL;
286  *_context = NULL;
287 
288  /* Set up a new main loop */
289  if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
290  return SDL_SetError("pa_mainloop_new() failed");
291  }
292 
293  *_mainloop = mainloop;
294 
295  mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
296  SDL_assert(mainloop_api); /* this never fails, right? */
297 
298  context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
299  if (!context) {
300  return SDL_SetError("pa_context_new() failed");
301  }
302  *_context = context;
303 
304  /* Connect to the PulseAudio server */
305  if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
306  return SDL_SetError("Could not setup connection to PulseAudio");
307  }
308 
309  do {
310  if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
311  return SDL_SetError("pa_mainloop_iterate() failed");
312  }
313  state = PULSEAUDIO_pa_context_get_state(context);
314  if (!PA_CONTEXT_IS_GOOD(state)) {
315  return SDL_SetError("Could not connect to PulseAudio");
316  }
317  } while (state != PA_CONTEXT_READY);
318 
319  return 0; /* connected and ready! */
320 }
321 
322 static int
323 ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
324 {
325  const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
326  if (retval < 0) {
327  DisconnectFromPulseServer(*_mainloop, *_context);
328  }
329  return retval;
330 }
331 
332 
333 /* This function waits until it is possible to write a full sound buffer */
334 static void
335 PULSEAUDIO_WaitDevice(_THIS)
336 {
337  struct SDL_PrivateAudioData *h = this->hidden;
338 
339  while (SDL_AtomicGet(&this->enabled)) {
340  if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
341  PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
342  PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
344  return;
345  }
346  if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
347  return;
348  }
349  }
350 }
351 
352 static void
353 PULSEAUDIO_PlayDevice(_THIS)
354 {
355  /* Write the audio data */
356  struct SDL_PrivateAudioData *h = this->hidden;
357  if (SDL_AtomicGet(&this->enabled)) {
358  if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
360  }
361  }
362 }
363 
364 static Uint8 *
365 PULSEAUDIO_GetDeviceBuf(_THIS)
366 {
367  return (this->hidden->mixbuf);
368 }
369 
370 
371 static int
372 PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
373 {
374  struct SDL_PrivateAudioData *h = this->hidden;
375  const void *data = NULL;
376  size_t nbytes = 0;
377 
378  while (SDL_AtomicGet(&this->enabled)) {
379  if (h->capturebuf != NULL) {
380  const int cpy = SDL_min(buflen, h->capturelen);
381  SDL_memcpy(buffer, h->capturebuf, cpy);
382  /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
383  h->capturebuf += cpy;
384  h->capturelen -= cpy;
385  if (h->capturelen == 0) {
386  h->capturebuf = NULL;
387  PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */
388  }
389  return cpy; /* new data, return it. */
390  }
391 
392  if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
393  PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
394  PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
396  return -1; /* uhoh, pulse failed! */
397  }
398 
399  if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
400  continue; /* no data available yet. */
401  }
402 
403  /* a new fragment is available! */
404  PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
405  SDL_assert(nbytes > 0);
406  if (data == NULL) { /* NULL==buffer had a hole. Ignore that. */
407  PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
408  } else {
409  /* store this fragment's data, start feeding it to SDL. */
410  /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
411  h->capturebuf = (const Uint8 *) data;
412  h->capturelen = nbytes;
413  }
414  }
415 
416  return -1; /* not enabled? */
417 }
418 
419 static void
420 PULSEAUDIO_FlushCapture(_THIS)
421 {
422  struct SDL_PrivateAudioData *h = this->hidden;
423  const void *data = NULL;
424  size_t nbytes = 0;
425 
426  if (h->capturebuf != NULL) {
427  PULSEAUDIO_pa_stream_drop(h->stream);
428  h->capturebuf = NULL;
429  h->capturelen = 0;
430  }
431 
432  while (SDL_TRUE) {
433  if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
434  PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
435  PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
437  return; /* uhoh, pulse failed! */
438  }
439 
440  if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
441  break; /* no data available, so we're done. */
442  }
443 
444  /* a new fragment is available! Just dump it. */
445  PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
446  PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
447  }
448 }
449 
450 static void
451 PULSEAUDIO_CloseDevice(_THIS)
452 {
453  if (this->hidden->stream) {
454  if (this->hidden->capturebuf != NULL) {
455  PULSEAUDIO_pa_stream_drop(this->hidden->stream);
456  }
457  PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
458  PULSEAUDIO_pa_stream_unref(this->hidden->stream);
459  }
460 
461  DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
462  SDL_free(this->hidden->mixbuf);
463  SDL_free(this->hidden->device_name);
464  SDL_free(this->hidden);
465 }
466 
467 static void
468 SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
469 {
470  if (i) {
471  char **devname = (char **) data;
472  *devname = SDL_strdup(i->name);
473  }
474 }
475 
476 static void
477 SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
478 {
479  if (i) {
480  char **devname = (char **) data;
481  *devname = SDL_strdup(i->name);
482  }
483 }
484 
485 static SDL_bool
486 FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
487 {
488  const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
489 
490  if (handle == NULL) { /* NULL == default device. */
491  return SDL_TRUE;
492  }
493 
494  if (iscapture) {
495  WaitForPulseOperation(h->mainloop,
496  PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
497  SourceDeviceNameCallback, &h->device_name));
498  } else {
499  WaitForPulseOperation(h->mainloop,
500  PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
501  SinkDeviceNameCallback, &h->device_name));
502  }
503 
504  return (h->device_name != NULL);
505 }
506 
507 static int
508 PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
509 {
510  struct SDL_PrivateAudioData *h = NULL;
511  Uint16 test_format = 0;
512  pa_sample_spec paspec;
513  pa_buffer_attr paattr;
514  pa_channel_map pacmap;
515  pa_stream_flags_t flags = 0;
516  int state = 0;
517  int rc = 0;
518 
519  /* Initialize all variables that we clean on shutdown */
520  h = this->hidden = (struct SDL_PrivateAudioData *)
521  SDL_malloc((sizeof *this->hidden));
522  if (this->hidden == NULL) {
523  return SDL_OutOfMemory();
524  }
525  SDL_zerop(this->hidden);
526 
527  paspec.format = PA_SAMPLE_INVALID;
528 
529  /* Try for a closest match on audio format */
530  for (test_format = SDL_FirstAudioFormat(this->spec.format);
531  (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
532 #ifdef DEBUG_AUDIO
533  fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
534 #endif
535  switch (test_format) {
536  case AUDIO_U8:
537  paspec.format = PA_SAMPLE_U8;
538  break;
539  case AUDIO_S16LSB:
540  paspec.format = PA_SAMPLE_S16LE;
541  break;
542  case AUDIO_S16MSB:
543  paspec.format = PA_SAMPLE_S16BE;
544  break;
545  case AUDIO_S32LSB:
546  paspec.format = PA_SAMPLE_S32LE;
547  break;
548  case AUDIO_S32MSB:
549  paspec.format = PA_SAMPLE_S32BE;
550  break;
551  case AUDIO_F32LSB:
552  paspec.format = PA_SAMPLE_FLOAT32LE;
553  break;
554  case AUDIO_F32MSB:
555  paspec.format = PA_SAMPLE_FLOAT32BE;
556  break;
557  default:
558  paspec.format = PA_SAMPLE_INVALID;
559  break;
560  }
561  if (paspec.format == PA_SAMPLE_INVALID) {
562  test_format = SDL_NextAudioFormat();
563  }
564  }
565  if (paspec.format == PA_SAMPLE_INVALID) {
566  return SDL_SetError("Couldn't find any hardware audio formats");
567  }
568  this->spec.format = test_format;
569 
570  /* Calculate the final parameters for this audio specification */
571 #ifdef PA_STREAM_ADJUST_LATENCY
572  this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
573 #endif
575 
576  /* Allocate mixing buffer */
577  if (!iscapture) {
578  h->mixlen = this->spec.size;
579  h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
580  if (h->mixbuf == NULL) {
581  return SDL_OutOfMemory();
582  }
583  SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
584  }
585 
586  paspec.channels = this->spec.channels;
587  paspec.rate = this->spec.freq;
588 
589  /* Reduced prebuffering compared to the defaults. */
590 #ifdef PA_STREAM_ADJUST_LATENCY
591  /* 2x original requested bufsize */
592  paattr.tlength = h->mixlen * 4;
593  paattr.prebuf = -1;
594  paattr.maxlength = -1;
595  /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
596  paattr.minreq = h->mixlen;
597  flags = PA_STREAM_ADJUST_LATENCY;
598 #else
599  paattr.tlength = h->mixlen*2;
600  paattr.prebuf = h->mixlen*2;
601  paattr.maxlength = h->mixlen*2;
602  paattr.minreq = h->mixlen;
603 #endif
604 
605  if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
606  return SDL_SetError("Could not connect to PulseAudio server");
607  }
608 
609  if (!FindDeviceName(h, iscapture, handle)) {
610  return SDL_SetError("Requested PulseAudio sink/source missing?");
611  }
612 
613  /* The SDL ALSA output hints us that we use Windows' channel mapping */
614  /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
615  PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
616  PA_CHANNEL_MAP_WAVEEX);
617 
618  h->stream = PULSEAUDIO_pa_stream_new(
619  h->context,
620  "Simple DirectMedia Layer", /* stream description */
621  &paspec, /* sample format spec */
622  &pacmap /* channel map */
623  );
624 
625  if (h->stream == NULL) {
626  return SDL_SetError("Could not set up PulseAudio stream");
627  }
628 
629  /* now that we have multi-device support, don't move a stream from
630  a device that was unplugged to something else, unless we're default. */
631  if (h->device_name != NULL) {
632  flags |= PA_STREAM_DONT_MOVE;
633  }
634 
635  if (iscapture) {
636  rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
637  } else {
638  rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
639  }
640 
641  if (rc < 0) {
642  return SDL_SetError("Could not connect PulseAudio stream");
643  }
644 
645  do {
646  if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
647  return SDL_SetError("pa_mainloop_iterate() failed");
648  }
649  state = PULSEAUDIO_pa_stream_get_state(h->stream);
650  if (!PA_STREAM_IS_GOOD(state)) {
651  return SDL_SetError("Could not connect PulseAudio stream");
652  }
653  } while (state != PA_STREAM_READY);
654 
655  /* We're ready to rock and roll. :-) */
656  return 0;
657 }
658 
659 static pa_mainloop *hotplug_mainloop = NULL;
660 static pa_context *hotplug_context = NULL;
661 static SDL_Thread *hotplug_thread = NULL;
662 
663 /* device handles are device index + 1, cast to void*, so we never pass a NULL. */
664 
665 /* This is called when PulseAudio adds an output ("sink") device. */
666 static void
667 SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
668 {
669  if (i) {
670  SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
671  }
672 }
673 
674 /* This is called when PulseAudio adds a capture ("source") device. */
675 static void
676 SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
677 {
678  if (i) {
679  /* Skip "monitor" sources. These are just output from other sinks. */
680  if (i->monitor_of_sink == PA_INVALID_INDEX) {
681  SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
682  }
683  }
684 }
685 
686 /* This is called when PulseAudio has a device connected/removed/changed. */
687 static void
688 HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
689 {
690  const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
691  const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
692 
693  if (added || removed) { /* we only care about add/remove events. */
694  const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
695  const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
696 
697  /* adds need sink details from the PulseAudio server. Another callback... */
698  if (added && sink) {
699  PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
700  } else if (added && source) {
701  PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
702  } else if (removed && (sink || source)) {
703  /* removes we can handle just with the device index. */
704  SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
705  }
706  }
707 }
708 
709 /* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
710 static int SDLCALL
711 HotplugThread(void *data)
712 {
713  pa_operation *o;
715  PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
716  o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
717  PULSEAUDIO_pa_operation_unref(o); /* don't wait for it, just do our thing. */
718  PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
719  return 0;
720 }
721 
722 static void
723 PULSEAUDIO_DetectDevices()
724 {
725  WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
726  WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
727 
728  /* ok, we have a sane list, let's set up hotplug notifications now... */
729  hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
730 }
731 
732 static void
733 PULSEAUDIO_Deinitialize(void)
734 {
735  if (hotplug_thread) {
736  PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
737  SDL_WaitThread(hotplug_thread, NULL);
738  hotplug_thread = NULL;
739  }
740 
741  DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
742  hotplug_mainloop = NULL;
743  hotplug_context = NULL;
744 
745  UnloadPulseAudioLibrary();
746 }
747 
748 static int
749 PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
750 {
751  if (LoadPulseAudioLibrary() < 0) {
752  return 0;
753  }
754 
755  if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
756  UnloadPulseAudioLibrary();
757  return 0;
758  }
759 
760  /* Set the function pointers */
761  impl->DetectDevices = PULSEAUDIO_DetectDevices;
762  impl->OpenDevice = PULSEAUDIO_OpenDevice;
763  impl->PlayDevice = PULSEAUDIO_PlayDevice;
764  impl->WaitDevice = PULSEAUDIO_WaitDevice;
765  impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
766  impl->CloseDevice = PULSEAUDIO_CloseDevice;
767  impl->Deinitialize = PULSEAUDIO_Deinitialize;
768  impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
769  impl->FlushCapture = PULSEAUDIO_FlushCapture;
770 
771  impl->HasCaptureSupport = SDL_TRUE;
772 
773  return 1; /* this audio target is available. */
774 }
775 
777  "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
778 };
779 
780 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
781 
782 /* vi: set ts=4 sw=4 expandtab: */
arts_stream_t stream
Definition: SDL_artsaudio.h:36
GLsizei GLenum GLboolean sink
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1584
#define AUDIO_S32MSB
Definition: SDL_audio.h:104
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
LPDIRECTSOUNDCAPTUREBUFFER capturebuf
GLfloat GLfloat GLfloat GLfloat h
struct xkb_state * state
static screen_context_t context
Definition: video.c:25
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
Uint16 samples
Definition: SDL_audio.h:183
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:449
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define SDL_LoadObject
#define SDL_UnloadObject
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1596
pa_context * context
#define AUDIO_F32MSB
Definition: SDL_audio.h:113
SDL_AudioSpec spec
Definition: loopwave.c:31
unsigned int size_t
SDL_bool retval
#define AUDIO_U8
Definition: SDL_audio.h:89
#define SDL_memcpy
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:427
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
Definition: SDL_audio.c:490
Uint8 channels
Definition: SDL_audio.h:181
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
GLenum const void * addr
#define AUDIO_F32LSB
Definition: SDL_audio.h:112
const GLubyte * c
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:83
GLsizei GLsizei GLchar * source
#define AUDIO_S32LSB
Definition: SDL_audio.h:103
#define SDL_sscanf
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
GLenum GLenum GLsizei const GLuint GLboolean enabled
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
Uint32 size
Definition: SDL_audio.h:185
#define SDL_assert(condition)
Definition: SDL_assert.h:169
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:139
GLuint buffer
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:76
unsigned int uint32_t
#define SDL_SetError
GLbitfield flags
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
#define SDL_strdup
SDL_AudioFormat format
Definition: SDL_audio.h:180
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:77
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_AtomicGet
uint16_t Uint16
Definition: SDL_stdinc.h:169
#define SDL_INLINE
Definition: begin_code.h:131
#define SDL_malloc
AudioBootStrap PULSEAUDIO_bootstrap
void * SDL_LoadFunction(void *handle, const char *name)
pa_mainloop * mainloop
#define SDLCALL
Definition: SDL_internal.h:45
#define SDL_SetThreadPriority
GLdouble GLdouble t
Definition: SDL_opengl.h:2071
#define SDL_memset
signed long long int64_t
#define SDL_WaitThread
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
Definition: SDL_audio.c:432