From ba4d76cd940d22a5a945f4663ad6c17970b95e5f Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 7 Jul 2013 02:03:07 -0400 Subject: [PATCH] Added an SDL2 OpenBSD sndio(7) audio target. --- CMakeLists.txt | 2 + cmake/sdlchecks.cmake | 31 +++ configure.in | 47 +++++ include/SDL_config.h.cmake | 2 + include/SDL_config.h.in | 2 + src/audio/SDL_audio.c | 4 + src/audio/sndio/SDL_sndioaudio.c | 327 +++++++++++++++++++++++++++++++ src/audio/sndio/SDL_sndioaudio.h | 45 +++++ 8 files changed, 460 insertions(+) create mode 100644 src/audio/sndio/SDL_sndioaudio.c create mode 100644 src/audio/sndio/SDL_sndioaudio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf3f34a4c..a649fb7a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,7 @@ dep_option(PULSEAUDIO_SHARED "Dynamically load PulseAudio support" ON "PULSEAU set_option(ARTS "Support the Analog Real Time Synthesizer" ${UNIX_SYS}) dep_option(ARTS_SHARED "Dynamically load aRts audio support" ON "ARTS" OFF) set_option(NAS "Support the NAS audio API" ${UNIX_SYS}) +set_option(SNDIO "Support the sndio audio API" ${UNIX_SYS}) set_option(RPATH "Use an rpath when linking SDL" ${UNIX_SYS}) set_option(CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" OFF) set_option(INPUT_TSLIB "Use the Touchscreen library for input" ${UNIX_SYS}) @@ -621,6 +622,7 @@ if(UNIX AND NOT APPLE) CheckESD() CheckARTS() CheckNAS() + CheckSNDIO() CheckFusionSound() endif(SDL_AUDIO) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 40936b996..b2a2ffb2a 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -225,6 +225,37 @@ macro(CheckNAS) endif(NAS) endmacro(CheckNAS) +# Requires: +# - n/a +# Optional: +# - SNDIO_SHARED opt +# - HAVE_DLOPEN opt +macro(CheckSNDIO) + if(SNDIO) + # TODO: set include paths properly, so the sndio headers are found + check_include_file(sndio.h HAVE_SNDIO_H) + find_library(D_SNDIO_LIB audio) + if(HAVE_SNDIO_H AND D_SNDIO_LIB) + set(HAVE_SNDIO TRUE) + file(GLOB SNDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/sndio/*.c) + set(SOURCE_FILES ${SOURCE_FILES} ${SNDIO_SOURCES}) + set(SDL_AUDIO_DRIVER_SNDIO 1) + if(SNDIO_SHARED) + if(NOT HAVE_DLOPEN) + message_warn("You must have SDL_LoadObject() support for dynamic sndio loading") + else() + get_filename_component(F_SNDIO_LIB ${D_SNDIO_LIB} NAME) + set(SDL_AUDIO_DRIVER_SNDIO_DYNAMIC "\"${F_SNDIO_LIB}\"") + set(HAVE_SNDIO_SHARED TRUE) + endif(NOT HAVE_DLOPEN) + else(SNDIO_SHARED) + list(APPEND EXTRA_LIBS ${D_SNDIO_LIB}) + endif(SNDIO_SHARED) + set(HAVE_SDL_AUDIO TRUE) + endif(HAVE_SNDIO_H AND D_SNDIO_LIB) + endif(SNDIO) +endmacro(CheckSNDIO) + # Requires: # - PkgCheckModules # Optional: diff --git a/configure.in b/configure.in index 2889c5106..f7a002440 100644 --- a/configure.in +++ b/configure.in @@ -910,6 +910,52 @@ AC_HELP_STRING([--enable-nas-shared], [dynamically load NAS audio support [[defa fi } +dnl See if the sndio audio interface is supported +CheckSNDIO() +{ + AC_ARG_ENABLE(sndio, +AC_HELP_STRING([--enable-sndio], [support the sndio audio API [[default=yes]]]), + , enable_sndio=yes) + if test x$enable_audio = xyes -a x$enable_sndio = xyes; then + AC_CHECK_HEADER(sndio.h, have_sndio_hdr=yes) + AC_CHECK_LIB(sndio, sio_open, have_sndio_lib=yes) + + AC_MSG_CHECKING(for sndio audio support) + have_sndio=no + + if test x$have_sndio_hdr = xyes -a x$have_sndio_lib = xyes; then + have_sndio=yes + SNDIO_LIBS="-lsndio" + fi + + AC_MSG_RESULT($have_sndio) + + if test x$have_sndio = xyes; then + AC_ARG_ENABLE(sndio-shared, +AC_HELP_STRING([--enable-sndio-shared], [dynamically load sndio audio support [[default=yes]]]), + , enable_sndio_shared=yes) + sndio_lib=[`find_lib "libsndio.so.*" "$SNDIO_LIBS" | sed 's/.*\/\(.*\)/\1/; q'`] + + if test x$have_loadso != xyes && \ + test x$enable_sndio_shared = xyes; then + AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic sndio loading]) + fi + if test x$have_loadso = xyes && \ + test x$enable_sndio_shared = xyes && test x$sndio_lib != x; then + echo "-- dynamic libsndio -> $sndio_lib" + AC_DEFINE_UNQUOTED(SDL_AUDIO_DRIVER_SNDIO_DYNAMIC, "$sndio_lib", [ ]) + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS $SNDIO_LIBS" + fi + + AC_DEFINE(SDL_AUDIO_DRIVER_SNDIO, 1, [ ]) + SOURCES="$SOURCES $srcdir/src/audio/sndio/*.c" + EXTRA_CFLAGS="$EXTRA_CFLAGS $SNDIO_CFLAGS" + have_audio=yes + fi + fi +} + dnl rcg07142001 See if the user wants the disk writer audio driver... CheckDiskAudio() { @@ -2262,6 +2308,7 @@ case "$host" in CheckARTSC CheckESD CheckNAS + CheckSNDIO CheckX11 CheckDirectFB CheckFusionSound diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake index c212bae9e..8423be702 100644 --- a/include/SDL_config.h.cmake +++ b/include/SDL_config.h.cmake @@ -195,6 +195,8 @@ #cmakedefine SDL_AUDIO_DRIVER_ESD_DYNAMIC @SDL_AUDIO_DRIVER_ESD_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_NAS @SDL_AUDIO_DRIVER_NAS@ #cmakedefine SDL_AUDIO_DRIVER_NAS_DYNAMIC @SDL_AUDIO_DRIVER_NAS_DYNAMIC@ +#cmakedefine SDL_AUDIO_DRIVER_SNDIO @SDL_AUDIO_DRIVER_SNDIO@ +#cmakedefine SDL_AUDIO_DRIVER_SNDIO_DYNAMIC @SDL_AUDIO_DRIVER_SNDIO_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_OSS @SDL_AUDIO_DRIVER_OSS@ #cmakedefine SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H @SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H@ #cmakedefine SDL_AUDIO_DRIVER_PAUDIO @SDL_AUDIO_DRIVER_PAUDIO@ diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 5d5252fe6..7c5d9bf97 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -199,6 +199,8 @@ #undef SDL_AUDIO_DRIVER_ESD_DYNAMIC #undef SDL_AUDIO_DRIVER_NAS #undef SDL_AUDIO_DRIVER_NAS_DYNAMIC +#undef SDL_AUDIO_DRIVER_SNDIO +#undef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC #undef SDL_AUDIO_DRIVER_OSS #undef SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H #undef SDL_AUDIO_DRIVER_PAUDIO diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 08467f43a..9f22d09c1 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -67,6 +67,7 @@ extern AudioBootStrap NDSAUD_bootstrap; extern AudioBootStrap FUSIONSOUND_bootstrap; extern AudioBootStrap ANDROIDAUD_bootstrap; extern AudioBootStrap PSPAUD_bootstrap; +extern AudioBootStrap SNDIO_bootstrap; /* Available audio drivers */ static const AudioBootStrap *const bootstrap[] = { @@ -76,6 +77,9 @@ static const AudioBootStrap *const bootstrap[] = { #if SDL_AUDIO_DRIVER_ALSA &ALSA_bootstrap, #endif +#if SDL_AUDIO_DRIVER_SNDIO + &SNDIO_bootstrap, +#endif #if SDL_AUDIO_DRIVER_BSD &BSD_AUDIO_bootstrap, #endif diff --git a/src/audio/sndio/SDL_sndioaudio.c b/src/audio/sndio/SDL_sndioaudio.c new file mode 100644 index 000000000..ce0b61b7c --- /dev/null +++ b/src/audio/sndio/SDL_sndioaudio.c @@ -0,0 +1,327 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_config.h" + +#if SDL_AUDIO_DRIVER_SNDIO + +/* OpenBSD sndio target */ + +#if HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#include + +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_sndioaudio.h" + +#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC +#include "SDL_loadso.h" +#endif + +static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int); +static void (*SNDIO_sio_close)(struct sio_hdl *); +static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *); +static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *); +static int (*SNDIO_sio_start)(struct sio_hdl *); +static int (*SNDIO_sio_stop)(struct sio_hdl *); +static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t); +static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t); +static void (*SNDIO_sio_initpar)(struct sio_par *); + +#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC +static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC; +static void *sndio_handle = NULL; + +static int +load_sndio_sym(const char *fn, void **addr) +{ + *addr = SDL_LoadFunction(sndio_handle, fn); + if (*addr == NULL) { + /* Don't call SDL_SetError(): SDL_LoadFunction already did. */ + return 0; + } + + return 1; +} + +/* cast funcs to char* first, to please GCC's strict aliasing rules. */ +#define SDL_SNDIO_SYM(x) \ + if (!load_sndio_sym(#x, (void **) (char *) &SNDIO_##x)) return -1 +#else +#define SDL_SNDIO_SYM(x) SNDIO_##x = x +#endif + +static int +load_sndio_syms(void) +{ + SDL_SNDIO_SYM(sio_open); + SDL_SNDIO_SYM(sio_close); + SDL_SNDIO_SYM(sio_setpar); + SDL_SNDIO_SYM(sio_getpar); + SDL_SNDIO_SYM(sio_start); + SDL_SNDIO_SYM(sio_stop); + SDL_SNDIO_SYM(sio_read); + SDL_SNDIO_SYM(sio_write); + SDL_SNDIO_SYM(sio_initpar); + return 0; +} + +#undef SDL_SNDIO_SYM + +#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC + +static void +UnloadSNDIOLibrary(void) +{ + if (sndio_handle != NULL) { + SDL_UnloadObject(sndio_handle); + sndio_handle = NULL; + } +} + +static int +LoadSNDIOLibrary(void) +{ + int retval = 0; + if (sndio_handle == NULL) { + sndio_handle = SDL_LoadObject(sndio_library); + if (sndio_handle == NULL) { + retval = -1; + /* Don't call SDL_SetError(): SDL_LoadObject already did. */ + } else { + retval = load_sndio_syms(); + if (retval < 0) { + UnloadSNDIOLibrary(); + } + } + } + return retval; +} + +#else + +static void +UnloadSNDIOLibrary(void) +{ +} + +static int +LoadSNDIOLibrary(void) +{ + load_sndio_syms(); + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */ + + + + +static void +SNDIO_WaitDevice(_THIS) +{ + /* no-op; sio_write() blocks if necessary. */ +} + +static void +SNDIO_PlayDevice(_THIS) +{ + const int written = sio_write(this->hidden->dev, this->hidden->mixbuf, this->hidden->mixlen); + + /* If we couldn't write, assume fatal error for now */ + if ( written == 0 ) { + this->enabled = 0; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 * +SNDIO_GetDeviceBuf(_THIS) +{ + return this->hidden->mixbuf; +} + +static void +SNDIO_WaitDone(_THIS) +{ + sio_stop(this->hidden->dev); +} + +static void +SNDIO_CloseDevice(_THIS) +{ + if (this->hidden != NULL) { + if (this->hidden->mixbuf != NULL) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } + if ( this->hidden->dev != NULL ) { + sio_close(this->hidden->dev); + this->hidden->dev = NULL; + } + SDL_free(this->hidden); + this->hidden = NULL; + } +} + +static int +SNDIO_OpenDevice(_THIS, const char *devname, int iscapture) +{ + SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); + struct sio_par par; + int status; + + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc(sizeof(*this->hidden)); + if (this->hidden == NULL) { + return SDL_OutOfMemory(); + } + SDL_memset(this->hidden, 0, sizeof(*this->hidden)); + + this->hidden->mixlen = this->spec.size; + + /* !!! FIXME: SIO_DEVANY can be a specific device... */ + if ((this->hidden->dev = sio_open(0 /*SIO_DEVANY*/, SIO_PLAY, 0)) == NULL) { + SNDIO_CloseDevice(this); + return SDL_SetError("sio_open() failed"); + } + + sio_initpar(&par); + + par.rate = this->spec.freq; + par.pchan = this->spec.channels; + par.round = this->spec.samples; + par.appbufsz = par.round * 2; + + /* Try for a closest match on audio format */ + status = -1; + while (test_format && (status < 0)) { + if (!SDL_AUDIO_ISFLOAT(test_format)) { + par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0; + par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0; + par.bits = SDL_AUDIO_BITSIZE(test_format); + + if (sio_setpar(this->hidden->dev, &par) == 1) { + status = 0; + break; + } + } + test_format = SDL_NextAudioFormat(); + } + + if (status < 0) { + SNDIO_CloseDevice(this); + return SDL_SetError("sndio: Couldn't find any hardware audio formats"); + } + + if (sio_getpar(this->hidden->dev, &par) == 0) { + SNDIO_CloseDevice(this); + return SDL_SetError("sio_getpar() failed"); + } + + if ((par.bits == 32) && (par.sig) && (par.le)) + this->spec.format = AUDIO_S32LSB; + else if ((par.bits == 32) && (par.sig) && (!par.le)) + this->spec.format = AUDIO_S32MSB; + else if ((par.bits == 16) && (par.sig) && (par.le)) + this->spec.format = AUDIO_S16LSB; + else if ((par.bits == 16) && (par.sig) && (!par.le)) + this->spec.format = AUDIO_S16MSB; + else if ((par.bits == 16) && (!par.sig) && (par.le)) + this->spec.format = AUDIO_U16LSB; + else if ((par.bits == 16) && (!par.sig) && (!par.le)) + this->spec.format = AUDIO_U16MSB; + else if ((par.bits == 8) && (par.sig)) + this->spec.format = AUDIO_S8; + else if ((par.bits == 8) && (!par.sig)) + this->spec.format = AUDIO_U8; + else { + SNDIO_CloseDevice(this); + return SDL_SetError("sndio: Got unsupported hardware audio format."); + } + + this->spec.freq = par.rate; + this->spec.channels = par.pchan; + this->spec.samples = par.round; + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(&this->spec); + + /* Allocate mixing buffer */ + this->hidden->mixlen = this->spec.size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if (this->hidden->mixbuf == NULL) { + SNDIO_CloseDevice(this); + return SDL_OutOfMemory(); + } + SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen); + + if (!sio_start(this->hidden->dev)) { + return SDL_SetError("sio_start() failed"); + } + + /* We're ready to rock and roll. :-) */ + return 0; +} + +static void +SNDIO_Deinitialize(void) +{ + UnloadSNDIOLibrary(); +} + +static int +SNDIO_Init(SDL_AudioDriverImpl * impl) +{ + if (LoadSNDIOLibrary() < 0) { + return 0; + } + + /* Set the function pointers */ + impl->OpenDevice = SNDIO_OpenDevice; + impl->WaitDevice = SNDIO_WaitDevice; + impl->PlayDevice = SNDIO_PlayDevice; + impl->GetDeviceBuf = SNDIO_GetDeviceBuf; + impl->WaitDone = SNDIO_WaitDone; + impl->CloseDevice = SNDIO_CloseDevice; + impl->Deinitialize = SNDIO_Deinitialize; + impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: sndio can handle multiple devices. */ + + return 1; /* this audio target is available. */ +} + +AudioBootStrap SNDIO_bootstrap = { + "sndio", "OpenBSD sndio", SNDIO_Init, 0 +}; + +#endif /* SDL_AUDIO_DRIVER_SNDIO */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/audio/sndio/SDL_sndioaudio.h b/src/audio/sndio/SDL_sndioaudio.h new file mode 100644 index 000000000..94e1542e4 --- /dev/null +++ b/src/audio/sndio/SDL_sndioaudio.h @@ -0,0 +1,45 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2013 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#ifndef _SDL_sndioaudio_h +#define _SDL_sndioaudio_h + +#include + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData +{ + /* The audio device handle */ + struct sio_hdl *dev; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; +}; + +#endif /* _SDL_sndioaudio_h */ + +/* vi: set ts=4 sw=4 expandtab: */