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