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