diff src/audio/nas/SDL_nasaudio.c @ 2049:5f6550e5184f

Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author Ryan C. Gordon <icculus@icculus.org>
date Tue, 17 Oct 2006 09:15:21 +0000
parents 3b4ce57c6215
children 866052b01ee5
line wrap: on
line diff
--- a/src/audio/nas/SDL_nasaudio.c	Sun Oct 01 16:10:41 2006 +0000
+++ b/src/audio/nas/SDL_nasaudio.c	Tue Oct 17 09:15:21 2006 +0000
@@ -32,106 +32,149 @@
 
 #include "SDL_timer.h"
 #include "SDL_audio.h"
+#include "SDL_loadso.h"
 #include "../SDL_audiomem.h"
 #include "../SDL_audio_c.h"
-#include "../SDL_audiodev_c.h"
 #include "SDL_nasaudio.h"
 
-/* The tag name used by artsc audio */
+/* The tag name used by nas audio */
 #define NAS_DRIVER_NAME         "nas"
 
 static struct SDL_PrivateAudioData *this2 = NULL;
 
-/* Audio driver functions */
-static int NAS_OpenAudio(_THIS, SDL_AudioSpec * spec);
-static void NAS_WaitAudio(_THIS);
-static void NAS_PlayAudio(_THIS);
-static Uint8 *NAS_GetAudioBuf(_THIS);
-static void NAS_CloseAudio(_THIS);
 
-/* Audio driver bootstrap functions */
+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
-Audio_Available(void)
+load_nas_sym(const char *fn, void **addr)
 {
-    AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
-    if (!aud)
+    *addr = SDL_LoadFunction(nas_handle, fn);
+    if (*addr == NULL) {
         return 0;
-
-    AuCloseServer(aud);
+    }
     return 1;
 }
 
-static void
-Audio_DeleteDevice(SDL_AudioDevice * device)
+/* 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_free(device->hidden);
-    SDL_free(device);
+    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 SDL_AudioDevice *
-Audio_CreateDevice(int devindex)
+static int
+LoadNASLibrary(void)
 {
-    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));
+    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();
+            }
+        }
     }
-    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 = NAS_OpenAudio;
-    this->WaitAudio = NAS_WaitAudio;
-    this->PlayAudio = NAS_PlayAudio;
-    this->GetAudioBuf = NAS_GetAudioBuf;
-    this->CloseAudio = NAS_CloseAudio;
-
-    this->free = Audio_DeleteDevice;
-
-    return this;
+    return retval;
 }
 
-AudioBootStrap NAS_bootstrap = {
-    NAS_DRIVER_NAME, "Network Audio System",
-    Audio_Available, Audio_CreateDevice
-};
+#else
+
+static void
+UnloadNASLibrary(void)
+{
+}
+
+static int
+LoadNASLibrary(void)
+{
+    load_nas_syms();
+    return 0;
+}
+
+#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
 
 /* This function waits until it is possible to write a full sound buffer */
 static void
-NAS_WaitAudio(_THIS)
+NAS_WaitDevice(_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);
     }
 }
 
 static void
-NAS_PlayAudio(_THIS)
+NAS_PlayDevice(_THIS)
 {
-    while (this->hidden->mixlen > this->hidden->buf_free) {     /* We think the buffer is full? Yikes! Ask the server for events,
-                                                                   in the hope that some of them is LowWater events telling us more
-                                                                   of the buffer is free now than what we think. */
+    while (this->hidden->mixlen > this->hidden->buf_free) {
+        /*
+         * We think the buffer is full? Yikes! Ask the server for events,
+         *  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,
+    NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
                    this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
 
     this->hidden->written += this->hidden->mixlen;
@@ -142,21 +185,25 @@
 }
 
 static Uint8 *
-NAS_GetAudioBuf(_THIS)
+NAS_GetDeviceBuf(_THIS)
 {
     return (this->hidden->mixbuf);
 }
 
 static void
-NAS_CloseAudio(_THIS)
+NAS_CloseDevice(_THIS)
 {
-    if (this->hidden->mixbuf != NULL) {
-        SDL_FreeAudioMem(this->hidden->mixbuf);
-        this->hidden->mixbuf = NULL;
-    }
-    if (this->hidden->aud) {
-        AuCloseServer(this->hidden->aud);
-        this->hidden->aud = 0;
+    if (this->hidden != NULL) {
+        if (this->hidden->mixbuf != NULL) {
+            SDL_FreeAudioMem(this->hidden->mixbuf);
+            this->hidden->mixbuf = NULL;
+        }
+        if (this->hidden->aud) {
+            NAS_AuCloseServer(this->hidden->aud);
+            this->hidden->aud = 0;
+        }
+        SDL_free(this->hidden);
+        this2 = this->hidden = NULL;
     }
 }
 
@@ -221,6 +268,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)) ==
@@ -233,46 +281,53 @@
 }
 
 static int
-NAS_OpenAudio(_THIS, SDL_AudioSpec * spec)
+NAS_OpenDevice(_THIS, const char *devname, int iscapture)
 {
     AuElement elms[3];
     int buffer_size;
     SDL_AudioFormat test_format, format;
 
-    this->hidden->mixbuf = NULL;
+    /* Initialize all variables that we clean on shutdown */
+    this->hidden = (struct SDL_PrivateAudioData *)
+                        SDL_malloc((sizeof *this->hidden));
+    if (this->hidden == NULL) {
+        SDL_OutOfMemory();
+        return 0;
+    }
+    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
 
     /* Try for a closest match on audio format */
     format = 0;
-    for (test_format = SDL_FirstAudioFormat(spec->format);
+    for (test_format = SDL_FirstAudioFormat(this->spec.format);
          !format && test_format;) {
         format = sdlformat_to_auformat(test_format);
-
         if (format == AuNone) {
             test_format = SDL_NextAudioFormat();
         }
     }
     if (format == 0) {
-        SDL_SetError("Couldn't find any hardware audio formats");
-        return (-1);
+        NAS_CloseDevice(this);
+        SDL_SetError("NAS: Couldn't find any hardware audio formats");
+        return 0;
     }
-    spec->format = test_format;
+    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) {
-        SDL_SetError("Couldn't open connection to NAS server");
-        return (-1);
+        NAS_CloseDevice(this);
+        SDL_SetError("NAS: Couldn't open connection to NAS server");
+        return 0;
     }
 
-    this->hidden->dev = find_device(this, spec->channels);
+    this->hidden->dev = find_device(this, this->spec.channels);
     if ((this->hidden->dev == AuNone)
-        || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
-        AuCloseServer(this->hidden->aud);
-        this->hidden->aud = 0;
-        SDL_SetError("Couldn't find a fitting playback device on NAS server");
-        return (-1);
+        || (!(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;
     }
 
-    buffer_size = spec->freq;
+    buffer_size = this->spec.freq;
     if (buffer_size < 4096)
         buffer_size = 4096;
 
@@ -280,35 +335,70 @@
         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
 
     /* Calculate the final parameters for this audio specification */
-    SDL_CalculateAudioSpec(spec);
+    SDL_CalculateAudioSpec(&this->spec);
 
     this2 = this->hidden;
 
-    AuMakeElementImportClient(elms, spec->freq, format, spec->channels,
+    AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
                               AuTrue, buffer_size, buffer_size / 4, 0, NULL);
-    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, spec->freq,
+    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 = spec->size;
+    this->hidden->mixlen = this->spec.size;
     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
     if (this->hidden->mixbuf == NULL) {
-        return (-1);
+        NAS_CloseDevice(this);
+        SDL_OutOfMemory();
+        return 0;
     }
-    SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
-
-    /* Get the parent process id (we're the parent of the audio thread) */
-    this->hidden->parent = getpid();
+    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     /* We're ready to rock and roll. :-) */
-    return (0);
+    return 1;
+}
+
+static void
+NAS_Deinitialize(void)
+{
+    UnloadNASLibrary();
 }
 
+static int
+NAS_Init(SDL_AudioDriverImpl *impl)
+{
+    if (LoadNASLibrary() < 0) {
+        return 0;
+    } else {
+        AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
+        if (aud == NULL) {
+            SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
+            return 0;
+        }
+        NAS_AuCloseServer(aud);
+    }
+
+    /* Set the function pointers */
+    impl->OpenDevice = NAS_OpenDevice;
+    impl->PlayDevice = NAS_PlayDevice;
+    impl->WaitDevice = NAS_WaitDevice;
+    impl->GetDeviceBuf = NAS_GetDeviceBuf;
+    impl->CloseDevice = NAS_CloseDevice;
+    impl->Deinitialize = NAS_Deinitialize;
+    impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: is this true? */
+
+    return 1;
+}
+
+AudioBootStrap NAS_bootstrap = {
+    NAS_DRIVER_NAME, "Network Audio System", NAS_Init, 0
+};
+
 /* vi: set ts=4 sw=4 expandtab: */