[Openal-devel] [PATCH] SDL 1.2 backend for OpenAL-Soft
Ryan C. Gordon
icculus at icculus.org
Mon Jan 4 23:29:55 PST 2010
This may or may not be of general interest to the list, but I'm passing
it along in case it is.
I'm getting a lot of audio bug reports for my Linux port of Aquaria.
Most, if not all of these, are probably _not_ OpenAL-Soft's fault, but
rather the fault of crappy and/or misconfigured drivers on end users'
systems...not to mention layers upon layers of emulation of other audio
APIs, so OpenAL may or may not really be talking to what it thinks it is.
It's a total mess.
That being said, we just spent an enormous amount of time for the latest
SDL 1.2 release fixing and tapdancing around these issues, not to
mention supporting a handful of audio targets OpenAL-Soft doesn't. As
we're shipping SDL with the game anyhow, I took the cheap route here and
just hooked up SDL as an OpenAL backend and disabled all the other
backends for the build I'll be shipping.
So my game is now talking to what it thinks is FMOD, but is really a
simple reimplementation of that API, which talks to OpenAL, which talks
to SDL, which probably talks to asound, which probably talks to
PulseAudio, which talks to ALSA's kernel drivers. This is engineering at
its best. :)
Anyhow, this patch adds SDL as a possible backend, and optionally
supports dlopen()/LoadLibrary(). Since this targets SDL 1.2, there isn't
multiple device support, capture support, or disconnect support. If this
is considered useful enough to commit to revision control, I'll probably
update it for SDL 1.3 when that is ready, which would remove all these
limitations.
But in the unending war of "I just want this game to make sound and
every library is broken differently on my system," this might be a
useful patch to have.
Attached in unified diff format, against svn revision #1955.
--ryan.
-------------- next part --------------
Index: OpenAL-Soft/Alc/ALc.c
===================================================================
--- OpenAL-Soft/Alc/ALc.c (revision 1955)
+++ OpenAL-Soft/Alc/ALc.c (working copy)
@@ -69,6 +69,9 @@
#ifdef HAVE_PULSEAUDIO
{ "pulse", alc_pulse_init, alc_pulse_deinit, alc_pulse_probe, EmptyFuncs },
#endif
+#ifdef HAVE_SDL
+ { "sdl", alc_sdl_init, alc_sdl_deinit, alc_sdl_probe, EmptyFuncs },
+#endif
{ "wave", alc_wave_init, alc_wave_deinit, alc_wave_probe, EmptyFuncs },
Index: OpenAL-Soft/Alc/sdl.c
===================================================================
--- OpenAL-Soft/Alc/sdl.c (revision 0)
+++ OpenAL-Soft/Alc/sdl.c (revision 0)
@@ -0,0 +1,306 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include "alMain.h"
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include "AL/al.h"
+#include "AL/alc.h"
+
+/* SDL.h may redefine this. */
+#ifdef HAVE_STDINT_H
+#undef HAVE_STDINT_H
+#endif
+
+#include "SDL.h"
+
+static const ALCchar sdl_device[] = "Simple Directmedia Layer";
+
+/* dynamic library loading code. */
+static void *sdl_handle;
+static volatile ALuint load_count;
+
+#define MAKE_FUNC(x) static typeof(x) * p##x
+MAKE_FUNC(SDL_InitSubSystem);
+MAKE_FUNC(SDL_OpenAudio);
+MAKE_FUNC(SDL_CloseAudio);
+MAKE_FUNC(SDL_PauseAudio);
+MAKE_FUNC(SDL_QuitSubSystem);
+MAKE_FUNC(SDL_WasInit);
+MAKE_FUNC(SDL_GetError);
+#undef MAKE_FUNC
+
+static void sdl_load(void)
+{
+ if(load_count == 0)
+ {
+#ifdef _WIN32
+ sdl_handle = LoadLibrary("SDL.dll");
+#define LOAD_FUNC(x) do { \
+ p##x = GetProcAddress(sdl_handle, #x); \
+ if(!(p##x)) { \
+ AL_PRINT("Could not load %s from SDL.dll\n", #x); \
+ FreeLibrary(sdl_handle); \
+ sdl_handle = NULL; \
+ return; \
+ } \
+} while(0)
+
+#elif defined (HAVE_DLFCN_H)
+
+ const char *err;
+#if defined(__APPLE__) && defined(__MACH__)
+ sdl_handle = dlopen("libSDL-1.2.0.dylib", RTLD_NOW);
+#else
+ sdl_handle = dlopen("libSDL-1.2.so.0", RTLD_NOW);
+#endif
+ dlerror();
+
+#define LOAD_FUNC(x) do { \
+ p##x = dlsym(sdl_handle, #x); \
+ if((err=dlerror()) != NULL) { \
+ AL_PRINT("Could not load %s from libSDL-1.2: %s\n", #x, err); \
+ dlclose(sdl_handle); \
+ sdl_handle = NULL; \
+ return; \
+ } \
+} while(0)
+
+#else
+
+ sdl_handle = (void*)0xDEADBEEF;
+#define LOAD_FUNC(x) p##x = (x)
+
+#endif
+ if(!sdl_handle)
+ return;
+
+ LOAD_FUNC(SDL_InitSubSystem);
+ LOAD_FUNC(SDL_OpenAudio);
+ LOAD_FUNC(SDL_CloseAudio);
+ LOAD_FUNC(SDL_PauseAudio);
+ LOAD_FUNC(SDL_QuitSubSystem);
+ LOAD_FUNC(SDL_WasInit);
+ LOAD_FUNC(SDL_GetError);
+
+#undef LOAD_FUNC
+ }
+ ++load_count;
+}
+
+static void sdl_unload(void)
+{
+ if(load_count == 0 || --load_count > 0)
+ return;
+
+#ifdef _WIN32
+ FreeLibrary(sdl_handle);
+#elif defined (HAVE_DLFCN_H)
+ dlclose(sdl_handle);
+#endif
+ sdl_handle = NULL;
+}
+
+
+static void SDLCALL sdl_callback(void *userdata, Uint8 *stream, int len)
+{
+ ALCdevice *pDevice = (ALCdevice*)userdata;
+ const ALint frameSize = aluChannelsFromFormat(pDevice->Format) *
+ aluBytesFromFormat(pDevice->Format);
+ aluMixData(pDevice, stream, len/frameSize);
+}
+
+static ALCboolean sdl_open_playback(ALCdevice *device, const ALCchar *deviceName)
+{
+ (void) device;
+ (void) deviceName;
+
+ sdl_load();
+ if(!sdl_handle)
+ return ALC_FALSE;
+
+ return ALC_TRUE;
+}
+
+static void sdl_close_playback(ALCdevice *device)
+{
+ (void) device;
+ pSDL_PauseAudio(1);
+ pSDL_CloseAudio();
+ pSDL_QuitSubSystem(SDL_INIT_AUDIO);
+ sdl_unload();
+}
+
+static ALCboolean sdl_reset_playback(ALCdevice *device)
+{
+ SDL_AudioSpec desired;
+ memset(&desired, '\0', sizeof (desired));
+
+ if (pSDL_WasInit(SDL_INIT_AUDIO))
+ {
+ pSDL_PauseAudio(1);
+ pSDL_CloseAudio();
+ pSDL_QuitSubSystem(SDL_INIT_AUDIO);
+ }
+
+ switch(aluBytesFromFormat(device->Format))
+ {
+ case 1:
+ desired.format = AUDIO_U8;
+ break;
+ case 4:
+ switch(aluChannelsFromFormat(device->Format))
+ {
+ case 1: device->Format = AL_FORMAT_MONO16; break;
+ case 2: device->Format = AL_FORMAT_STEREO16; break;
+ case 4: device->Format = AL_FORMAT_QUAD16; break;
+ case 6: device->Format = AL_FORMAT_51CHN16; break;
+ case 7: device->Format = AL_FORMAT_61CHN16; break;
+ case 8: device->Format = AL_FORMAT_71CHN16; break;
+ }
+ /* fall-through */
+ case 2:
+ desired.format = AUDIO_S16;
+ break;
+ default:
+ AL_PRINT("Unknown format: 0x%x\n", device->Format);
+ return ALC_FALSE;
+ }
+
+ desired.freq = device->Frequency;
+ desired.channels = aluChannelsFromFormat(device->Format);
+ desired.samples = device->UpdateSize * desired.channels;
+ desired.callback = sdl_callback;
+ desired.userdata = device;
+
+ device->NumUpdates = 2;
+
+ if (pSDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
+ {
+ AL_PRINT("SDL_InitSubSystem(SDL_INIT_AUDIO) failed: %s\n", pSDL_GetError());
+ return ALC_FALSE;
+ }
+
+ if (pSDL_OpenAudio(&desired, NULL) == -1)
+ {
+ AL_PRINT("SDL_OpenAudio failed: %s\n", pSDL_GetError());
+ pSDL_QuitSubSystem(SDL_INIT_AUDIO);
+ return ALC_FALSE;
+ }
+
+ pSDL_PauseAudio(0);
+ return ALC_TRUE;
+}
+
+static void sdl_stop_playback(ALCdevice *device)
+{
+ (void) device;
+ pSDL_PauseAudio(1);
+}
+
+static void sdl_warn_nocapture(void)
+{
+ static int warned = 0;
+ if (!warned)
+ {
+ AL_PRINT("No OpenAL capture support in the SDL backend at this time.\n");
+ warned = 1;
+ }
+}
+
+static ALCboolean sdl_open_capture(ALCdevice *device, const ALCchar *deviceName)
+{
+ (void) device;
+ (void) deviceName;
+ sdl_warn_nocapture();
+ return ALC_FALSE;
+}
+
+static void sdl_close_capture(ALCdevice *device)
+{
+ (void) device;
+ sdl_warn_nocapture();
+}
+
+static void sdl_start_capture(ALCdevice *pDevice)
+{
+ (void) pDevice;
+ sdl_warn_nocapture();
+}
+
+static void sdl_stop_capture(ALCdevice *pDevice)
+{
+ (void) pDevice;
+ sdl_warn_nocapture();
+}
+
+static void sdl_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
+{
+ (void) pDevice;
+ (void) pBuffer;
+ (void) lSamples;
+ sdl_warn_nocapture();
+}
+
+static ALCuint sdl_available_samples(ALCdevice *pDevice)
+{
+ (void) pDevice;
+ sdl_warn_nocapture();
+ return 0;
+}
+
+
+BackendFuncs sdl_funcs = {
+ sdl_open_playback,
+ sdl_close_playback,
+ sdl_reset_playback,
+ sdl_stop_playback,
+ sdl_open_capture,
+ sdl_close_capture,
+ sdl_start_capture,
+ sdl_stop_capture,
+ sdl_capture_samples,
+ sdl_available_samples
+};
+
+void alc_sdl_init(BackendFuncs *func_list)
+{
+ *func_list = sdl_funcs;
+}
+
+void alc_sdl_deinit(void)
+{
+ // no-op.
+}
+
+void alc_sdl_probe(int type)
+{
+ if(type == DEVICE_PROBE)
+ AppendDeviceList(sdl_device);
+ else if(type == ALL_DEVICE_PROBE)
+ AppendAllDeviceList(sdl_device);
+ /* No SDL capture support until SDL 1.3! */
+}
+
Index: OpenAL-Soft/config.h.in
===================================================================
--- OpenAL-Soft/config.h.in (revision 1955)
+++ OpenAL-Soft/config.h.in (working copy)
@@ -25,6 +25,9 @@
/* Define if we have the PulseAudio backend */
#cmakedefine HAVE_PULSEAUDIO
+/* Define if we have the SDL backend */
+#cmakedefine HAVE_SDL
+
/* Define if we have dlfcn.h */
#cmakedefine HAVE_DLFCN_H
Index: OpenAL-Soft/OpenAL32/Include/alMain.h
===================================================================
--- OpenAL-Soft/OpenAL32/Include/alMain.h (revision 1955)
+++ OpenAL-Soft/OpenAL32/Include/alMain.h (working copy)
@@ -243,6 +243,9 @@
void alc_pulse_init(BackendFuncs *func_list);
void alc_pulse_deinit(void);
void alc_pulse_probe(int type);
+void alc_sdl_init(BackendFuncs *func_list);
+void alc_sdl_deinit(void);
+void alc_sdl_probe(int type);
struct ALCdevice_struct
Index: OpenAL-Soft/CMakeLists.txt
===================================================================
--- OpenAL-Soft/CMakeLists.txt (revision 1955)
+++ OpenAL-Soft/CMakeLists.txt (working copy)
@@ -31,6 +31,7 @@
OPTION(WINMM "Check for Windows Multimedia backend" ON)
OPTION(PORTAUDIO "Check for PortAudio backend" ON)
OPTION(PULSEAUDIO "Check for PulseAudio backend" ON)
+OPTION(SDL "Check for SDL backend" ON)
OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON)
@@ -412,6 +413,22 @@
ENDIF()
ENDIF()
+# Check SDL backend
+IF(SDL)
+ FIND_PACKAGE(SDL)
+ IF(SDL_FOUND)
+ SET(HAVE_SDL 1)
+ SET(ALC_OBJS ${ALC_OBJS} Alc/sdl.c)
+ INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR})
+ IF(HAVE_DLFCN_H)
+ SET(BACKENDS "${BACKENDS} SDL,")
+ ELSE()
+ SET(BACKENDS "${BACKENDS} SDL \(linked\),")
+ SET(EXTRA_LIBS ${SDL_LIBRARY} ${EXTRA_LIBS})
+ ENDIF()
+ ENDIF()
+ENDIF()
+
# This is always available
SET(BACKENDS "${BACKENDS} WaveFile")
More information about the Openal-devel
mailing list