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