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