28 #include "../../SDL_internal.h" 31 #if SDL_AUDIO_DRIVER_PULSEAUDIO 39 #include <sys/types.h> 40 #include <pulse/pulseaudio.h> 44 #include "../SDL_audio_c.h" 47 #include "../../thread/SDL_systhread.h" 50 #if (PA_API_VERSION < 12) 52 static SDL_INLINE int PA_CONTEXT_IS_GOOD(pa_context_state_t
x) {
54 x == PA_CONTEXT_CONNECTING ||
55 x == PA_CONTEXT_AUTHORIZING ||
56 x == PA_CONTEXT_SETTING_NAME ||
57 x == PA_CONTEXT_READY;
60 static SDL_INLINE int PA_STREAM_IS_GOOD(pa_stream_state_t
x) {
62 x == PA_STREAM_CREATING ||
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 *);
79 static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state) (
81 static void (*PULSEAUDIO_pa_operation_cancel) (pa_operation *);
82 static void (*PULSEAUDIO_pa_operation_unref) (pa_operation *);
84 static pa_context * (*PULSEAUDIO_pa_context_new) (pa_mainloop_api *,
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 *);
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 *);
118 static int load_pulseaudio_syms(
void);
121 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC 123 static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
124 static void *pulseaudio_handle =
NULL;
127 load_pulseaudio_sym(
const char *fn,
void **
addr)
139 #define SDL_PULSEAUDIO_SYM(x) \ 140 if (!load_pulseaudio_sym(#x, (void **) (char *) &PULSEAUDIO_##x)) return -1 143 UnloadPulseAudioLibrary(
void)
145 if (pulseaudio_handle !=
NULL) {
147 pulseaudio_handle =
NULL;
152 LoadPulseAudioLibrary(
void)
155 if (pulseaudio_handle ==
NULL) {
157 if (pulseaudio_handle ==
NULL) {
161 retval = load_pulseaudio_syms();
163 UnloadPulseAudioLibrary();
172 #define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x 175 UnloadPulseAudioLibrary(
void)
180 LoadPulseAudioLibrary(
void)
182 load_pulseaudio_syms();
190 load_pulseaudio_syms(
void)
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);
232 squashVersion(
const int major,
const int minor,
const int patch)
234 return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
241 const char *verstr = PULSEAUDIO_pa_get_library_version();
242 if (verstr !=
NULL) {
244 if (
SDL_sscanf(verstr,
"%d.%d.%d", &maj, &min, &patch) == 3) {
245 if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
250 return "SDL Application";
254 WaitForPulseOperation(pa_mainloop *mainloop, pa_operation *o)
259 while (okay && (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING)) {
260 okay = (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1,
NULL) >= 0);
262 PULSEAUDIO_pa_operation_unref(o);
267 DisconnectFromPulseServer(pa_mainloop *mainloop, pa_context *
context)
270 PULSEAUDIO_pa_context_disconnect(context);
271 PULSEAUDIO_pa_context_unref(context);
273 if (mainloop !=
NULL) {
274 PULSEAUDIO_pa_mainloop_free(mainloop);
279 ConnectToPulseServer_Internal(pa_mainloop **_mainloop, pa_context **_context)
281 pa_mainloop *mainloop =
NULL;
282 pa_context *context =
NULL;
283 pa_mainloop_api *mainloop_api =
NULL;
290 if (!(mainloop = PULSEAUDIO_pa_mainloop_new())) {
294 *_mainloop = mainloop;
296 mainloop_api = PULSEAUDIO_pa_mainloop_get_api(mainloop);
299 context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
306 if (PULSEAUDIO_pa_context_connect(context,
NULL, 0,
NULL) < 0) {
307 return SDL_SetError(
"Could not setup connection to PulseAudio");
311 if (PULSEAUDIO_pa_mainloop_iterate(mainloop, 1,
NULL) < 0) {
314 state = PULSEAUDIO_pa_context_get_state(context);
315 if (!PA_CONTEXT_IS_GOOD(state)) {
318 }
while (state != PA_CONTEXT_READY);
324 ConnectToPulseServer(pa_mainloop **_mainloop, pa_context **_context)
326 const int retval = ConnectToPulseServer_Internal(_mainloop, _context);
328 DisconnectFromPulseServer(*_mainloop, *_context);
336 PULSEAUDIO_WaitDevice(
_THIS)
341 if (PULSEAUDIO_pa_context_get_state(h->
context) != PA_CONTEXT_READY ||
342 PULSEAUDIO_pa_stream_get_state(h->
stream) != PA_STREAM_READY ||
343 PULSEAUDIO_pa_mainloop_iterate(h->
mainloop, 1,
NULL) < 0) {
347 if (PULSEAUDIO_pa_stream_writable_size(h->
stream) >= h->
mixlen) {
354 PULSEAUDIO_PlayDevice(
_THIS)
368 PULSEAUDIO_GetDeviceBuf(
_THIS)
370 return (this->hidden->mixbuf);
375 PULSEAUDIO_CaptureFromDevice(
_THIS,
void *
buffer,
int buflen)
390 PULSEAUDIO_pa_stream_drop(h->
stream);
395 if (PULSEAUDIO_pa_context_get_state(h->
context) != PA_CONTEXT_READY ||
396 PULSEAUDIO_pa_stream_get_state(h->
stream) != PA_STREAM_READY ||
397 PULSEAUDIO_pa_mainloop_iterate(h->
mainloop, 1,
NULL) < 0) {
402 if (PULSEAUDIO_pa_stream_readable_size(h->
stream) == 0) {
407 PULSEAUDIO_pa_stream_peek(h->
stream, &data, &nbytes);
410 PULSEAUDIO_pa_stream_drop(h->
stream);
423 PULSEAUDIO_FlushCapture(
_THIS)
426 const void *data =
NULL;
430 PULSEAUDIO_pa_stream_drop(h->
stream);
436 if (PULSEAUDIO_pa_context_get_state(h->
context) != PA_CONTEXT_READY ||
437 PULSEAUDIO_pa_stream_get_state(h->
stream) != PA_STREAM_READY ||
438 PULSEAUDIO_pa_mainloop_iterate(h->
mainloop, 1,
NULL) < 0) {
443 if (PULSEAUDIO_pa_stream_readable_size(h->
stream) == 0) {
448 PULSEAUDIO_pa_stream_peek(h->
stream, &data, &nbytes);
449 PULSEAUDIO_pa_stream_drop(h->
stream);
454 PULSEAUDIO_CloseDevice(
_THIS)
456 if (this->hidden->stream) {
457 if (this->hidden->capturebuf !=
NULL) {
458 PULSEAUDIO_pa_stream_drop(this->hidden->stream);
460 PULSEAUDIO_pa_stream_disconnect(this->hidden->stream);
461 PULSEAUDIO_pa_stream_unref(this->hidden->stream);
464 DisconnectFromPulseServer(this->hidden->mainloop, this->hidden->context);
466 SDL_free(this->hidden->device_name);
471 SinkDeviceNameCallback(pa_context *
c,
const pa_sink_info *
i,
int is_last,
void *data)
474 char **devname = (
char **) data;
480 SourceDeviceNameCallback(pa_context *c,
const pa_source_info *i,
int is_last,
void *data)
483 char **devname = (
char **) data;
493 if (handle ==
NULL) {
499 PULSEAUDIO_pa_context_get_source_info_by_index(h->
context, idx,
503 PULSEAUDIO_pa_context_get_sink_info_by_index(h->
context, idx,
511 PULSEAUDIO_OpenDevice(
_THIS,
void *handle,
const char *devname,
int iscapture)
515 pa_sample_spec paspec;
516 pa_buffer_attr paattr;
517 pa_channel_map pacmap;
518 pa_stream_flags_t
flags = 0;
525 if (this->hidden ==
NULL) {
530 paspec.format = PA_SAMPLE_INVALID;
534 (paspec.format == PA_SAMPLE_INVALID) && test_format;) {
536 fprintf(stderr,
"Trying format 0x%4.4x\n", test_format);
538 switch (test_format) {
540 paspec.format = PA_SAMPLE_U8;
543 paspec.format = PA_SAMPLE_S16LE;
546 paspec.format = PA_SAMPLE_S16BE;
549 paspec.format = PA_SAMPLE_S32LE;
552 paspec.format = PA_SAMPLE_S32BE;
555 paspec.format = PA_SAMPLE_FLOAT32LE;
558 paspec.format = PA_SAMPLE_FLOAT32BE;
561 paspec.format = PA_SAMPLE_INVALID;
564 if (paspec.format == PA_SAMPLE_INVALID) {
568 if (paspec.format == PA_SAMPLE_INVALID) {
569 return SDL_SetError(
"Couldn't find any hardware audio formats");
574 #ifdef PA_STREAM_ADJUST_LATENCY 593 #ifdef PA_STREAM_ADJUST_LATENCY 595 paattr.tlength = h->
mixlen * 4;
597 paattr.maxlength = -1;
599 paattr.minreq = h->
mixlen;
600 flags = PA_STREAM_ADJUST_LATENCY;
602 paattr.tlength = h->
mixlen*2;
603 paattr.prebuf = h->
mixlen*2;
604 paattr.maxlength = h->
mixlen*2;
605 paattr.minreq = h->
mixlen;
609 return SDL_SetError(
"Could not connect to PulseAudio server");
612 if (!FindDeviceName(h, iscapture, handle)) {
613 return SDL_SetError(
"Requested PulseAudio sink/source missing?");
618 PULSEAUDIO_pa_channel_map_init_auto(&pacmap, this->
spec.
channels,
619 PA_CHANNEL_MAP_WAVEEX);
621 h->
stream = PULSEAUDIO_pa_stream_new(
623 "Simple DirectMedia Layer",
629 return SDL_SetError(
"Could not set up PulseAudio stream");
635 flags |= PA_STREAM_DONT_MOVE;
639 rc = PULSEAUDIO_pa_stream_connect_record(h->
stream, h->
device_name, &paattr, flags);
645 return SDL_SetError(
"Could not connect PulseAudio stream");
649 if (PULSEAUDIO_pa_mainloop_iterate(h->
mainloop, 1,
NULL) < 0) {
652 state = PULSEAUDIO_pa_stream_get_state(h->
stream);
653 if (!PA_STREAM_IS_GOOD(state)) {
654 return SDL_SetError(
"Could not connect PulseAudio stream");
656 }
while (state != PA_STREAM_READY);
662 static pa_mainloop *hotplug_mainloop =
NULL;
663 static pa_context *hotplug_context =
NULL;
670 SinkInfoCallback(pa_context *c,
const pa_sink_info *i,
int is_last,
void *data)
679 SourceInfoCallback(pa_context *c,
const pa_source_info *i,
int is_last,
void *data)
683 if (i->monitor_of_sink == PA_INVALID_INDEX) {
691 HotplugCallback(pa_context *c, pa_subscription_event_type_t
t,
uint32_t idx,
void *data)
693 const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
694 const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
696 if (added || removed) {
697 const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
698 const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
702 PULSEAUDIO_pa_context_get_sink_info_by_index(hotplug_context, idx, SinkInfoCallback,
NULL);
703 }
else if (added && source) {
704 PULSEAUDIO_pa_context_get_source_info_by_index(hotplug_context, idx, SourceInfoCallback,
NULL);
705 }
else if (removed && (sink || source)) {
714 HotplugThread(
void *data)
718 PULSEAUDIO_pa_context_set_subscribe_callback(hotplug_context, HotplugCallback,
NULL);
719 o = PULSEAUDIO_pa_context_subscribe(hotplug_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE,
NULL,
NULL);
720 PULSEAUDIO_pa_operation_unref(o);
721 PULSEAUDIO_pa_mainloop_run(hotplug_mainloop,
NULL);
726 PULSEAUDIO_DetectDevices()
728 WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_sink_info_list(hotplug_context, SinkInfoCallback,
NULL));
729 WaitForPulseOperation(hotplug_mainloop, PULSEAUDIO_pa_context_get_source_info_list(hotplug_context, SourceInfoCallback,
NULL));
736 PULSEAUDIO_Deinitialize(
void)
738 if (hotplug_thread) {
739 PULSEAUDIO_pa_mainloop_quit(hotplug_mainloop, 0);
741 hotplug_thread =
NULL;
744 DisconnectFromPulseServer(hotplug_mainloop, hotplug_context);
745 hotplug_mainloop =
NULL;
746 hotplug_context =
NULL;
748 UnloadPulseAudioLibrary();
754 if (LoadPulseAudioLibrary() < 0) {
758 if (ConnectToPulseServer(&hotplug_mainloop, &hotplug_context) < 0) {
759 UnloadPulseAudioLibrary();
780 "pulseaudio",
"PulseAudio", PULSEAUDIO_Init, 0
GLsizei GLenum GLboolean sink
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
void(* DetectDevices)(void)
GLint GLint GLint GLint GLint x
LPDIRECTSOUNDCAPTUREBUFFER capturebuf
GLfloat GLfloat GLfloat GLfloat h
static screen_context_t context
void(* PlayDevice)(_THIS)
void(* WaitDevice)(_THIS)
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
SDL_AudioFormat SDL_NextAudioFormat(void)
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
EGLImageKHR EGLint EGLint * handle
void SDL_RemoveAudioDevice(const int iscapture, void *handle)
void(* Deinitialize)(void)
GLsizei GLsizei GLchar * source
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
GLenum GLenum GLsizei const GLuint GLboolean enabled
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)
#define SDL_assert(condition)
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
#define SDL_OutOfMemory()
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
void(* CloseDevice)(_THIS)
void(* FlushCapture)(_THIS)
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
Uint8 *(* GetDeviceBuf)(_THIS)
AudioBootStrap PULSEAUDIO_bootstrap
void * SDL_LoadFunction(void *handle, const char *name)
#define SDL_SetThreadPriority
void SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)