From 240fa9286f4d265127d60adc3d7bb9b8e161f57f Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 8 Jul 2012 07:20:50 -0400 Subject: [PATCH] Added OpenBSD "sndio" audio target. Thanks to Brad Smith and the OpenBSD team for the patch! --- configure.in | 31 +++- include/SDL_config.h.in | 1 + src/audio/SDL_audio.c | 3 + src/audio/SDL_sysaudio.h | 3 + src/audio/sndio/SDL_sndioaudio.c | 286 +++++++++++++++++++++++++++++++ src/audio/sndio/SDL_sndioaudio.h | 50 ++++++ 6 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 src/audio/sndio/SDL_sndioaudio.c create mode 100644 src/audio/sndio/SDL_sndioaudio.h diff --git a/configure.in b/configure.in index 08c8e1e97..33491c304 100644 --- a/configure.in +++ b/configure.in @@ -486,6 +486,34 @@ AC_HELP_STRING([--enable-esd-shared], [dynamically load ESD audio support [[defa fi } +dnl Find Sndio +CheckSndio() +{ + AC_ARG_ENABLE(sndio, +AC_HELP_STRING([--enable-sndio], [support the sndio audio API [[default=yes]]]), + , enable_sndioaudio=yes) + if test x$enable_audio = xyes -a x$enable_sndioaudio = 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 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_nas) + + if test x$have_sndio = xyes; then + AC_DEFINE(SDL_AUDIO_DRIVER_SNDIO) + SOURCES="$SOURCES $srcdir/src/audio/sndio/*.c" + EXTRA_LDFLAGS="$EXTRA_LDFLAGS $SNDIO_LIBS" + have_audio=yes + fi +} + dnl Find PulseAudio CheckPulseAudio() { @@ -2358,6 +2386,7 @@ case "$host" in CheckALSA CheckARTSC CheckESD + CheckSndio CheckPulseAudio CheckNAS CheckX11 @@ -2390,7 +2419,7 @@ case "$host" in SOURCES="$SOURCES $srcdir/src/audio/sun/*.c" have_audio=yes ;; - netbsd|openbsd) + netbsd) AC_DEFINE(SDL_AUDIO_DRIVER_BSD) SOURCES="$SOURCES $srcdir/src/audio/bsd/*.c" have_audio=yes diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index 8bb1773c0..1d9730346 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -182,6 +182,7 @@ #undef SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H #undef SDL_AUDIO_DRIVER_PAUD #undef SDL_AUDIO_DRIVER_QNXNTO +#undef SDL_AUDIO_DRIVER_SNDIO #undef SDL_AUDIO_DRIVER_SNDMGR #undef SDL_AUDIO_DRIVER_SUNAUDIO #undef SDL_AUDIO_DRIVER_WAVEOUT diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index beb26e0b4..b59033a72 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -42,6 +42,9 @@ static AudioBootStrap *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/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 74ac21df0..fa29a2bbd 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -102,6 +102,9 @@ typedef struct AudioBootStrap { SDL_AudioDevice *(*create)(int devindex); } AudioBootStrap; +#if SDL_AUDIO_DRIVER_SNDIO +extern AudioBootStrap SNDIO_bootstrap; +#endif #if SDL_AUDIO_DRIVER_BSD extern AudioBootStrap 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..5894e90b1 --- /dev/null +++ b/src/audio/sndio/SDL_sndioaudio.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2008 Jacob Meuser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#ifdef HAVE_SIGNAL_H +#include +#endif +#include + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_sndioaudio.h" + +/* The tag name used by sndio audio */ +#define SNDIO_DRIVER_NAME "sndio" + +/* Audio driver functions */ +static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void SNDIO_WaitAudio(_THIS); +static void SNDIO_PlayAudio(_THIS); +static Uint8 *SNDIO_GetAudioBuf(_THIS); +static void SNDIO_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + struct sio_hdl *this_hdl; + int available = 0; + + if ( (this_hdl = sio_open(NULL, SIO_PLAY, 0)) != NULL ) { + sio_close(this_hdl); + available = 1; + } + + return available; +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = SNDIO_OpenAudio; + this->WaitAudio = SNDIO_WaitAudio; + this->PlayAudio = SNDIO_PlayAudio; + this->GetAudioBuf = SNDIO_GetAudioBuf; + this->CloseAudio = SNDIO_CloseAudio; + + this->free = Audio_DeleteDevice; + + hdl = NULL; + + return this; +} + +AudioBootStrap SNDIO_bootstrap = { + SNDIO_DRIVER_NAME, "sndio", + Audio_Available, Audio_CreateDevice +}; + + + +/* This function waits until it is possible to write a full sound buffer */ +static void SNDIO_WaitAudio(_THIS) +{ + /* Check to see if the thread-parent process is still alive */ + { static int cnt = 0; + /* Note that this only works with thread implementations + that use a different process id for each thread. + */ + if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ + if ( kill(parent, 0) < 0 ) { + this->enabled = 0; + } + } + } +} + +static void SNDIO_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data */ + written = sio_write(hdl, mixbuf, 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_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void SNDIO_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( hdl != NULL ) { + sio_close(hdl); + hdl = NULL; + } +} + +static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + struct sio_par par, reqpar; + int newrate; + + mixbuf = NULL; + + if ((hdl = sio_open(NULL, SIO_PLAY, 0)) == NULL) { + SDL_SetError("sio_open() failed"); + return(-1); + } + + sio_initpar(&par); + + switch (spec->format) { + case AUDIO_S16LSB: + par.bits = 16; + par.sig = 1; + par.le = 1; + break; + case AUDIO_S16MSB: + par.bits = 16; + par.sig = 1; + par.le = 0; + break; + case AUDIO_S8: + par.bits = 8; + par.sig = 1; + break; + case AUDIO_U16LSB: + par.bits = 16; + par.sig = 0; + par.le = 1; + break; + case AUDIO_U16MSB: + par.bits = 16; + par.sig = 0; + par.le = 0; + break; + case AUDIO_U8: + par.bits = 8; + par.sig = 0; + break; + default: + SDL_SetError("SNDIO unknown format"); + return(-1); + } + + par.rate = spec->freq; + par.pchan = spec->channels; + par.round = spec->samples; + par.appbufsz = par.round * 2; + + reqpar = par; + + if (sio_setpar(hdl, &par) == 0) { + SDL_SetError("sio_setpar() failed"); + return(-1); + } + + if (sio_getpar(hdl, &par) == 0) { + SDL_SetError("sio_getpar() failed"); + return(-1); + } + + /* if wanted rate not found, find a multiple/factor */ + if (par.rate != spec->freq) { + newrate = par.rate; + if ((newrate > spec->freq && newrate % spec->freq != 0) || + (newrate < spec->freq && spec->freq % newrate != 0)) { + if ((spec->freq < 44100 && 44100 % spec->freq == 0) || + (spec->freq > 44100 && spec->freq % 44100 == 0)) { + newrate = 44100; + } + } + /* only change sample rate */ + par = reqpar; + par.rate = newrate; + /* keep same latency */ + par.round = spec->samples * par.rate / reqpar.rate; + par.appbufsz = par.round * 2; + if (sio_setpar(hdl, &par) == 0) { + SDL_SetError("sio_setpar() failed"); + return(-1); + } + } + + if (sio_getpar(hdl, &par) == 0) { + SDL_SetError("sio_getpar() failed"); + return(-1); + } + + if (par.bits == 16) { + if (par.sig && par.le) { + spec->format = AUDIO_S16LSB; + } else if (par.sig && !par.le) { + spec->format = AUDIO_S16MSB; + } else if (!par.sig && par.le) { + spec->format = AUDIO_U16LSB; + } else + spec->format = AUDIO_U16MSB; + } else if (par.bits == 8) { + spec->format = par.sig ? AUDIO_S8 : AUDIO_U8; + } else { + SDL_SetError("SNDIO couldn't configure a suitable format"); + return(-1); + } + + spec->freq = par.rate; + spec->channels = par.pchan; + spec->samples = par.round; + + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + if ( sio_start(hdl) == 0 ) { + SDL_SetError("sio_start() failed"); + return(-1); + } + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/sndio/SDL_sndioaudio.h b/src/audio/sndio/SDL_sndioaudio.h new file mode 100644 index 000000000..32c566ed9 --- /dev/null +++ b/src/audio/sndio/SDL_sndioaudio.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008 Jacob Meuser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "SDL_config.h" + +#ifndef _SDL_sndioaudio_h +#define _SDL_sndioaudio_h + +#include + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The stream descriptor for the audio device */ + struct sio_hdl *hdl; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + +}; + +/* Old variable names */ +#define stream (this->hidden->stream) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define hdl (this->hidden->hdl) + +#endif /* _SDL_sndioaudio_h */ +