Made NAS target dynamic, so you can safely include it in shipping binaries. SDL-1.2
authorRyan C. Gordon <icculus@icculus.org>
Sun, 13 Sep 2009 22:19:56 +0000
branchSDL-1.2
changeset 4192 2f5e884f0a9d
parent 4191 caaa3cbf7b13
child 4193 430e6690c4cd
Made NAS target dynamic, so you can safely include it in shipping binaries.
configure.in
include/SDL_config.h.in
src/audio/nas/SDL_nasaudio.c
--- a/configure.in	Sat Sep 12 19:41:54 2009 +0000
+++ b/configure.in	Sun Sep 13 22:19:56 2009 +0000
@@ -591,30 +591,62 @@
 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
--- a/include/SDL_config.h.in	Sat Sep 12 19:41:54 2009 +0000
+++ b/include/SDL_config.h.in	Sun Sep 13 22:19:56 2009 +0000
@@ -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
--- a/src/audio/nas/SDL_nasaudio.c	Sat Sep 12 19:41:54 2009 +0000
+++ b/src/audio/nas/SDL_nasaudio.c	Sun Sep 13 22:19:56 2009 +0000
@@ -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 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 @@
 {
 	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 @@
 		if ( this ) {
 			SDL_free(this);
 		}
-		return(0);
+		return NULL;
 	}
 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
 
@@ -108,8 +230,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);
 	}
 }
 
@@ -119,13 +241,13 @@
 				    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 @@
 		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 @@
 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 @@
 	}
 	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 @@
 	}
 	
 	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 @@
 
 	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;