Dynamic loading for NAS audio driver. SDL-ryan-multiple-audio-device
authorRyan C. Gordon <icculus@icculus.org>
Sat, 07 Oct 2006 07:25:30 +0000
branchSDL-ryan-multiple-audio-device
changeset 3825 76c5a414b996
parent 3824 25052dd25810
child 3826 5b483ce86357
Dynamic loading for NAS audio driver.
configure.in
include/SDL_config.h.in
src/audio/nas/SDL_nasaudio.c
--- a/configure.in	Sat Oct 07 07:23:52 2006 +0000
+++ b/configure.in	Sat Oct 07 07:25:30 2006 +0000
@@ -511,10 +511,11 @@
 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
+        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
-        AC_CHECK_HEADER(audio/audiolib.h, have_nas_hdr=yes)
-        AC_CHECK_LIB(audio, AuOpenServer, have_nas_lib=yes)
 
         if test x$have_nas_hdr = xyes -a x$have_nas_lib = xyes; then
             have_nas=yes
@@ -532,12 +533,40 @@
             have_nas=yes
             NAS_LIBS="-lnas -lXt"
         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$alsa_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
--- a/include/SDL_config.h.in	Sat Oct 07 07:23:52 2006 +0000
+++ b/include/SDL_config.h.in	Sat Oct 07 07:25:30 2006 +0000
@@ -168,6 +168,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
--- a/src/audio/nas/SDL_nasaudio.c	Sat Oct 07 07:23:52 2006 +0000
+++ b/src/audio/nas/SDL_nasaudio.c	Sat Oct 07 07:25:30 2006 +0000
@@ -32,6 +32,7 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
+#include "SDL_loadso.h"
 #include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
 #include "SDL_nasaudio.h"
@@ -41,17 +42,128 @@
 
 static struct SDL_PrivateAudioData *this2 = NULL;
 
-/* !!! FIXME: dynamic loading? */
+
+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 int library_load_count = 0;
+
+static void
+UnloadNASLibrary(void)
+{
+    if ((nas_handle != NULL) && (--library_load_count == 0)) {
+        SDL_UnloadObject(nas_handle);
+        nas_handle = NULL;
+    }
+}
+
+static int
+LoadNASLibrary(void)
+{
+    int retval = 0;
+    if (library_load_count++ == 0) {
+        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);
+
+            library_load_count--;
+            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 */
 
 static int
 NAS_Available(void)
 {
-    AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
-    if (!aud)
-        return 0;
-
-    AuCloseServer(aud);
-    return 1;
+    int available = 0;
+    if (LoadNASLibrary() >= 0) {
+        AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
+        if (aud != NULL) {
+            available = 1;
+            NAS_AuCloseServer(aud);
+        }
+        UnloadNASLibrary();
+    }
+    return available;
 }
 
 /* This function waits until it is possible to write a full sound buffer */
@@ -60,8 +172,8 @@
 {
     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);
     }
 }
 
@@ -75,13 +187,13 @@
          *  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,
+    NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
                    this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
 
     this->hidden->written += this->hidden->mixlen;
@@ -106,11 +218,12 @@
             this->hidden->mixbuf = NULL;
         }
         if (this->hidden->aud) {
-            AuCloseServer(this->hidden->aud);
+            NAS_AuCloseServer(this->hidden->aud);
             this->hidden->aud = 0;
         }
         SDL_free(this->hidden);
         this2 = this->hidden = NULL;
+        UnloadNASLibrary();
     }
 }
 
@@ -175,6 +288,7 @@
 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)) ==
@@ -202,6 +316,11 @@
     }
     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
 
+    if (LoadNASLibrary() < 0) {
+        NAS_CloseDevice(this);
+        return 0;
+    }
+
     /* Try for a closest match on audio format */
     format = 0;
     for (test_format = SDL_FirstAudioFormat(this->spec.format);
@@ -218,7 +337,7 @@
     }
     this->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) {
         NAS_CloseDevice(this);
         SDL_SetError("NAS: Couldn't open connection to NAS server");
@@ -227,7 +346,7 @@
 
     this->hidden->dev = find_device(this, this->spec.channels);
     if ((this->hidden->dev == AuNone)
-        || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
+        || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
         NAS_CloseDevice(this);
         SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
         return 0;
@@ -249,12 +368,13 @@
                               AuTrue, buffer_size, buffer_size / 4, 0, NULL);
     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->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, event_handler,
-                           (AuPointer) NULL);
+    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 = this->spec.size;
@@ -262,7 +382,7 @@
     if (this->hidden->mixbuf == NULL) {
         NAS_CloseDevice(this);
         SDL_OutOfMemory();
-        return (-1);
+        return 0;
     }
     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);