SDL  2.0
SDL_pulseaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2017 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 stream_operation_complete_no_op(pa_stream *s, int success, void *userdata)
254 {
255  /* no-op for pa_stream_drain(), etc, to use for callback. */
256 }
257 
258 static void
259 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
260 {
261  /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
262  if (mainloop && o) {
263  SDL_bool okay = SDL_TRUE;
264  while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
265  okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) >= 0);
266  }
267  PULSEAUDIO_pa_operation_unref(o);
268  }
269 }
270 
271 static void
272 DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *context)
273 {
274  if (context) {
275  PULSEAUDIO_pa_context_disconnect(context);
276  PULSEAUDIO_pa_context_unref(context);
277  }
278  if (mainloop != NULL) {
279  PULSEAUDIO_pa_mainloop_free(mainloop);
280  }
281 }
282 
283 static int
284 ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
285 {
286  pa_mainloop *mainloop = NULL;
287  pa_context *context = NULL;
288  pa_mainloop_api *mainloop_api = NULL;
289  int state = 0;
290 
291  *_mainloop = NULL;
292  *_context = NULL;
293 
294  /* Set up a new main loop */
295  if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
296  return SDL_SetError("pa_mainloop_new() failed");
297  }
298 
299  *_mainloop = mainloop;
300 
301  mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
302  SDL_assert(mainloop_api); /* this never fails, right? */
303 
304  context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
305  if (!context) {
306  return SDL_SetError("pa_context_new() failed");
307  }
308  *_context = context;
309 
310  /* Connect to the PulseAudio server */
311  if (PULSEAUDIO_pa_context_connect(context, NULL, 0, NULL) < 0) {
312  return SDL_SetError("Could not setup connection to PulseAudio");
313  }
314 
315  do {
316  if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1, NULL) < 0) {
317  return SDL_SetError("pa_mainloop_iterate() failed");
318  }
319  state = PULSEAUDIO_pa_context_get_state(context);
320  if (!PA_CONTEXT_IS_GOOD(state)) {
321  return SDL_SetError("Could not connect to PulseAudio");
322  }
323  } while (state != PA_CONTEXT_READY);
324 
325  return 0; /* connected and ready! */
326 }
327 
328 static int
329 ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
330 {
331  const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
332  if (retval < 0) {
333  DisconnectFromPulseServer(*_mainloop, *_context);
334  }
335  return retval;
336 }
337 
338 
339 /* This function waits until it is possible to write a full sound buffer */
340 static void
341 PULSEAUDIO_WaitDevice(_THIS)
342 {
343  struct SDL_PrivateAudioData *h = this->hidden;
344 
345  while (SDL_AtomicGet(&this->enabled)) {
346  if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
347  PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
348  PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
350  return;
351  }
352  if (PULSEAUDIO_pa_stream_writable_size(h->stream) >= h->mixlen) {
353  return;
354  }
355  }
356 }
357 
358 static void
359 PULSEAUDIO_PlayDevice(_THIS)
360 {
361  /* Write the audio data */
362  struct SDL_PrivateAudioData *h = this->hidden;
363  if (SDL_AtomicGet(&this->enabled)) {
364  if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf, h->mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
366  }
367  }
368 }
369 
370 static Uint8 *
371 PULSEAUDIO_GetDeviceBuf(_THIS)
372 {
373  return (this->hidden->mixbuf);
374 }
375 
376 
377 static int
378 PULSEAUDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
379 {
380  struct SDL_PrivateAudioData *h = this->hidden;
381  const void *data = NULL;
382  size_t nbytes = 0;
383 
384  while (SDL_AtomicGet(&this->enabled)) {
385  if (h->capturebuf != NULL) {
386  const int cpy = SDL_min(buflen, h->capturelen);
387  SDL_memcpy(buffer, h->capturebuf, cpy);
388  /*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
389  h->capturebuf += cpy;
390  h->capturelen -= cpy;
391  if (h->capturelen == 0) {
392  h->capturebuf = NULL;
393  PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */
394  }
395  return cpy; /* new data, return it. */
396  }
397 
398  if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
399  PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
400  PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
402  return -1; /* uhoh, pulse failed! */
403  }
404 
405  if (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0) {
406  continue; /* no data available yet. */
407  }
408 
409  /* a new fragment is available! */
410  PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
411  SDL_assert(nbytes > 0);
412  if (data == NULL) { /* NULL==buffer had a hole. Ignore that. */
413  PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
414  } else {
415  /* store this fragment's data, start feeding it to SDL. */
416  /*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
417  h->capturebuf = (const Uint8 *) data;
418  h->capturelen = nbytes;
419  }
420  }
421 
422  return -1; /* not enabled? */
423 }
424 
425 static void
426 PULSEAUDIO_FlushCapture(_THIS)
427 {
428  struct SDL_PrivateAudioData *h = this->hidden;
429 
430  if (h->capturebuf != NULL) {
431  PULSEAUDIO_pa_stream_drop(h->stream);
432  h->capturebuf = NULL;
433  h->capturelen = 0;
434  }
435 
436  WaitForPulseOperation(h->mainloop, PULSEAUDIO_pa_stream_flush(h->stream, stream_operation_complete_no_op, NULL));
437 }
438 
439 static void
440 PULSEAUDIO_CloseDevice(_THIS)
441 {
442  if (this->hidden->stream) {
443  if (this->hidden->capturebuf != NULL) {
444  PULSEAUDIO_pa_stream_drop(this->hidden->stream);
445  }
446  PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
447  PULSEAUDIO_pa_stream_unref(this->hidden->stream);
448  }
449 
450  DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
451  SDL_free(this->hidden->mixbuf);
452  SDL_free(this->hidden->device_name);
453  SDL_free(this->hidden);
454 }
455 
456 static void
457 SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
458 {
459  if (i) {
460  char **devname = (char **) data;
461  *devname = SDL_strdup(i->name);
462  }
463 }
464 
465 static void
466 SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
467 {
468  if (i) {
469  char **devname = (char **) data;
470  *devname = SDL_strdup(i->name);
471  }
472 }
473 
474 static SDL_bool
475 FindDeviceName(struct SDL_PrivateAudioData *h, const int iscapture, void *handle)
476 {
477  const uint32_t idx = ((uint32_t) ((size_t) handle)) - 1;
478 
479  if (handle == NULL) { /* NULL == default device. */
480  return SDL_TRUE;
481  }
482 
483  if (iscapture) {
484  WaitForPulseOperation(h->mainloop,
485  PULSEAUDIO_pa_context_get_source_info_by_index(h->context, idx,
486  SourceDeviceNameCallback, &h->device_name));
487  } else {
488  WaitForPulseOperation(h->mainloop,
489  PULSEAUDIO_pa_context_get_sink_info_by_index(h->context, idx,
490  SinkDeviceNameCallback, &h->device_name));
491  }
492 
493  return (h->device_name != NULL);
494 }
495 
496 static int
497 PULSEAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
498 {
499  struct SDL_PrivateAudioData *h = NULL;
500  Uint16 test_format = 0;
501  pa_sample_spec paspec;
502  pa_buffer_attr paattr;
503  pa_channel_map pacmap;
504  pa_stream_flags_t flags = 0;
505  int state = 0;
506  int rc = 0;
507 
508  /* Initialize all variables that we clean on shutdown */
509  h = this->hidden = (struct SDL_PrivateAudioData *)
510  SDL_malloc((sizeof *this->hidden));
511  if (this->hidden == NULL) {
512  return SDL_OutOfMemory();
513  }
514  SDL_zerop(this->hidden);
515 
516  paspec.format = PA_SAMPLE_INVALID;
517 
518  /* Try for a closest match on audio format */
519  for (test_format = SDL_FirstAudioFormat(this->spec.format);
520  (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
521 #ifdef DEBUG_AUDIO
522  fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
523 #endif
524  switch (test_format) {
525  case AUDIO_U8:
526  paspec.format = PA_SAMPLE_U8;
527  break;
528  case AUDIO_S16LSB:
529  paspec.format = PA_SAMPLE_S16LE;
530  break;
531  case AUDIO_S16MSB:
532  paspec.format = PA_SAMPLE_S16BE;
533  break;
534  case AUDIO_S32LSB:
535  paspec.format = PA_SAMPLE_S32LE;
536  break;
537  case AUDIO_S32MSB:
538  paspec.format = PA_SAMPLE_S32BE;
539  break;
540  case AUDIO_F32LSB:
541  paspec.format = PA_SAMPLE_FLOAT32LE;
542  break;
543  case AUDIO_F32MSB:
544  paspec.format = PA_SAMPLE_FLOAT32BE;
545  break;
546  default:
547  paspec.format = PA_SAMPLE_INVALID;
548  break;
549  }
550  if (paspec.format == PA_SAMPLE_INVALID) {
551  test_format = SDL_NextAudioFormat();
552  }
553  }
554  if (paspec.format == PA_SAMPLE_INVALID) {
555  return SDL_SetError("Couldn't find any hardware audio formats");
556  }
557  this->spec.format = test_format;
558 
559  /* Calculate the final parameters for this audio specification */
560 #ifdef PA_STREAM_ADJUST_LATENCY
561  this->spec.samples /= 2; /* Mix in smaller chunck to avoid underruns */
562 #endif
564 
565  /* Allocate mixing buffer */
566  if (!iscapture) {
567  h->mixlen = this->spec.size;
568  h->mixbuf = (Uint8 *) SDL_malloc(h->mixlen);
569  if (h->mixbuf == NULL) {
570  return SDL_OutOfMemory();
571  }
572  SDL_memset(h->mixbuf, this->spec.silence, this->spec.size);
573  }
574 
575  paspec.channels = this->spec.channels;
576  paspec.rate = this->spec.freq;
577 
578  /* Reduced prebuffering compared to the defaults. */
579 #ifdef PA_STREAM_ADJUST_LATENCY
580  /* 2x original requested bufsize */
581  paattr.tlength = h->mixlen * 4;
582  paattr.prebuf = -1;
583  paattr.maxlength = -1;
584  /* -1 can lead to pa_stream_writable_size() >= mixlen never being true */
585  paattr.minreq = h->mixlen;
586  flags = PA_STREAM_ADJUST_LATENCY;
587 #else
588  paattr.tlength = h->mixlen*2;
589  paattr.prebuf = h->mixlen*2;
590  paattr.maxlength = h->mixlen*2;
591  paattr.minreq = h->mixlen;
592 #endif
593 
594  if (ConnectToPulseServer(&h->mainloop, &h->context) < 0) {
595  return SDL_SetError("Could not connect to PulseAudio server");
596  }
597 
598  if (!FindDeviceName(h, iscapture, handle)) {
599  return SDL_SetError("Requested PulseAudio sink/source missing?");
600  }
601 
602  /* The SDL ALSA output hints us that we use Windows' channel mapping */
603  /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
604  PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->spec.channels,
605  PA_CHANNEL_MAP_WAVEEX);
606 
607  h->stream = PULSEAUDIO_pa_stream_new(
608  h->context,
609  "Simple DirectMedia Layer", /* stream description */
610  &paspec, /* sample format spec */
611  &pacmap /* channel map */
612  );
613 
614  if (h->stream == NULL) {
615  return SDL_SetError("Could not set up PulseAudio stream");
616  }
617 
618  /* now that we have multi-device support, don't move a stream from
619  a device that was unplugged to something else, unless we're default. */
620  if (h->device_name != NULL) {
621  flags |= PA_STREAM_DONT_MOVE;
622  }
623 
624  if (iscapture) {
625  rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
626  } else {
627  rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
628  }
629 
630  if (rc < 0) {
631  return SDL_SetError("Could not connect PulseAudio stream");
632  }
633 
634  do {
635  if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
636  return SDL_SetError("pa_mainloop_iterate() failed");
637  }
638  state = PULSEAUDIO_pa_stream_get_state(h->stream);
639  if (!PA_STREAM_IS_GOOD(state)) {
640  return SDL_SetError("Could not connect PulseAudio stream");
641  }
642  } while (state != PA_STREAM_READY);
643 
644  /* We're ready to rock and roll. :-) */
645  return 0;
646 }
647 
648 static pa_mainloop *hotplug_mainloop = NULL;
649 static pa_context *hotplug_context = NULL;
650 static SDL_Thread *hotplug_thread = NULL;
651 
652 /* device handles are device index + 1, cast to void*, so we never pass a NULL. */
653 
654 /* This is called when PulseAudio adds an output ("sink") device. */
655 static void
656 SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
657 {
658  if (i) {
659  SDL_AddAudioDevice(SDL_FALSE, i->description, (void *) ((size_t) i->index+1));
660  }
661 }
662 
663 /* This is called when PulseAudio adds a capture ("source") device. */
664 static void
665 SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
666 {
667  if (i) {
668  /* Skip "monitor" sources. These are just output from other sinks. */
669  if (i->monitor_of_sink == PA_INVALID_INDEX) {
670  SDL_AddAudioDevice(SDL_TRUE, i->description, (void *) ((size_t) i->index+1));
671  }
672  }
673 }
674 
675 /* This is called when PulseAudio has a device connected/removed/changed. */
676 static void
677 HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
678 {
679  const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
680  const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
681 
682  if (added || removed) { /* we only care about add/remove events. */
683  const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
684  const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
685 
686  /* adds need sink details from the PulseAudio server. Another callback... */
687  if (added && sink) {
688  PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback, NULL);
689  } else if (added && source) {
690  PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback, NULL);
691  } else if (removed && (sink || source)) {
692  /* removes we can handle just with the device index. */
693  SDL_RemoveAudioDevice(source != 0, (void *) ((size_t) idx+1));
694  }
695  }
696 }
697 
698 /* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
699 static int SDLCALL
700 HotplugThread(void *data)
701 {
702  pa_operation *o;
704  PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback, NULL);
705  o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
706  PULSEAUDIO_pa_operation_unref(o); /* don't wait for it, just do our thing. */
707  PULSEAUDIO_pa_mainloop_run(hotplug_mainloop, NULL);
708  return 0;
709 }
710 
711 static void
712 PULSEAUDIO_DetectDevices()
713 {
714  WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback, NULL));
715  WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback, NULL));
716 
717  /* ok, we have a sane list, let's set up hotplug notifications now... */
718  hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, NULL);
719 }
720 
721 static void
722 PULSEAUDIO_Deinitialize(void)
723 {
724  if (hotplug_thread) {
725  PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
726  SDL_WaitThread(hotplug_thread, NULL);
727  hotplug_thread = NULL;
728  }
729 
730  DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
731  hotplug_mainloop = NULL;
732  hotplug_context = NULL;
733 
734  UnloadPulseAudioLibrary();
735 }
736 
737 static int
738 PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
739 {
740  if (LoadPulseAudioLibrary() < 0) {
741  return 0;
742  }
743 
744  if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
745  UnloadPulseAudioLibrary();
746  return 0;
747  }
748 
749  /* Set the function pointers */
750  impl->DetectDevices = PULSEAUDIO_DetectDevices;
751  impl->OpenDevice = PULSEAUDIO_OpenDevice;
752  impl->PlayDevice = PULSEAUDIO_PlayDevice;
753  impl->WaitDevice = PULSEAUDIO_WaitDevice;
754  impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
755  impl->CloseDevice = PULSEAUDIO_CloseDevice;
756  impl->Deinitialize = PULSEAUDIO_Deinitialize;
757  impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
758  impl->FlushCapture = PULSEAUDIO_FlushCapture;
759 
760  impl->HasCaptureSupport = SDL_TRUE;
761 
762  return 1; /* this audio target is available. */
763 }
764 
766  "pulseaudio", "PulseAudio", PULSEAUDIO_Init, 0
767 };
768 
769 #endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
770 
771 /* 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:375
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1577
#define AUDIO_S32MSB
Definition: SDL_audio.h:104
GLdouble s
Definition: SDL_opengl.h:2063
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:72
Uint16 samples
Definition: SDL_audio.h:174
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:71
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:446
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:386
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1589
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:487
Uint8 channels
Definition: SDL_audio.h:172
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
void SDL_free(void *mem)
GLenum const void * addr
#define AUDIO_F32LSB
Definition: SDL_audio.h:112
const GLubyte * c
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
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:1598
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:176
#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:75
unsigned int uint32_t
#define SDL_SetError
GLbitfield flags
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
#define SDL_strdup
SDL_AudioFormat format
Definition: SDL_audio.h:171
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
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:74
#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:429