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