From 8b54d59b8c201c90da4ec45bf81d7a945b143d10 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 7 Dec 2008 01:05:39 -0800 Subject: [PATCH] Add an SDL backend --- Alc/ALc.c | 3 + Alc/sdl.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 19 +++ OpenAL32/Include/alMain.h | 1 + alsoftrc.sample | 5 +- config.h.in | 3 + 6 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 Alc/sdl.c diff --git a/Alc/ALc.c b/Alc/ALc.c index 13e2662c..925e4867 100644 --- a/Alc/ALc.c +++ b/Alc/ALc.c @@ -67,6 +67,9 @@ static struct { #ifdef HAVE_WINMM { "winmm", alcWinMMInit, EmptyFuncs }, #endif +#ifdef HAVE_SDL + { "sdl", alc_sdl_init, EmptyFuncs }, +#endif { "wave", alc_wave_init, EmptyFuncs }, diff --git a/Alc/sdl.c b/Alc/sdl.c new file mode 100644 index 00000000..791763ba --- /dev/null +++ b/Alc/sdl.c @@ -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" +#include "AL/al.h" +#include "AL/alc.h" + +#include +#include +#ifdef HAVE_DLFCN_H +#include +#endif + +#include + +static void *sdl_handle; +#define MAKE_FUNC(x) static typeof(x) * p##x +MAKE_FUNC(SDL_PauseAudio); +MAKE_FUNC(SDL_CloseAudio); +MAKE_FUNC(SDL_OpenAudio); +MAKE_FUNC(SDL_InitSubSystem); +MAKE_FUNC(SDL_GetError); +MAKE_FUNC(SDL_LockAudio); +MAKE_FUNC(SDL_UnlockAudio); +#undef MAKE_FUNC + +static char *sdl_device; +/* SDL audio can only be initialized once per process */ +static int initialized; + +typedef struct { + SDL_AudioSpec audioSpec; + volatile int killNow; + ALvoid *thread; + + ALubyte *mix_data; + int data_size; + int data_read; + int data_write; +} sdl_data; + + +static void SDLCALL fillAudio(void *userdata, Uint8 *stream, int len) +{ + sdl_data *data = (sdl_data*)userdata; + int rem = data->data_size-data->data_read; + if(len >= rem) + { + memcpy(stream, data->mix_data + data->data_read, rem); + stream += rem; + len -= rem; + data->data_read = 0; + } + if(len > 0) + { + memcpy(stream, data->mix_data + data->data_read, len); + data->data_read += len; + } +} + +static ALuint SDLProc(ALvoid *ptr) +{ + ALCdevice *pDevice = (ALCdevice*)ptr; + sdl_data *data = (sdl_data*)pDevice->ExtraData; + int len, rem; + + pSDL_PauseAudio(0); + while(!data->killNow) + { + pSDL_LockAudio(); + + len = (data->data_read-data->data_write+data->data_size)%data->data_size; + if(len == 0) + { + pSDL_UnlockAudio(); + + Sleep(1); + continue; + } + + rem = data->data_size - data->data_write; + + SuspendContext(NULL); + if(len > rem) + { + aluMixData(pDevice->Context, data->mix_data+data->data_write, rem, pDevice->Format); + aluMixData(pDevice->Context, data->mix_data, len-rem, pDevice->Format); + } + else + aluMixData(pDevice->Context, data->mix_data+data->data_write, len, pDevice->Format); + ProcessContext(NULL); + + data->data_write = data->data_read; + + pSDL_UnlockAudio(); + } + pSDL_PauseAudio(1); + + return 0; +} + +static ALCboolean sdl_open_playback(ALCdevice *device, const ALCchar *deviceName) +{ + SDL_AudioSpec sdlSpec; + ALuint frameSize; + sdl_data *data; + + if(initialized || !sdl_device) + return ALC_FALSE; + + if(deviceName) + { + if(strcmp(deviceName, sdl_device)) + return ALC_FALSE; + device->szDeviceName = sdl_device; + } + else + device->szDeviceName = sdl_device; + + data = (sdl_data*)calloc(1, sizeof(sdl_data)); + data->killNow = 0; + + frameSize = aluBytesFromFormat(device->Format) * + aluChannelsFromFormat(device->Format); + + sdlSpec.freq = device->Frequency; + sdlSpec.channels = aluChannelsFromFormat(device->Format); + switch(aluBytesFromFormat(device->Format)) + { + case 1: + sdlSpec.format = AUDIO_U8; + break; + case 2: + sdlSpec.format = AUDIO_S16SYS; + break; + default: + AL_PRINT("Unknown format?! %x\n", device->Format); + free(data); + return ALC_FALSE; + } + sdlSpec.samples = 1; + while(sdlSpec.samples < device->UpdateSize) + sdlSpec.samples <<= 1; + sdlSpec.samples >>= 1; + + sdlSpec.callback = fillAudio; + sdlSpec.userdata = data; + + if(pSDL_OpenAudio(&sdlSpec, &data->audioSpec) < 0) + { + AL_PRINT("Audio init failed: %s\n", pSDL_GetError()); + free(data); + return ALC_FALSE; + } + + if(!((data->audioSpec.format == AUDIO_U8 && aluBytesFromFormat(device->Format) == 1) || + (data->audioSpec.format == AUDIO_S16SYS && aluBytesFromFormat(device->Format) == 2))) + { + AL_PRINT("Could not set %d-bit, got format %#x instead\n", aluBytesFromFormat(device->Format), data->audioSpec.format); + pSDL_CloseAudio(); + free(data); + return ALC_FALSE; + } + if(aluChannelsFromFormat(device->Format) != data->audioSpec.channels) + { + AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), data->audioSpec.channels); + pSDL_CloseAudio(); + free(data); + return ALC_FALSE; + } + + device->Frequency = data->audioSpec.freq; + device->UpdateSize = data->audioSpec.size / frameSize; + + data->data_size = device->UpdateSize * frameSize * 2; + data->mix_data = malloc(data->data_size); + if(data->mix_data == NULL) + { + AL_PRINT("Could not allocate %d bytes\n", data->data_size); + pSDL_CloseAudio(); + free(data); + return ALC_FALSE; + } + memset(data->mix_data, data->audioSpec.silence, data->data_size); + + device->ExtraData = data; + data->thread = StartThread(SDLProc, device); + if(data->thread == NULL) + { + pSDL_CloseAudio(); + device->ExtraData = NULL; + free(data->mix_data); + free(data); + return ALC_FALSE; + } + + initialized = 1; + + return ALC_TRUE; +} + +static void sdl_close_playback(ALCdevice *device) +{ + sdl_data *data = (sdl_data*)device->ExtraData; + data->killNow = 1; + StopThread(data->thread); + + pSDL_CloseAudio(); + initialized = 0; + + free(data->mix_data); + free(data); + device->ExtraData = NULL; +} + + +static ALCboolean sdl_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) +{ + return ALC_FALSE; + (void)device; + (void)deviceName; + (void)frequency; + (void)format; + (void)SampleSize; +} + + +BackendFuncs sdl_funcs = { + sdl_open_playback, + sdl_close_playback, + sdl_open_capture, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +void alc_sdl_init(BackendFuncs *func_list) +{ + const char *str; + + *func_list = sdl_funcs; + +#ifdef HAVE_DLFCN_H +#if defined(__APPLE__) && defined(__MACH__) +# define SDLLIB "SDL.framework/SDL" +#else +# define SDLLIB "libSDL.so" +#endif + sdl_handle = dlopen(SDLLIB, RTLD_NOW); + if(!sdl_handle) + return; + dlerror(); + +#define LOAD_FUNC(f) do { \ + p##f = (typeof(f)*)dlsym(sdl_handle, #f); \ + if((str=dlerror()) != NULL) \ + { \ + dlclose(sdl_handle); \ + sdl_handle = NULL; \ + AL_PRINT("Could not load %s from "SDLLIB": %s\n", #f, str); \ + return; \ + } \ +} while(0) +#else + str = NULL; + sdl_handle = 0xDEADBEEF; +#define LOAD_FUNC(f) p##f = f +#endif + + LOAD_FUNC(SDL_PauseAudio); + LOAD_FUNC(SDL_CloseAudio); + LOAD_FUNC(SDL_OpenAudio); + LOAD_FUNC(SDL_InitSubSystem); + LOAD_FUNC(SDL_GetError); + LOAD_FUNC(SDL_LockAudio); + LOAD_FUNC(SDL_UnlockAudio); + +#undef LOAD_FUNC + + if(pSDL_InitSubSystem(SDL_INIT_AUDIO) < 0) + return; + + sdl_device = AppendDeviceList("SDL Software"); + AppendAllDeviceList(sdl_device); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index d418b428..415b8839 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ OPTION(OSS "Check for OSS backend" ON) OPTION(SOLARIS "Check for Solaris backend" ON) OPTION(DSOUND "Check for DirectSound backend" ON) OPTION(WINMM "Check for Windows Multimedia backend" ON) +OPTION(SDL "Check for SDL backend" ON) OPTION(DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -320,6 +321,24 @@ IF(HAVE_WINDOWS_H) ENDIF() ENDIF() +# Check SDL backend +IF(SDL) + CHECK_INCLUDE_FILE(SDL/SDL.h HAVE_SDL_SDL_H) + IF(HAVE_SDL_SDL_H) + CHECK_LIBRARY_EXISTS(SDL SDL_Init "" HAVE_LIBSDL) + IF(HAVE_DLFCN_H OR HAVE_LIBSDL) + SET(HAVE_SDL 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/sdl.c) + IF(HAVE_DLFCN_H) + SET(BACKENDS "${BACKENDS} SDL,") + ELSE() + SET(BACKENDS "${BACKENDS} SDL \(linked\),") + SET(EXTRA_LIBS SDL ${EXTRA_LIBS}) + ENDIF() + ENDIF() + ENDIF() +ENDIF() + # This is always available SET(BACKENDS "${BACKENDS} WaveFile") diff --git a/OpenAL32/Include/alMain.h b/OpenAL32/Include/alMain.h index 151675eb..042ab779 100644 --- a/OpenAL32/Include/alMain.h +++ b/OpenAL32/Include/alMain.h @@ -147,6 +147,7 @@ void alc_oss_init(BackendFuncs *func_list); void alc_solaris_init(BackendFuncs *func_list); void alcDSoundInit(BackendFuncs *func_list); void alcWinMMInit(BackendFuncs *FuncList); +void alc_sdl_init(BackendFuncs *func_list); void alc_wave_init(BackendFuncs *func_list); diff --git a/alsoftrc.sample b/alsoftrc.sample index 0974a41c..e447d528 100644 --- a/alsoftrc.sample +++ b/alsoftrc.sample @@ -59,7 +59,7 @@ drivers = # Sets the backend driver list order, comma-seperated. Unknown # backends and duplicated names are ignored, and unlisted backends # won't be considered for use. An empty list means the default. # Default is: - # alsa,oss,solaris,dsound,winmm,wave + # alsa,oss,solaris,dsound,winmm,sdl,wave excludefx = # Sets which effects to exclude, preventing apps from using them. # This can help for apps that try to use effects which are too CPU @@ -100,6 +100,9 @@ periods = 4 # Sets the number of updates for the output buffer. Default is 4 [winmm] # Windows Multimedia backend stuff # Nothing yet... +[sdl] # SDL backend stuff + # Nothing yet... + [wave] # Wave File Writer stuff file = # Sets the filename of the wave file to write to. An empty name # prevents the backend from opening, even when explicitly requested. diff --git a/config.h.in b/config.h.in index 9cc80eeb..fce1dab2 100644 --- a/config.h.in +++ b/config.h.in @@ -19,6 +19,9 @@ /* Define if we have the Windows Multimedia backend */ #cmakedefine HAVE_WINMM +/* Define if we have the SDL backend */ +#cmakedefine HAVE_SDL + /* Define if we have dlfcn.h */ #cmakedefine HAVE_DLFCN_H -- 2.11.4.GIT