diff options
author | Jan Niklas Hasse <[email protected]> | 2018-03-07 20:53:56 +0100 |
---|---|---|
committer | Jan Niklas Hasse <[email protected]> | 2018-03-07 20:57:53 +0100 |
commit | 86319127e3e377312f815d55693f7b46569cde62 (patch) | |
tree | 92800a9483d73db1b70b876bc2044a67cee565da | |
parent | 179e1c4dbc143c9df50676ce538f628690799cd3 (diff) |
Add SDL2 backend for playback, fix #173
-rw-r--r-- | Alc/ALc.c | 3 | ||||
-rw-r--r-- | Alc/backends/base.h | 1 | ||||
-rw-r--r-- | Alc/backends/sdl2.c | 265 | ||||
-rw-r--r-- | CMakeLists.txt | 22 | ||||
-rw-r--r-- | config.h.in | 3 |
5 files changed, 293 insertions, 1 deletions
@@ -100,6 +100,9 @@ static struct BackendInfo BackendList[] = { #ifdef HAVE_OPENSL { "opensl", ALCopenslBackendFactory_getFactory }, #endif +#ifdef HAVE_SDL2 + { "sdl2", ALCsdl2BackendFactory_getFactory }, +#endif { "null", ALCnullBackendFactory_getFactory }, #ifdef HAVE_WAVE diff --git a/Alc/backends/base.h b/Alc/backends/base.h index 177f6869..b05523b2 100644 --- a/Alc/backends/base.h +++ b/Alc/backends/base.h @@ -147,6 +147,7 @@ ALCbackendFactory *ALCportBackendFactory_getFactory(void); ALCbackendFactory *ALCopenslBackendFactory_getFactory(void); ALCbackendFactory *ALCnullBackendFactory_getFactory(void); ALCbackendFactory *ALCwaveBackendFactory_getFactory(void); +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void); ALCbackendFactory *ALCloopbackFactory_getFactory(void); diff --git a/Alc/backends/sdl2.c b/Alc/backends/sdl2.c new file mode 100644 index 00000000..4b2d3acd --- /dev/null +++ b/Alc/backends/sdl2.c @@ -0,0 +1,265 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2018 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include <stdlib.h> +#include <SDL2/SDL.h> + +#include "alMain.h" +#include "alu.h" +#include "threads.h" +#include "compat.h" + +#include "backends/base.h" + + +typedef struct ALCsdl2Backend { + DERIVE_FROM_TYPE(ALCbackend); + + SDL_AudioDeviceID deviceID; + ALvoid *mBuffer; + ALuint mSize; + ALCboolean quit; + ATOMIC(int) killNow; + althrd_t thread; +} ALCsdl2Backend; + +static int ALCsdl2Backend_mixerProc(void *ptr); + +static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device); +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, void, Destruct) +static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name); +static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self); +static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self); +static void ALCsdl2Backend_stop(ALCsdl2Backend *self); +static DECLARE_FORWARD2(ALCsdl2Backend, ALCbackend, ALCenum, captureSamples, void*, ALCuint) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ALCuint, availableSamples) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, ClockLatency, getClockLatency) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, void, lock) +static DECLARE_FORWARD(ALCsdl2Backend, ALCbackend, void, unlock) +DECLARE_DEFAULT_ALLOCATORS(ALCsdl2Backend) + +DEFINE_ALCBACKEND_VTABLE(ALCsdl2Backend); + +static void ALCsdl2Backend_Construct(ALCsdl2Backend *self, ALCdevice *device) +{ + ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device); + SET_VTABLE2(ALCsdl2Backend, ALCbackend, self); + + self->mBuffer = NULL; + self->mSize = 0; + self->quit = ALC_FALSE; + if(SDL_WasInit(0) == 0) // Is SDL2 initialized at all? + { + SDL_Init(SDL_INIT_AUDIO); + self->quit = ALC_TRUE; + } + else if(!SDL_WasInit(SDL_INIT_AUDIO)) + SDL_InitSubSystem(SDL_INIT_AUDIO); + + ATOMIC_INIT(&self->killNow, AL_TRUE); +} + + +static int ALCsdl2Backend_mixerProc(void *ptr) +{ + ALCsdl2Backend *self = (ALCsdl2Backend*)ptr; + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + struct timespec now, start; + ALuint64 avail, done; + const long restTime = (long)((ALuint64)device->UpdateSize * 1000000000 / + device->Frequency / 2); + + SetRTPriority(); + althrd_setname(althrd_current(), MIXER_THREAD_NAME); + + done = 0; + if(altimespec_get(&start, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get starting time\n"); + return 1; + } + while(!ATOMIC_LOAD(&self->killNow, almemory_order_acquire) && + ATOMIC_LOAD(&device->Connected, almemory_order_acquire)) + { + if(altimespec_get(&now, AL_TIME_UTC) != AL_TIME_UTC) + { + ERR("Failed to get current time\n"); + return 1; + } + + avail = (now.tv_sec - start.tv_sec) * device->Frequency; + avail += (ALint64)(now.tv_nsec - start.tv_nsec) * device->Frequency / 1000000000; + if(avail < done) + { + /* Oops, time skipped backwards. Reset the number of samples done + * with one update available since we (likely) just came back from + * sleeping. */ + done = avail - device->UpdateSize; + } + + if(avail-done < device->UpdateSize) + al_nssleep(restTime); + else while(avail-done >= device->UpdateSize) + { + ALCsdl2Backend_lock(self); + aluMixData(device, self->mBuffer, device->UpdateSize); + SDL_QueueAudio(self->deviceID, self->mBuffer, self->mSize); + ALCsdl2Backend_unlock(self); + done += device->UpdateSize; + } + } + + return 0; +} + +static ALCenum ALCsdl2Backend_open(ALCsdl2Backend *self, const ALCchar *name) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + SDL_AudioSpec want; + SDL_zero(want); + want.freq = device->Frequency; + want.format = AUDIO_F32; + want.channels = 2; + want.samples = device->UpdateSize; + + if(!name) + name = SDL_GetAudioDeviceName(0, 0); + self->deviceID = SDL_OpenAudioDevice(name, 0, &want, NULL, 0); + if(self->deviceID == 0) { + ERR("Could not open device\n"); + return ALC_INVALID_VALUE; + } + alstr_copy_cstr(&STATIC_CAST(ALCbackend, self)->mDevice->DeviceName, name); + + return ALC_NO_ERROR; +} + +static ALCboolean ALCsdl2Backend_reset(ALCsdl2Backend *self) +{ + SetDefaultWFXChannelOrder(STATIC_CAST(ALCbackend, self)->mDevice); + return ALC_TRUE; +} + +static ALCboolean ALCsdl2Backend_start(ALCsdl2Backend *self) +{ + ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice; + self->mSize = device->UpdateSize * FrameSizeFromDevFmt( + device->FmtChans, device->FmtType, device->AmbiOrder + ); + self->mBuffer = malloc(self->mSize); + if(!self->mBuffer) + { + ERR("Buffer malloc failed\n"); + return ALC_FALSE; + } + + ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release); + if(althrd_create(&self->thread, ALCsdl2Backend_mixerProc, self) != althrd_success) + { + free(self->mBuffer); + self->mBuffer = NULL; + self->mSize = 0; + return ALC_FALSE; + } + SDL_PauseAudioDevice(self->deviceID, 0); + return ALC_TRUE; +} + +static void ALCsdl2Backend_stop(ALCsdl2Backend *self) +{ + int res; + + if(ATOMIC_EXCHANGE(&self->killNow, AL_TRUE, almemory_order_acq_rel)) + return; + althrd_join(self->thread, &res); + + free(self->mBuffer); + self->mBuffer = NULL; + if(self->quit) + SDL_Quit(); +} + + +typedef struct ALCsdl2BackendFactory { + DERIVE_FROM_TYPE(ALCbackendFactory); +} ALCsdl2BackendFactory; +#define ALCsdl2BACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCsdl2BackendFactory, ALCbackendFactory) } } + +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void); + +static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory *self); +static DECLARE_FORWARD(ALCsdl2BackendFactory, ALCbackendFactory, void, deinit) +static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory *self, ALCbackend_Type type); +static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory *self, enum DevProbe type); +static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory *self, ALCdevice *device, ALCbackend_Type type); +DEFINE_ALCBACKENDFACTORY_VTABLE(ALCsdl2BackendFactory); + + +ALCbackendFactory *ALCsdl2BackendFactory_getFactory(void) +{ + static ALCsdl2BackendFactory factory = ALCsdl2BACKENDFACTORY_INITIALIZER; + return STATIC_CAST(ALCbackendFactory, &factory); +} + + +static ALCboolean ALCsdl2BackendFactory_init(ALCsdl2BackendFactory* UNUSED(self)) +{ + return ALC_TRUE; +} + +static ALCboolean ALCsdl2BackendFactory_querySupport(ALCsdl2BackendFactory* UNUSED(self), ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + return ALC_TRUE; + return ALC_FALSE; +} + +static void ALCsdl2BackendFactory_probe(ALCsdl2BackendFactory* UNUSED(self), enum DevProbe type) +{ + if(type != ALL_DEVICE_PROBE) + return; + ALCboolean quit = ALC_FALSE; + if(SDL_WasInit(0) == 0) // Is SDL2 initialized at all? + { + SDL_Init(SDL_INIT_AUDIO); + quit = ALC_TRUE; + } + else if(!SDL_WasInit(SDL_INIT_AUDIO)) + SDL_InitSubSystem(SDL_INIT_AUDIO); + for(int i = 0; i < SDL_GetNumAudioDevices(0); ++i) + AppendAllDevicesList(SDL_GetAudioDeviceName(i, 0)); + if(quit) + SDL_Quit(); +} + +static ALCbackend* ALCsdl2BackendFactory_createBackend(ALCsdl2BackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type) +{ + if(type == ALCbackend_Playback) + { + ALCsdl2Backend *backend; + NEW_OBJ(backend, ALCsdl2Backend)(device); + if(!backend) return NULL; + return STATIC_CAST(ALCbackend, backend); + } + + return NULL; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index bd531128..0c6196f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -759,6 +759,7 @@ SET(HAVE_PULSEAUDIO 0) SET(HAVE_COREAUDIO 0) SET(HAVE_OPENSL 0) SET(HAVE_WAVE 0) +SET(HAVE_SDL2 0) # Check for SSE support OPTION(ALSOFT_REQUIRE_SSE "Require SSE support" OFF) @@ -1123,6 +1124,23 @@ IF(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) MESSAGE(FATAL_ERROR "Failed to enabled required OpenSL backend") ENDIF() +# Check for SDL2 backend +OPTION(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) +FIND_PACKAGE(SDL2) +IF(SDL2_FOUND) + # Off by default, since it adds a runtime dependency + OPTION(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) + IF(ALSOFT_BACKEND_SDL2) + SET(HAVE_SDL2 1) + SET(ALC_OBJS ${ALC_OBJS} Alc/backends/sdl2.c) + SET(BACKENDS "${BACKENDS} SDL2,") + SET(EXTRA_LIBS ${SDL2_LIBRARY} ${EXTRA_LIBS}) + ENDIF() +ENDIF() +IF(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) + MESSAGE(FATAL_ERROR "Failed to enabled required SDL2 backend") +ENDIF() + # Optionally enable the Wave Writer backend OPTION(ALSOFT_BACKEND_WAVE "Enable Wave Writer backend" ON) IF(ALSOFT_BACKEND_WAVE) @@ -1206,7 +1224,9 @@ IF(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL) add_subdirectory(utils/alsoft-config) ENDIF() IF(ALSOFT_EXAMPLES) - FIND_PACKAGE(SDL2) + IF(NOT SDL2_FOUND) + FIND_PACKAGE(SDL2) + ENDIF() IF(SDL2_FOUND) FIND_PACKAGE(SDL_sound) FIND_PACKAGE(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) diff --git a/config.h.in b/config.h.in index 2abeedfd..345e8408 100644 --- a/config.h.in +++ b/config.h.in @@ -77,6 +77,9 @@ /* Define if we have the Wave Writer backend */ #cmakedefine HAVE_WAVE +/* Define if we have the SDL2 backend */ +#cmakedefine HAVE_SDL2 + /* Define if we have the stat function */ #cmakedefine HAVE_STAT |