28#include "../../SDL_internal.h"
31#if SDL_AUDIO_DRIVER_PULSEAUDIO
40#include <pulse/pulseaudio.h>
44#include "../SDL_audio_c.h"
47#include "../../thread/SDL_systhread.h"
49#if (PA_API_VERSION < 12)
51static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t
x) {
53 x == PA_CONTEXT_CONNECTING ||
54 x == PA_CONTEXT_AUTHORIZING ||
55 x == PA_CONTEXT_SETTING_NAME ||
56 x == PA_CONTEXT_READY;
59static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t
x) {
61 x == PA_STREAM_CREATING ||
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 *);
78static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
80static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
81static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
83static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
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 *);
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 *);
117static int load_pulseaudio_syms(
void);
120#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
122static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
123static void *pulseaudio_handle =
NULL;
126load_pulseaudio_sym(
const char *fn,
void **
addr)
138#define SDL_PULSEAUDIO_SYM(x) \
139 if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1
142UnloadPulseAudioLibrary(
void)
144 if (pulseaudio_handle !=
NULL) {
146 pulseaudio_handle =
NULL;
151LoadPulseAudioLibrary(
void)
154 if (pulseaudio_handle ==
NULL) {
156 if (pulseaudio_handle ==
NULL) {
160 retval = load_pulseaudio_syms();
162 UnloadPulseAudioLibrary();
171#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
174UnloadPulseAudioLibrary(
void)
179LoadPulseAudioLibrary(
void)
181 load_pulseaudio_syms();
189load_pulseaudio_syms(
void)
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);
231squashVersion(
const int major,
const int minor,
const int patch)
233 return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
240 const char *verstr = PULSEAUDIO_pa_get_library_version();
241 if (verstr !=
NULL) {
243 if (
SDL_sscanf(verstr,
"%d.%d.%d", &maj, &min, &patch) == 3) {
244 if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
249 return "SDL Application";
253WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
258 while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
259 okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1,
NULL) >= 0);
261 PULSEAUDIO_pa_operation_unref(o);
266DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *
context)
269 PULSEAUDIO_pa_context_disconnect(
context);
270 PULSEAUDIO_pa_context_unref(
context);
272 if (mainloop !=
NULL) {
273 PULSEAUDIO_pa_mainloop_free(mainloop);
278ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
280 pa_mainloop *mainloop =
NULL;
282 pa_mainloop_api *mainloop_api =
NULL;
289 if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
293 *_mainloop = mainloop;
295 mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
298 context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
306 return SDL_SetError(
"Could not setup connection to PulseAudio");
310 if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1,
NULL) < 0) {
314 if (!PA_CONTEXT_IS_GOOD(
state)) {
317 }
while (
state != PA_CONTEXT_READY);
323ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
325 const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
327 DisconnectFromPulseServer(*_mainloop, *_context);
335PULSEAUDIO_WaitDevice(
_THIS)
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) {
346 if (PULSEAUDIO_pa_stream_writable_size(
h->stream) >=
h->mixlen) {
353PULSEAUDIO_PlayDevice(
_THIS)
358 if (PULSEAUDIO_pa_stream_write(
h->stream,
h->mixbuf,
h->mixlen,
NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
365PULSEAUDIO_GetDeviceBuf(
_THIS)
367 return (this->hidden->mixbuf);
372PULSEAUDIO_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
379 if (
h->capturebuf !=
NULL) {
380 const int cpy =
SDL_min(buflen,
h->capturelen);
383 h->capturebuf += cpy;
384 h->capturelen -= cpy;
385 if (
h->capturelen == 0) {
386 h->capturebuf =
NULL;
387 PULSEAUDIO_pa_stream_drop(
h->stream);
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) {
399 if (PULSEAUDIO_pa_stream_readable_size(
h->stream) == 0) {
404 PULSEAUDIO_pa_stream_peek(
h->stream, &
data, &nbytes);
407 PULSEAUDIO_pa_stream_drop(
h->stream);
412 h->capturelen = nbytes;
420PULSEAUDIO_FlushCapture(
_THIS)
426 if (
h->capturebuf !=
NULL) {
427 PULSEAUDIO_pa_stream_drop(
h->stream);
428 h->capturebuf =
NULL;
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) {
440 if (PULSEAUDIO_pa_stream_readable_size(
h->stream) == 0) {
445 PULSEAUDIO_pa_stream_peek(
h->stream, &
data, &nbytes);
446 PULSEAUDIO_pa_stream_drop(
h->stream);
451PULSEAUDIO_CloseDevice(
_THIS)
453 if (this->hidden->stream) {
454 if (this->hidden->capturebuf !=
NULL) {
455 PULSEAUDIO_pa_stream_drop(this->hidden->stream);
457 PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
458 PULSEAUDIO_pa_stream_unref(this->hidden->stream);
461 DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
463 SDL_free(this->hidden->device_name);
468SinkDeviceNameCallback(pa_context *
c,
const pa_sink_info *
i,
int is_last,
void *
data)
471 char **devname = (
char **)
data;
477SourceDeviceNameCallback(pa_context *
c,
const pa_source_info *
i,
int is_last,
void *
data)
480 char **devname = (
char **)
data;
495 WaitForPulseOperation(
h->mainloop,
496 PULSEAUDIO_pa_context_get_source_info_by_index(
h->context, idx,
497 SourceDeviceNameCallback, &
h->device_name));
499 WaitForPulseOperation(
h->mainloop,
500 PULSEAUDIO_pa_context_get_sink_info_by_index(
h->context, idx,
501 SinkDeviceNameCallback, &
h->device_name));
504 return (
h->device_name !=
NULL);
512 pa_sample_spec paspec;
513 pa_buffer_attr paattr;
514 pa_channel_map pacmap;
515 pa_stream_flags_t
flags = 0;
522 if (this->hidden ==
NULL) {
527 paspec.format = PA_SAMPLE_INVALID;
531 (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
533 fprintf(stderr,
"Trying format 0x%4.4x\n", test_format);
535 switch (test_format) {
537 paspec.format = PA_SAMPLE_U8;
540 paspec.format = PA_SAMPLE_S16LE;
543 paspec.format = PA_SAMPLE_S16BE;
546 paspec.format = PA_SAMPLE_S32LE;
549 paspec.format = PA_SAMPLE_S32BE;
552 paspec.format = PA_SAMPLE_FLOAT32LE;
555 paspec.format = PA_SAMPLE_FLOAT32BE;
558 paspec.format = PA_SAMPLE_INVALID;
561 if (paspec.format == PA_SAMPLE_INVALID) {
565 if (paspec.format == PA_SAMPLE_INVALID) {
566 return SDL_SetError(
"Couldn't find any hardware audio formats");
571#ifdef PA_STREAM_ADJUST_LATENCY
580 if (
h->mixbuf ==
NULL) {
583 SDL_memset(
h->mixbuf, this->spec.silence, this->spec.size);
590#ifdef PA_STREAM_ADJUST_LATENCY
592 paattr.tlength =
h->mixlen * 4;
594 paattr.maxlength = -1;
596 paattr.minreq =
h->mixlen;
597 flags = PA_STREAM_ADJUST_LATENCY;
599 paattr.tlength =
h->mixlen*2;
600 paattr.prebuf =
h->mixlen*2;
601 paattr.maxlength =
h->mixlen*2;
602 paattr.minreq =
h->mixlen;
605 if (ConnectToPulseServer(&
h->mainloop, &
h->context) < 0) {
606 return SDL_SetError(
"Could not connect to PulseAudio server");
610 return SDL_SetError(
"Requested PulseAudio sink/source missing?");
615 PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->
spec.
channels,
616 PA_CHANNEL_MAP_WAVEEX);
618 h->stream = PULSEAUDIO_pa_stream_new(
620 "Simple DirectMedia Layer",
625 if (
h->stream ==
NULL) {
626 return SDL_SetError(
"Could not set up PulseAudio stream");
631 if (
h->device_name !=
NULL) {
632 flags |= PA_STREAM_DONT_MOVE;
636 rc = PULSEAUDIO_pa_stream_connect_record(
h->stream,
h->device_name, &paattr,
flags);
638 rc = PULSEAUDIO_pa_stream_connect_playback(
h->stream,
h->device_name, &paattr,
flags,
NULL,
NULL);
642 return SDL_SetError(
"Could not connect PulseAudio stream");
646 if (PULSEAUDIO_pa_mainloop_iterate(
h->mainloop, 1,
NULL) < 0) {
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");
653 }
while (
state != PA_STREAM_READY);
659static pa_mainloop *hotplug_mainloop =
NULL;
660static pa_context *hotplug_context =
NULL;
667SinkInfoCallback(pa_context *
c,
const pa_sink_info *
i,
int is_last,
void *
data)
676SourceInfoCallback(pa_context *
c,
const pa_source_info *
i,
int is_last,
void *
data)
680 if (
i->monitor_of_sink == PA_INVALID_INDEX) {
688HotplugCallback(pa_context *
c, pa_subscription_event_type_t
t,
uint32_t idx,
void *
data)
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);
693 if (added || removed) {
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);
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);
711HotplugThread(
void *
data)
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);
718 PULSEAUDIO_pa_mainloop_run(hotplug_mainloop,
NULL);
723PULSEAUDIO_DetectDevices()
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));
733PULSEAUDIO_Deinitialize(
void)
735 if (hotplug_thread) {
736 PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
738 hotplug_thread =
NULL;
741 DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
742 hotplug_mainloop =
NULL;
743 hotplug_context =
NULL;
745 UnloadPulseAudioLibrary();
751 if (LoadPulseAudioLibrary() < 0) {
755 if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
756 UnloadPulseAudioLibrary();
777 "pulseaudio",
"PulseAudio", PULSEAUDIO_Init, 0
#define SDL_assert(condition)
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
SDL_AudioFormat SDL_NextAudioFormat(void)
#define SDL_SetThreadPriority
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()
void * SDL_LoadFunction(void *handle, const char *name)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
GLint GLint GLint GLint GLint x
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei GLsizei GLchar * source
GLsizei GLenum GLboolean sink
GLfloat GLfloat GLfloat GLfloat h
AudioBootStrap PULSEAUDIO_bootstrap
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
@ SDL_THREAD_PRIORITY_LOW
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)
EGLImageKHR EGLint EGLint * handle
void(* PlayDevice)(_THIS)
void(* WaitDevice)(_THIS)
void(* CloseDevice)(_THIS)
void(* FlushCapture)(_THIS)
void(* DetectDevices)(void)
void(* Deinitialize)(void)
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Uint8 *(* GetDeviceBuf)(_THIS)
static screen_context_t context