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