From 1f5453075c20018c450d57be4bf434d14beb9dbe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 19 May 2009 06:35:12 -0700 Subject: [PATCH] Dynamically load PulseAudio --- Alc/pulseaudio.c | 263 +++++++++++++++++++++++++++++++++++++++---------------- CMakeLists.txt | 9 +- 2 files changed, 194 insertions(+), 78 deletions(-) diff --git a/Alc/pulseaudio.c b/Alc/pulseaudio.c index 6ba0255a..83c7c84b 100644 --- a/Alc/pulseaudio.c +++ b/Alc/pulseaudio.c @@ -21,6 +21,9 @@ #include "config.h" #include "alMain.h" +#ifdef HAVE_DLFCN_H +#include +#endif #include @@ -41,6 +44,45 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) #error Invalid PulseAudio API version #endif +static void *pa_handle; +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(pa_context_unref); +MAKE_FUNC(pa_sample_spec_valid); +MAKE_FUNC(pa_stream_drop); +MAKE_FUNC(pa_strerror); +MAKE_FUNC(pa_context_get_state); +MAKE_FUNC(pa_stream_get_state); +MAKE_FUNC(pa_threaded_mainloop_signal); +MAKE_FUNC(pa_stream_peek); +MAKE_FUNC(pa_threaded_mainloop_wait); +MAKE_FUNC(pa_threaded_mainloop_unlock); +MAKE_FUNC(pa_context_new); +MAKE_FUNC(pa_threaded_mainloop_stop); +MAKE_FUNC(pa_context_disconnect); +MAKE_FUNC(pa_threaded_mainloop_start); +MAKE_FUNC(pa_threaded_mainloop_get_api); +MAKE_FUNC(pa_context_set_state_callback); +MAKE_FUNC(pa_stream_write); +MAKE_FUNC(pa_xfree); +MAKE_FUNC(pa_stream_connect_record); +MAKE_FUNC(pa_stream_connect_playback); +MAKE_FUNC(pa_path_get_filename); +MAKE_FUNC(pa_get_binary_name); +MAKE_FUNC(pa_threaded_mainloop_free); +MAKE_FUNC(pa_context_errno); +MAKE_FUNC(pa_xmalloc0); +MAKE_FUNC(pa_stream_unref); +MAKE_FUNC(pa_threaded_mainloop_accept); +MAKE_FUNC(pa_stream_set_write_callback); +MAKE_FUNC(pa_threaded_mainloop_new); +MAKE_FUNC(pa_context_connect); +MAKE_FUNC(pa_stream_set_read_callback); +MAKE_FUNC(pa_stream_set_state_callback); +MAKE_FUNC(pa_stream_new); +MAKE_FUNC(pa_stream_disconnect); +MAKE_FUNC(pa_threaded_mainloop_lock); +#undef MAKE_FUNC + typedef struct { ALCdevice *device; @@ -72,7 +114,7 @@ static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ { pulse_data *data = pdata; - switch(pa_stream_get_state(stream)) + switch(ppa_stream_get_state(stream)) { case PA_STREAM_READY: AL_PRINT("%s: %s ready!\n", data->context_name, data->stream_name); @@ -80,7 +122,7 @@ static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ case PA_STREAM_FAILED: AL_PRINT("%s: %s: Connection failed: %s\n", data->context_name, - data->stream_name, pa_strerror(pa_context_errno(data->context))); + data->stream_name, ppa_strerror(ppa_context_errno(data->context))); break; case PA_STREAM_TERMINATED: @@ -91,14 +133,14 @@ static void stream_state_callback(pa_stream *stream, void *pdata) //{{{ break; } - pa_threaded_mainloop_signal(data->loop, 1); + ppa_threaded_mainloop_signal(data->loop, 1); } //}}} static void context_state_callback(pa_context *context, void *pdata) //{{{ { pulse_data *data = pdata; - switch(pa_context_get_state(context)) + switch(ppa_context_get_state(context)) { case PA_CONTEXT_READY: AL_PRINT("%s ready!\n", data->context_name); @@ -106,7 +148,7 @@ static void context_state_callback(pa_context *context, void *pdata) //{{{ case PA_CONTEXT_FAILED: AL_PRINT("%s: Connection failed: %s\n", data->context_name, - pa_strerror(pa_context_errno(context))); + ppa_strerror(ppa_context_errno(context))); break; case PA_CONTEXT_TERMINATED: @@ -117,7 +159,7 @@ static void context_state_callback(pa_context *context, void *pdata) //{{{ break; } - pa_threaded_mainloop_signal(data->loop, 1); + ppa_threaded_mainloop_signal(data->loop, 1); } //}}} //}}} @@ -125,13 +167,13 @@ static void context_state_callback(pa_context *context, void *pdata) //{{{ static void stream_write_callback(pa_stream *stream, size_t len, void *pdata) //{{{ { ALCdevice *Device = pdata; - void *buf = pa_xmalloc0(len); + void *buf = ppa_xmalloc0(len); SuspendContext(NULL); aluMixData(Device->Context, buf, len, Device->Format); ProcessContext(NULL); - pa_stream_write(stream, buf, len, pa_xfree, 0, PA_SEEK_RELATIVE); + ppa_stream_write(stream, buf, len, ppa_xfree, 0, PA_SEEK_RELATIVE); } //}}} static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) //{{{ @@ -140,10 +182,10 @@ static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) pulse_data *data = Device->ExtraData; const void *buf; - if(pa_stream_peek(stream, &buf, &length) < 0) + if(ppa_stream_peek(stream, &buf, &length) < 0) { AL_PRINT("pa_stream_peek() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); return; } @@ -157,13 +199,13 @@ static void stream_read_callback(pa_stream *stream, size_t length, void *pdata) WriteRingBuffer(data->ring, buf, (lengthsamples) ? length : data->samples); - pa_stream_drop(stream); + ppa_stream_drop(stream); } //}}} //}}} static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum format, ALCuint samples, ALCuint frequency) //{{{ { - pulse_data *data = pa_xmalloc0(sizeof(pulse_data)); + pulse_data *data = ppa_xmalloc0(sizeof(pulse_data)); data->device = device; data->format = format; @@ -171,14 +213,14 @@ static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum fo data->frequency = frequency; data->frame_size = aluBytesFromFormat(format) * aluChannelsFromFormat(format); - if(pa_get_binary_name(data->path_name, sizeof(data->path_name))) - data->context_name = pa_path_get_filename(data->path_name); + if(ppa_get_binary_name(data->path_name, sizeof(data->path_name))) + data->context_name = ppa_path_get_filename(data->path_name); else data->context_name = "OpenAL Soft"; if(!(data->ring = CreateRingBuffer(data->frame_size, data->samples))) { - pa_xfree(data); + ppa_xfree(data); return ALC_FALSE; } @@ -218,86 +260,86 @@ static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum fo goto out2; } - if(pa_sample_spec_valid(&data->spec) == 0) + if(ppa_sample_spec_valid(&data->spec) == 0) { AL_PRINT("Invalid sample format\n"); goto out2; } - if(!(data->loop = pa_threaded_mainloop_new())) + if(!(data->loop = ppa_threaded_mainloop_new())) { AL_PRINT("pa_threaded_mainloop_new() failed!\n"); goto out2; } - if(pa_threaded_mainloop_start(data->loop) < 0) + if(ppa_threaded_mainloop_start(data->loop) < 0) { AL_PRINT("pa_threaded_mainloop_start() failed\n"); goto out3; } - pa_threaded_mainloop_lock(data->loop); + ppa_threaded_mainloop_lock(data->loop); - data->context = pa_context_new(pa_threaded_mainloop_get_api(data->loop), data->context_name); + data->context = ppa_context_new(ppa_threaded_mainloop_get_api(data->loop), data->context_name); if(!data->context) { AL_PRINT("pa_context_new() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_unlock(data->loop); goto out3; } - pa_context_set_state_callback(data->context, context_state_callback, data); + ppa_context_set_state_callback(data->context, context_state_callback, data); - if(pa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) + if(ppa_context_connect(data->context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { AL_PRINT("Context did not connect: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); - pa_context_unref(data->context); - pa_threaded_mainloop_unlock(data->loop); + ppa_context_unref(data->context); + ppa_threaded_mainloop_unlock(data->loop); data->context = NULL; goto out3; } - while(pa_context_get_state(data->context) != PA_CONTEXT_READY) + while(ppa_context_get_state(data->context) != PA_CONTEXT_READY) { - if(!PA_CONTEXT_IS_GOOD(pa_context_get_state(data->context))) + if(!PA_CONTEXT_IS_GOOD(ppa_context_get_state(data->context))) { - pa_context_unref(data->context); - pa_threaded_mainloop_unlock(data->loop); + ppa_context_unref(data->context); + ppa_threaded_mainloop_unlock(data->loop); data->context = NULL; goto out3; } - pa_threaded_mainloop_wait(data->loop); - pa_threaded_mainloop_accept(data->loop); + ppa_threaded_mainloop_wait(data->loop); + ppa_threaded_mainloop_accept(data->loop); } - data->stream = pa_stream_new(data->context, data->stream_name, &data->spec, NULL); + data->stream = ppa_stream_new(data->context, data->stream_name, &data->spec, NULL); if(!data->stream) { AL_PRINT("pa_stream_new() failed: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_unlock(data->loop); goto out4; } - pa_stream_set_state_callback(data->stream, stream_state_callback, data); + ppa_stream_set_state_callback(data->stream, stream_state_callback, data); if(device->IsCaptureDevice) { - if(pa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0) + if(ppa_stream_connect_record(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY) < 0) { AL_PRINT("Stream did not connect: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); - pa_stream_unref(data->stream); - pa_threaded_mainloop_unlock(data->loop); + ppa_stream_unref(data->stream); + ppa_threaded_mainloop_unlock(data->loop); data->stream = NULL; goto out4; @@ -305,57 +347,57 @@ static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum fo } else { - pa_stream_set_write_callback(data->stream, stream_write_callback, device); + ppa_stream_set_write_callback(data->stream, stream_write_callback, device); - if(pa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) + if(ppa_stream_connect_playback(data->stream, NULL, &data->attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) { AL_PRINT("Stream did not connect: %s\n", - pa_strerror(pa_context_errno(data->context))); + ppa_strerror(ppa_context_errno(data->context))); - pa_stream_unref(data->stream); - pa_threaded_mainloop_unlock(data->loop); + ppa_stream_unref(data->stream); + ppa_threaded_mainloop_unlock(data->loop); data->stream = NULL; goto out4; } } - while(pa_stream_get_state(data->stream) != PA_STREAM_READY) + while(ppa_stream_get_state(data->stream) != PA_STREAM_READY) { - if(!PA_STREAM_IS_GOOD(pa_stream_get_state(data->stream))) + if(!PA_STREAM_IS_GOOD(ppa_stream_get_state(data->stream))) { - pa_stream_unref(data->stream); - pa_threaded_mainloop_unlock(data->loop); + ppa_stream_unref(data->stream); + ppa_threaded_mainloop_unlock(data->loop); data->stream = NULL; goto out4; } - pa_threaded_mainloop_wait(data->loop); - pa_threaded_mainloop_accept(data->loop); + ppa_threaded_mainloop_wait(data->loop); + ppa_threaded_mainloop_accept(data->loop); } device->UpdateSize /= 4; - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; out4: - pa_threaded_mainloop_lock(data->loop); + ppa_threaded_mainloop_lock(data->loop); - pa_context_disconnect(data->context); - pa_context_unref(data->context); + ppa_context_disconnect(data->context); + ppa_context_unref(data->context); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_unlock(data->loop); out3: - pa_threaded_mainloop_stop(data->loop); - pa_threaded_mainloop_free(data->loop); + ppa_threaded_mainloop_stop(data->loop); + ppa_threaded_mainloop_free(data->loop); out2: device->ExtraData = NULL; device->szDeviceName = NULL; DestroyRingBuffer(data->ring); - pa_xfree(data); + ppa_xfree(data); return ALC_FALSE; } //}}} @@ -363,24 +405,24 @@ static void pulse_close(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); + ppa_threaded_mainloop_lock(data->loop); - pa_stream_disconnect(data->stream); - pa_stream_unref(data->stream); + ppa_stream_disconnect(data->stream); + ppa_stream_unref(data->stream); - pa_context_disconnect(data->context); - pa_context_unref(data->context); + ppa_context_disconnect(data->context); + ppa_context_unref(data->context); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_unlock(data->loop); - pa_threaded_mainloop_stop(data->loop); - pa_threaded_mainloop_free(data->loop); + ppa_threaded_mainloop_stop(data->loop); + ppa_threaded_mainloop_free(data->loop); device->ExtraData = NULL; device->szDeviceName = NULL; DestroyRingBuffer(data->ring); - pa_xfree(data); + ppa_xfree(data); } //}}} //}}} @@ -421,18 +463,18 @@ static void pulse_start_capture(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); - pa_stream_set_read_callback(data->stream, stream_read_callback, device); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_lock(data->loop); + ppa_stream_set_read_callback(data->stream, stream_read_callback, device); + ppa_threaded_mainloop_unlock(data->loop); } //}}} static void pulse_stop_capture(ALCdevice *device) //{{{ { pulse_data *data = device->ExtraData; - pa_threaded_mainloop_lock(data->loop); - pa_stream_set_read_callback(data->stream, NULL, NULL); - pa_threaded_mainloop_unlock(data->loop); + ppa_threaded_mainloop_lock(data->loop); + ppa_stream_set_read_callback(data->stream, NULL, NULL); + ppa_threaded_mainloop_unlock(data->loop); } //}}} static void pulse_capture_samples(ALCdevice *device, ALCvoid *buffer, ALCuint samples) //{{{ @@ -467,6 +509,77 @@ void alc_pulse_init(BackendFuncs *func_list) //{{{ { *func_list = pulse_funcs; +#ifdef _WIN32 + pa_handle = LoadLibrary("libpulse-0.dll"); +#define LOAD_FUNC(x) do { \ + p##x = GetProcAddress(pa_handle, #x); \ + if(!(p##x)) { \ + FreeLibrary(pa_handle); \ + pa_handle = NULL; \ + return; \ + } \ +} while(0) + +#elif defined (HAVE_DLFCN_H) + +#if defined(__APPLE__) && defined(__MACH__) + pa_handle = dlopen("libpulse.0.dylib", RTLD_NOW); +#else + pa_handle = dlopen("libpulse.so.0", RTLD_NOW); +#endif +#define LOAD_FUNC(x) do { \ + p##x = dlsym(pa_handle, #x); \ + if(!(p##x)) { \ + dlclose(pa_handle); \ + pa_handle = NULL; \ + return; \ + } \ +} while(0) + +#else + +#define LOAD_FUNC(x) p##x = (x) + +#endif + +LOAD_FUNC(pa_context_unref); +LOAD_FUNC(pa_sample_spec_valid); +LOAD_FUNC(pa_stream_drop); +LOAD_FUNC(pa_strerror); +LOAD_FUNC(pa_context_get_state); +LOAD_FUNC(pa_stream_get_state); +LOAD_FUNC(pa_threaded_mainloop_signal); +LOAD_FUNC(pa_stream_peek); +LOAD_FUNC(pa_threaded_mainloop_wait); +LOAD_FUNC(pa_threaded_mainloop_unlock); +LOAD_FUNC(pa_context_new); +LOAD_FUNC(pa_threaded_mainloop_stop); +LOAD_FUNC(pa_context_disconnect); +LOAD_FUNC(pa_threaded_mainloop_start); +LOAD_FUNC(pa_threaded_mainloop_get_api); +LOAD_FUNC(pa_context_set_state_callback); +LOAD_FUNC(pa_stream_write); +LOAD_FUNC(pa_xfree); +LOAD_FUNC(pa_stream_connect_record); +LOAD_FUNC(pa_stream_connect_playback); +LOAD_FUNC(pa_path_get_filename); +LOAD_FUNC(pa_get_binary_name); +LOAD_FUNC(pa_threaded_mainloop_free); +LOAD_FUNC(pa_context_errno); +LOAD_FUNC(pa_xmalloc0); +LOAD_FUNC(pa_stream_unref); +LOAD_FUNC(pa_threaded_mainloop_accept); +LOAD_FUNC(pa_stream_set_write_callback); +LOAD_FUNC(pa_threaded_mainloop_new); +LOAD_FUNC(pa_context_connect); +LOAD_FUNC(pa_stream_set_read_callback); +LOAD_FUNC(pa_stream_set_state_callback); +LOAD_FUNC(pa_stream_new); +LOAD_FUNC(pa_stream_disconnect); +LOAD_FUNC(pa_threaded_mainloop_lock); + +#undef LOAD_FUNC + pulse_device = AppendDeviceList("PulseAudio Software"); AppendAllDeviceList(pulse_device); diff --git a/CMakeLists.txt b/CMakeLists.txt index 509b5591..780875b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -352,9 +352,12 @@ IF(PULSEAUDIO) IF(HAVE_LIBPULSE) SET(HAVE_PULSEAUDIO 1) SET(ALC_OBJS ${ALC_OBJS} Alc/pulseaudio.c) - SET(BACKENDS "${BACKENDS} PulseAudio \(linked\),") - - SET(EXTRA_LIBS pulse ${EXTRA_LIBS}) + IF(HAVE_DLFCN_H) + SET(BACKENDS "${BACKENDS} PulseAudio,") + ELSE() + SET(BACKENDS "${BACKENDS} PulseAudio \(linked\),") + SET(EXTRA_LIBS pulse ${EXTRA_LIBS}) + ENDIF() ENDIF() ENDIF() ENDIF() -- 2.11.4.GIT