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