From 72d17755d35122e542a68389bde42d27c8622f37 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 26 May 2001 16:58:37 +0000 Subject: [PATCH] Added native OpenBSD audio driver (thanks vedge!) --- README.OpenBSD | 81 ++++++ configure.in | 24 +- docs.html | 1 + src/audio/Makefile.am | 2 +- src/audio/SDL_audio.c | 3 + src/audio/SDL_sysaudio.h | 3 + src/audio/openbsd/Makefile.am | 9 + src/audio/openbsd/SDL_openbsdaudio.c | 408 +++++++++++++++++++++++++++ src/audio/openbsd/SDL_openbsdaudio.h | 57 ++++ 9 files changed, 581 insertions(+), 7 deletions(-) create mode 100644 README.OpenBSD create mode 100644 src/audio/openbsd/Makefile.am create mode 100644 src/audio/openbsd/SDL_openbsdaudio.c create mode 100644 src/audio/openbsd/SDL_openbsdaudio.h diff --git a/README.OpenBSD b/README.OpenBSD new file mode 100644 index 000000000..cf224635a --- /dev/null +++ b/README.OpenBSD @@ -0,0 +1,81 @@ +============================================================= +Using the Simple DirectMedia Layer with OpenBSD 2.9 and later +============================================================= + +Author: +Peter Valchev + +======= +Install +======= +There is an OpenBSD port for SDL and various other tools. Using +this port is recommended, since it contains all the needed patches +and configure arguments nicely organized in several FLAVORS you +can pick from. If you're unfamiliar with the OpenBSD ports system +please visit: +http://www.openbsd.org/ports.html +http://www.openbsd.org/anoncvs.html + +You should have a precompiled binary package on your OpenBSD 2.9 +or later CD-ROM, which you can easily add with pkg_add(1). + +If for whatever reason you're unable to take the advantage of the +created port, or you want to stick with the newest fresh SDL from +the current CVS repository, here are some hints of what has left +unresolved and needs to be disabled/changed. + +To build SDL with OSS + esound: + +1. ./configure --disable-joystick \ + --disable-pthread-sem \ + --disable-nasm \ + --enable-esd \ + --enable-oss \ + --disable-arts \ + --disable-video-opengl + +OR + +To build SDL with native audio + esound (remove --disable-esd otherwise): +1. ./configure --disable-joystick \ + --disable-pthread-sem \ + --disable-nasm \ + --enable-openbsdaudio \ + --enable-esd \ + --disable-arts \ + --disable-video-opengl + +If you need arts or GL, omit the responding --disable lines. In the +port, they're disabled by default, you need to specify flavors to +make use of them. + +2. gmake # Yeah, GNU make is required, install the package +3. sudo gmake install + +========= +Todo List +========= + +Feedback is always welcome with anything you may find useful and +you can suggest. +For now, the following things have to be worked on: + +1. The native audio should be widely tested and will eventually +become the default. At that moment I have not received any +feedback which should explain its current condition. + +2. There are occurences of dynamic loading of drivers in the OpenGL +code section, and our a.out architectures need an underscore prefixed +to symbol names. Some solution to that may be a good thing. I can +fix it theoretically, but unfortunately I do not have the needed +hardware/configuration to test it. If you bump into that, email me. + +=================== +Contact Information +=================== +Everybody is welcome to give me any suggestions and to report bugs +(as well as fixing them ;-) You can contact me by email: +Peter Valchev + +$Id$ + diff --git a/configure.in b/configure.in index 46534a54a..ef3f2278a 100644 --- a/configure.in +++ b/configure.in @@ -278,6 +278,19 @@ CheckALSA() fi } +dnl Check whether we want to use OpenBSD native audio or not +CheckOPENBSDAUDIO() +{ + AC_ARG_ENABLE(openbsdaudio, +[ --enable-openbsdaudio OpenBSD native audio support [default=no]], + , enable_openbsdaudio=no) + if test x$enable_audio = xyes -a x$enable_openbsdaudio = xyes; then + SYSTEM_LIBS="$SYSTEM_LIBS $ESD_LIBS" + AUDIO_SUBDIRS="$AUDIO_SUBDIRS openbsd" + AUDIO_DRIVERS="$AUDIO_DRIVERS openbsd/libaudio_openbsd.la" + fi +} + dnl Find the ESD includes and libraries CheckESD() { @@ -1275,7 +1288,6 @@ case "$target" in ;; *-*-openbsd*) ARCH=openbsd - CFLAGS="$CFLAGS -Dunix" CheckDummyVideo CheckNASM CheckOSS @@ -1289,10 +1301,7 @@ case "$target" in # Set up files for the main() stub COPY_ARCH_SRC(src/main, linux, SDL_main.c) # Set up files for the audio library - if test x$enable_audio = xyes; then - AUDIO_SUBDIRS="$AUDIO_SUBDIRS sun" - AUDIO_DRIVERS="$AUDIO_DRIVERS sun/libaudio_sun.la" - fi + CheckOPENBSDAUDIO # Set up files for the joystick library # (No joystick support yet) if test x$enable_joystick = xyes; then @@ -1322,6 +1331,8 @@ case "$target" in if test x$enable_timers = xyes; then COPY_ARCH_SRC(src/timer, linux, SDL_systimer.c) fi + # OpenBSD does not define "unix" + CFLAGS="$CFLAGS -Dunix" ;; *-*-sysv5*) ARCH=sysv5 @@ -1882,7 +1893,7 @@ if test $ARCH = solaris; then fi if test $ARCH = openbsd; then - SDL_RLD_FLAGS="-Wl,-R\${exec_prefix}/lib -Wl,-R\${X11BASE}/lib" + SDL_RLD_FLAGS="-L${X11BASE}/lib -Wl,-rpath,\${exec_prefix}/lib -Wl,-rpath,${X11BASE}/lib" fi dnl Output the video drivers we use @@ -1947,6 +1958,7 @@ src/audio/esd/Makefile src/audio/macrom/Makefile src/audio/nas/Makefile src/audio/nto/Makefile +src/audio/openbsd/Makefile src/audio/paudio/Makefile src/audio/sun/Makefile src/audio/ums/Makefile diff --git a/docs.html b/docs.html index 89ede8de5..ea26c74e3 100644 --- a/docs.html +++ b/docs.html @@ -16,6 +16,7 @@

Major changes since SDL 1.0.0:

    +
  • 1.2.1: Added native OpenBSD audio driver (thanks vedge!)
  • 1.2.1: Added detection of Open Sound System on Solaris x86
  • 1.2.1: Added initial support for Nano-X (thanks Hsieh-Fu!)
  • 1.2.1: Fixed endian detection on IA64 architectures (thanks Bill!) diff --git a/src/audio/Makefile.am b/src/audio/Makefile.am index d1ad57c59..b182d98dc 100644 --- a/src/audio/Makefile.am +++ b/src/audio/Makefile.am @@ -6,7 +6,7 @@ noinst_LTLIBRARIES = libaudio.la # Define which subdirectories need to be built SUBDIRS = @AUDIO_SUBDIRS@ DIST_SUBDIRS = alsa arts baudio dma dmedia dsp esd macrom nas nto \ - paudio sun ums windib windx5 + openbsd paudio sun ums windib windx5 DRIVERS = @AUDIO_DRIVERS@ diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 0e3fb1192..ebb51f524 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -47,6 +47,9 @@ static AudioBootStrap *bootstrap[] = { #ifdef ALSA_SUPPORT &ALSA_bootstrap, #endif +#ifdef __OpenBSD__ + &OBSD_bootstrap; +#endif #if (defined(unix) && !defined(__CYGWIN32__)) && \ !defined(OSS_SUPPORT) && !defined(ALSA_SUPPORT) &AUDIO_bootstrap, diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 43f237c1c..7f396daaf 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -106,6 +106,9 @@ extern AudioBootStrap DMA_bootstrap; #ifdef ALSA_SUPPORT extern AudioBootStrap ALSA_bootstrap; #endif +#ifdef __OpenBSD__ +extern AudioBootStrap OBSD_bootstrap; +#endif #if (defined(unix) && !defined(__CYGWIN32__)) && \ !defined(OSS_SUPPORT) && !defined(ALSA_SUPPORT) extern AudioBootStrap AUDIO_bootstrap; diff --git a/src/audio/openbsd/Makefile.am b/src/audio/openbsd/Makefile.am new file mode 100644 index 000000000..a63b6ecd8 --- /dev/null +++ b/src/audio/openbsd/Makefile.am @@ -0,0 +1,9 @@ + +## Makefile.am for SDL using the native OpenBSD sound driver + +noinst_LTLIBRARIES = libaudio_openbsd.la +libaudio_openbsd_la_SOURCES = $(SRCS) + +# The SDL audio driver sources +SRCS = SDL_openbsdaudio.c \ + SDL_openbsdaudio.h diff --git a/src/audio/openbsd/SDL_openbsdaudio.c b/src/audio/openbsd/SDL_openbsdaudio.c new file mode 100644 index 000000000..eef205134 --- /dev/null +++ b/src/audio/openbsd/SDL_openbsdaudio.c @@ -0,0 +1,408 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + 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 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +/* + * Driver for native OpenBSD audio(4). + * vedge@vedge.com.ar. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SDL_audio.h" +#include "SDL_error.h" +#include "SDL_audiomem.h" +#include "SDL_audio_c.h" +#include "SDL_timer.h" +#include "SDL_audiodev_c.h" +#include "SDL_openbsdaudio.h" + +/* The tag name used by OpenBSD audio */ +#define OBSD_DRIVER_NAME "openbsd" + +/* Open the audio device for playback, and don't block if busy */ +/* #define USE_BLOCKING_WRITES */ + +/* Use timer for synchronization */ +/* #define USE_TIMER_SYNC */ + +/* #define DEBUG_AUDIO */ +/* #define DEBUG_AUDIO_STREAM */ + +#ifdef USE_BLOCKING_WRITES +#define OPEN_FLAGS O_WRONLY +#else +#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) +#endif + +/* Audio driver functions */ +static void OBSD_WaitAudio(_THIS); +static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void OBSD_PlayAudio(_THIS); +static Uint8 *OBSD_GetAudioBuf(_THIS); +static void OBSD_CloseAudio(_THIS); + +#ifdef DEBUG_AUDIO +static void OBSD_Status(_THIS); +#endif + +/* Audio driver bootstrap functions */ + +static int +Audio_Available(void) +{ + int fd; + int available; + + available = 0; + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); + if(fd >= 0) { + available = 1; + close(fd); + } + return(available); +} + +static void +Audio_DeleteDevice(SDL_AudioDevice *device) +{ + free(device->hidden); + free(device); +} + +static SDL_AudioDevice +*Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice*)malloc(sizeof(SDL_AudioDevice)); + if(this) { + memset(this, 0, (sizeof *this)); + this->hidden = + (struct SDL_PrivateAudioData*)malloc((sizeof *this->hidden)); + } + if((this == NULL) || (this->hidden == NULL)) { + SDL_OutOfMemory(); + if(this) free(this); + return(0); + } + memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = OBSD_OpenAudio; + this->WaitAudio = OBSD_WaitAudio; + this->PlayAudio = OBSD_PlayAudio; + this->GetAudioBuf = OBSD_GetAudioBuf; + this->CloseAudio = OBSD_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap OBSD_bootstrap = { + OBSD_DRIVER_NAME, "Native OpenBSD audio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void +OBSD_WaitAudio(_THIS) +{ +#ifndef USE_BLOCKING_WRITES + fd_set fdset; + + /* 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; + } + } + +#ifdef USE_TIMER_SYNC + /* See if we need to use timed audio synchronization */ + if(frame_ticks) + { + /* Use timer for general audio synchronization */ + Sint32 ticks; + + ticks = ((Sint32)(next_frame - SDL_GetTicks())) - FUDGE_TICKS; + if(ticks > 0) + SDL_Delay(ticks); + } + else +#endif /* USE_TIMER_SYNC */ + { + /* Use select() for audio synchronization */ + struct timeval timeout; + FD_ZERO(&fdset); + FD_SET(audio_fd, &fdset); + timeout.tv_sec = 10; + timeout.tv_usec = 0; + +#if defined(DEBUG_AUDIO_STREAM) && defined(DEBUG_AUDIO_STREAM) + OBSD_Status(this); +#endif + if(select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0) + { + const char *message = + "Audio timeout - buggy audio driver? (disabled)"; + fprintf(stderr, "SDL: %s\n", message); + this->enabled = 0; + audio_fd = -1; + } + } +#endif /* !USE_BLOCKING_WRITES */ + +} + +static void +OBSD_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data, checking for EAGAIN on broken audio drivers */ + do + { + written = write(audio_fd, mixbuf, mixlen); + if((written < 0) && ((errno == 0) || (errno == EAGAIN))) + SDL_Delay(1); + } + while((written < 0) && + ((errno == 0) || (errno == EAGAIN) || (errno == EINTR))); + +#ifdef USE_TIMER_SYNC + if(frame_ticks) + next_frame += frame_ticks; +#endif + + /* If we couldn't write, assume fatal error for now */ + if(written < 0) + this->enabled = 0; + +#ifdef DEBUG_AUDIO_STREAM + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 +*OBSD_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void +OBSD_CloseAudio(_THIS) +{ + if(mixbuf != NULL) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if(audio_fd >= 0) { + close(audio_fd); + audio_fd = -1; + } +} + +#ifdef DEBUG_AUDIO +void +OBSD_Status(_THIS) +{ + audio_info_t info; + + if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + fprintf(stderr,"AUDIO_GETINFO failed.\n"); + return; + } + + fprintf(stderr," +[play/record info] +buffer size : %d bytes +sample rate : %i Hz +channels : %i +precision : %i-bit +encoding : 0x%x +seek : %i +sample count : %i +EOF count : %i +paused : %s +error occured : %s +waiting : %s +active : %s +", + info.play.buffer_size, + info.play.sample_rate, + info.play.channels, + info.play.precision, + info.play.encoding, + info.play.seek, + info.play.samples, + info.play.eof, + info.play.pause ? "yes" : "no", + info.play.error ? "yes" : "no", + info.play.waiting ? "yes" : "no", + info.play.active ? "yes": "no"); + + fprintf(stderr," +[audio info] +monitor_gain : %i +hw block size : %d bytes +hi watermark : %i +lo watermark : %i +audio mode : %s +", + info.monitor_gain, + info.blocksize, + info.hiwat, info.lowat, + (info.mode == AUMODE_PLAY) ? "PLAY" + : (info.mode = AUMODE_RECORD) ? "RECORD" + : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" + : "???")); +} +#endif /* DEBUG_AUDIO */ + +static int +OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[64]; + Uint16 setenc; + audio_encoding_t enc; + audio_info_t info; + + AUDIO_INITINFO(&info); + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + +#ifdef USE_TIMER_SYNC + frame_ticks = 0.0; +#endif + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if(audio_fd < 0) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return(-1); + } + + /* Set to play mode */ + info.mode = AUMODE_PLAY; + if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + SDL_SetError("Couldn't put device into play mode"); + return(-1); + } + + mixbuf = NULL; + setenc = 0; + + for(enc.index = 0; (ioctl(audio_fd, AUDIO_GETENC, &enc)>=0) + && (enc.encoding != setenc); enc.index++) + { + switch(spec->format) + { + case AUDIO_U8: /* 8-bit unsigned linear */ + setenc = AUDIO_ENCODING_PCM8; + break; + case AUDIO_S8: /* 8-bit signed linear */ + setenc = AUDIO_ENCODING_SLINEAR; + break; + case AUDIO_U16LSB: /* 16-bit unsigned linear, LSB */ + setenc = AUDIO_ENCODING_ULINEAR_LE; + break; + case AUDIO_U16MSB: /* 16-bit unsigned linear, MSB */ + setenc = AUDIO_ENCODING_ULINEAR_BE; + break; + case AUDIO_S16LSB: /* 16-bit signed linear, LSB */ + setenc = AUDIO_ENCODING_SLINEAR_LE; + break; + case AUDIO_S16MSB: /* 16-bit signed linear, MSB */ + setenc = AUDIO_ENCODING_SLINEAR_BE; + break; + } +#ifdef DEBUG_AUDIO + fprintf(stderr,"encoding #%i: \"%s\" %i-bit (0x%x) flags=%i...\n", + enc.index, enc.name, enc.precision, enc.encoding, enc.flags); +#endif + } + + if(!setenc) { + SDL_SetError("No supported encoding for 0x%x", spec->format); + return(-1); + } + + /* Set audio encoding */ + info.play.encoding = enc.encoding; + info.play.precision = enc.precision; + if((ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)) { + SDL_SetError("Couldn't set encoding to 0x%x %i-bit", + enc.encoding, enc.precision); + return(-1); + } + + /* Set audio channels */ + info.play.channels = spec->channels; + if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + info.play.channels = (spec->channels > 1); + ioctl(audio_fd, AUDIO_SETINFO, &info); + } + + /* Set the sample rate */ + info.play.sample_rate = spec->freq; + if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + SDL_SetError("Couldn't set sample rate to %i Hz", spec->freq); + return(-1); + } + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen); + if(mixbuf == NULL) { + return(-1); + } + memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + +#ifdef DEBUG_AUDIO + OBSD_Status(this); +#endif + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/openbsd/SDL_openbsdaudio.h b/src/audio/openbsd/SDL_openbsdaudio.h new file mode 100644 index 000000000..4798ecb4b --- /dev/null +++ b/src/audio/openbsd/SDL_openbsdaudio.h @@ -0,0 +1,57 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + 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 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifndef _SDL_openbsdaudio_h +#define _SDL_openbsdaudio_h + +#include "SDL_sysaudio.h" + +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData +{ + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + /* Support for audio timing using a timer, in addition to select() */ + float frame_ticks; + float next_frame; +}; + +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_openbsdaudio_h */