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