From 81244ca48c6ad91a86e90bdd07b90fe24f6a625e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 13 Sep 2009 22:19:56 +0000 Subject: [PATCH] Made NAS target dynamic, so you can safely include it in shipping binaries. --- configure.in | 56 +++++++++--- include/SDL_config.h.in | 1 + src/audio/nas/SDL_nasaudio.c | 160 +++++++++++++++++++++++++++++++---- 3 files changed, 187 insertions(+), 30 deletions(-) diff --git a/configure.in b/configure.in index 973cb2cc1..4c22f8917 100644 --- a/configure.in +++ b/configure.in @@ -591,30 +591,62 @@ CheckNAS() AC_HELP_STRING([--enable-nas], [support the NAS audio API [[default=yes]]]), , enable_nas=yes) if test x$enable_audio = xyes -a x$enable_nas = xyes; then - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -lXt -lm" - AC_CHECK_HEADER(audio/audiolib.h, have_audio_hdr=yes) - AC_CHECK_LIB(audio, AuOpenServer, have_audio_lib=yes) - AC_CHECK_HEADER(nas/audiolib.h, have_nas_hdr=yes) - AC_CHECK_LIB(nas, AuOpenServer, have_nas_lib=yes) - LDFLAGS="$save_LDFLAGS" + AC_CHECK_HEADER(audio/audiolib.h, have_nas_hdr=yes) + AC_CHECK_LIB(audio, AuOpenServer, have_nas_lib=yes) AC_MSG_CHECKING(for NAS audio support) have_nas=no - if test x$have_audio_hdr = xyes -a x$have_audio_lib = xyes; then + + if test x$have_nas_hdr = xyes -a x$have_nas_lib = xyes; then + have_nas=yes + NAS_LIBS="-laudio" + + elif test -r /usr/X11R6/include/audio/audiolib.h; then have_nas=yes - NAS_LIBS="-laudio -lXt" - elif test x$have_nas_hdr = xyes -a x$have_nas_lib = xyes; then + NAS_CFLAGS="-I/usr/X11R6/include/" + NAS_LIBS="-L/usr/X11R6/lib -laudio -lXt" + + dnl On IRIX, the NAS includes are in a different directory, + dnl and libnas must be explicitly linked in + + elif test -r /usr/freeware/include/nas/audiolib.h; then have_nas=yes NAS_LIBS="-lnas -lXt" - fi + fi + AC_MSG_RESULT($have_nas) if test x$have_nas = xyes; then + AC_ARG_ENABLE(nas-shared, +AC_HELP_STRING([--enable-nas-shared], [dynamically load NAS audio support [[default=yes]]]), + , enable_nas_shared=yes) + if test "x`echo $NAS_LIBS | grep -- -L`" = "x"; then + if test "x`ls /lib/libaudio.so.* 2> /dev/null`" != "x"; then + NAS_LIBS="-L/lib $NAS_LIBS" + elif test "x`ls /usr/lib/libaudio.so.* 2> /dev/null`" != "x"; then + NAS_LIBS="-L/usr/lib $NAS_LIBS" + elif test "x`ls /usr/local/lib/libaudio.so.* 2> /dev/null`" != "x"; then + NAS_LIBS="-L/usr/local/lib $NAS_LIBS" + fi + fi + nas_lib_spec=`echo $NAS_LIBS | sed 's/.*-L\([[^ ]]*\).*/\1\/libaudio.so.*/'` + nas_lib=`ls -- $nas_lib_spec | sed 's/.*\/\(.*\)/\1/; q'` + echo "-- $nas_lib_spec -> $nas_lib" + + if test x$have_loadso != xyes && \ + test x$enable_nas_shared = xyes; then + AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic NAS loading]) + fi + if test x$have_loadso = xyes && \ + test x$enable_nas_shared = xyes && test x$nas_lib != x; then + AC_DEFINE_UNQUOTED(SDL_AUDIO_DRIVER_NAS_DYNAMIC, "$nas_lib") + else + EXTRA_LDFLAGS="$EXTRA_LDFLAGS $NAS_LIBS" + fi + AC_DEFINE(SDL_AUDIO_DRIVER_NAS) SOURCES="$SOURCES $srcdir/src/audio/nas/*.c" EXTRA_CFLAGS="$EXTRA_CFLAGS $NAS_CFLAGS" - EXTRA_LDFLAGS="$EXTRA_LDFLAGS $NAS_LIBS" have_audio=yes fi fi diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in index a63618fb9..05aa2c272 100644 --- a/include/SDL_config.h.in +++ b/include/SDL_config.h.in @@ -176,6 +176,7 @@ #undef SDL_AUDIO_DRIVER_MINT #undef SDL_AUDIO_DRIVER_MMEAUDIO #undef SDL_AUDIO_DRIVER_NAS +#undef SDL_AUDIO_DRIVER_NAS_DYNAMIC #undef SDL_AUDIO_DRIVER_OSS #undef SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H #undef SDL_AUDIO_DRIVER_PAUD diff --git a/src/audio/nas/SDL_nasaudio.c b/src/audio/nas/SDL_nasaudio.c index afe9731c1..677eb17fb 100644 --- a/src/audio/nas/SDL_nasaudio.c +++ b/src/audio/nas/SDL_nasaudio.c @@ -37,11 +37,122 @@ #include "../SDL_audiodev_c.h" #include "SDL_nasaudio.h" +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC +#include "SDL_loadso.h" +#endif + /* The tag name used by artsc audio */ #define NAS_DRIVER_NAME "nas" static struct SDL_PrivateAudioData *this2 = NULL; +static void (*NAS_AuCloseServer) (AuServer *); +static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *); +static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *); +static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *); +static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *); +static void (*NAS_AuSetElements) + (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *); +static void (*NAS_AuWriteElement) + (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *); +static AuServer *(*NAS_AuOpenServer) + (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **); +static AuEventHandlerRec *(*NAS_AuRegisterEventHandler) + (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer); + + +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC + +static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC; +static void *nas_handle = NULL; + +static int +load_nas_sym(const char *fn, void **addr) +{ + *addr = SDL_LoadFunction(nas_handle, fn); + if (*addr == NULL) { + return 0; + } + return 1; +} + +/* cast funcs to char* first, to please GCC's strict aliasing rules. */ +#define SDL_NAS_SYM(x) \ + if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1 +#else +#define SDL_NAS_SYM(x) NAS_##x = x +#endif + +static int +load_nas_syms(void) +{ + SDL_NAS_SYM(AuCloseServer); + SDL_NAS_SYM(AuNextEvent); + SDL_NAS_SYM(AuDispatchEvent); + SDL_NAS_SYM(AuCreateFlow); + SDL_NAS_SYM(AuStartFlow); + SDL_NAS_SYM(AuSetElements); + SDL_NAS_SYM(AuWriteElement); + SDL_NAS_SYM(AuOpenServer); + SDL_NAS_SYM(AuRegisterEventHandler); + return 0; +} + +#undef SDL_NAS_SYM + +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC + +static void +UnloadNASLibrary(void) +{ + if (nas_handle != NULL) { + SDL_UnloadObject(nas_handle); + nas_handle = NULL; + } +} + +static int +LoadNASLibrary(void) +{ + int retval = 0; + if (nas_handle == NULL) { + nas_handle = SDL_LoadObject(nas_library); + if (nas_handle == NULL) { + /* Copy error string so we can use it in a new SDL_SetError(). */ + char *origerr = SDL_GetError(); + size_t len = SDL_strlen(origerr) + 1; + char *err = (char *) alloca(len); + SDL_strlcpy(err, origerr, len); + retval = -1; + SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n", + nas_library, err); + } else { + retval = load_nas_syms(); + if (retval < 0) { + UnloadNASLibrary(); + } + } + } + return retval; +} + +#else + +static void +UnloadNASLibrary(void) +{ +} + +static int +LoadNASLibrary(void) +{ + load_nas_syms(); + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */ + + /* Audio driver functions */ static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec); static void NAS_WaitAudio(_THIS); @@ -53,15 +164,22 @@ static void NAS_CloseAudio(_THIS); static int Audio_Available(void) { - AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); - if (!aud) return 0; - - AuCloseServer(aud); - return 1; + if (LoadNASLibrary() == 0) { + AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); + if (!aud) { + UnloadNASLibrary(); + return 0; + } + NAS_AuCloseServer(aud); + UnloadNASLibrary(); + return 1; + } + return 0; } static void Audio_DeleteDevice(SDL_AudioDevice *device) { + UnloadNASLibrary(); SDL_free(device->hidden); SDL_free(device); } @@ -70,6 +188,10 @@ static SDL_AudioDevice *Audio_CreateDevice(int devindex) { SDL_AudioDevice *this; + if (LoadNASLibrary() < 0) { + return NULL; + } + /* Initialize all variables that we clean on shutdown */ this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); if ( this ) { @@ -82,7 +204,7 @@ static SDL_AudioDevice *Audio_CreateDevice(int devindex) if ( this ) { SDL_free(this); } - return(0); + return NULL; } SDL_memset(this->hidden, 0, (sizeof *this->hidden)); @@ -108,8 +230,8 @@ static void NAS_WaitAudio(_THIS) { while ( this->hidden->buf_free < this->hidden->mixlen ) { AuEvent ev; - AuNextEvent(this->hidden->aud, AuTrue, &ev); - AuDispatchEvent(this->hidden->aud, &ev); + NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); + NAS_AuDispatchEvent(this->hidden->aud, &ev); } } @@ -119,13 +241,13 @@ static void NAS_PlayAudio(_THIS) in the hope that some of them is LowWater events telling us more of the buffer is free now than what we think. */ AuEvent ev; - AuNextEvent(this->hidden->aud, AuTrue, &ev); - AuDispatchEvent(this->hidden->aud, &ev); + NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); + NAS_AuDispatchEvent(this->hidden->aud, &ev); } this->hidden->buf_free -= this->hidden->mixlen; /* Write the audio data */ - AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL); + NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL); this->hidden->written += this->hidden->mixlen; @@ -146,7 +268,7 @@ static void NAS_CloseAudio(_THIS) this->hidden->mixbuf = NULL; } if ( this->hidden->aud ) { - AuCloseServer(this->hidden->aud); + NAS_AuCloseServer(this->hidden->aud); this->hidden->aud = 0; } } @@ -211,6 +333,7 @@ event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd) static AuDeviceID find_device(_THIS, int nch) { + /* These "Au" things are all macros, not functions... */ int i; for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) { if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) == @@ -246,7 +369,7 @@ static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec) } spec->format = test_format; - this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL); + this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); if (this->hidden->aud == 0) { SDL_SetError("Couldn't open connection to NAS server"); @@ -254,8 +377,8 @@ static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec) } this->hidden->dev = find_device(this, spec->channels); - if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) { - AuCloseServer(this->hidden->aud); + if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, NULL)))) { + NAS_AuCloseServer(this->hidden->aud); this->hidden->aud = 0; SDL_SetError("Couldn't find a fitting playback device on NAS server"); return (-1); @@ -273,15 +396,16 @@ static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec) this2 = this->hidden; + /* These "Au" things without a NAS_ prefix are macros, not functions... */ AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue, buffer_size, buffer_size / 4, 0, NULL); AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq, AuUnlimitedSamples, 0, NULL); - AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL); - AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow, + NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL); + NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow, event_handler, (AuPointer) NULL); - AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); + NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); /* Allocate mixing buffer */ this->hidden->mixlen = spec->size;