nas: initial shot at audio capture support. Compiles, but not tested.
authorRyan C. Gordon <icculus@icculus.org>
Fri, 05 Aug 2016 04:23:32 -0400
changeset 10241 457d9c7868ec
parent 10240 0ba7106e9a6d
child 10242 48c3f46395ae
nas: initial shot at audio capture support. Compiles, but not tested.
src/audio/nas/SDL_nasaudio.c
--- a/src/audio/nas/SDL_nasaudio.c	Fri Aug 05 02:04:48 2016 -0400
+++ b/src/audio/nas/SDL_nasaudio.c	Fri Aug 05 04:23:32 2016 -0400
@@ -33,18 +33,18 @@
 #include "../SDL_audio_c.h"
 #include "SDL_nasaudio.h"
 
-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 void (*NAS_AuHandleEvents) (AuServer *);
 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 AuUint32 (*NAS_AuReadElement)
+  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
 static AuServer *(*NAS_AuOpenServer)
   (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
@@ -79,10 +79,12 @@
     SDL_NAS_SYM(AuCloseServer);
     SDL_NAS_SYM(AuNextEvent);
     SDL_NAS_SYM(AuDispatchEvent);
+    SDL_NAS_SYM(AuHandleEvents);
     SDL_NAS_SYM(AuCreateFlow);
     SDL_NAS_SYM(AuStartFlow);
     SDL_NAS_SYM(AuSetElements);
     SDL_NAS_SYM(AuWriteElement);
+    SDL_NAS_SYM(AuReadElement);
     SDL_NAS_SYM(AuOpenServer);
     SDL_NAS_SYM(AuRegisterEventHandler);
     return 0;
@@ -186,6 +188,45 @@
     return (this->hidden->mixbuf);
 }
 
+static int
+NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    int retval;
+
+    while (SDL_TRUE) {
+        /* just keep the event queue moving and the server chattering. */
+        NAS_AuHandleEvents(h->aud);
+    
+        retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
+        /*printf("read %d capture bytes\n", (int) retval);*/
+        if (retval == 0) {
+            SDL_Delay(10);  /* don't burn the CPU if we're waiting for data. */
+        } else {
+            break;
+        }
+    }
+
+    return retval;
+}
+
+static void
+NAS_FlushCapture(_THIS)
+{
+    struct SDL_PrivateAudioData *h = this->hidden;
+    AuUint32 total = 0;
+    AuUint32 br;
+    Uint8 buf[512];
+
+    do {
+        /* just keep the event queue moving and the server chattering. */
+        NAS_AuHandleEvents(h->aud);
+        br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof (buf), buf, NULL);
+        /*printf("flushed %d capture bytes\n", (int) br);*/
+        total += br;
+    } while ((br == sizeof (buf)) && (total < this->spec.size));
+}
+
 static void
 NAS_CloseDevice(_THIS)
 {
@@ -219,6 +260,12 @@
 static AuBool
 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
 {
+    SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data;
+    struct SDL_PrivateAudioData *h = this->hidden;
+    if (this->iscapture) {
+        return AuTrue;  /* we don't (currently) care about any of this for capture devices */
+    }
+
     switch (ev->type) {
     case AuEventTypeElementNotify:
         {
@@ -226,24 +273,24 @@
 
             switch (event->kind) {
             case AuElementNotifyKindLowWater:
-                if (this2->buf_free >= 0) {
-                    this2->really += event->num_bytes;
-                    gettimeofday(&this2->last_tv, 0);
-                    this2->buf_free += event->num_bytes;
+                if (h->buf_free >= 0) {
+                    h->really += event->num_bytes;
+                    gettimeofday(&h->last_tv, 0);
+                    h->buf_free += event->num_bytes;
                 } else {
-                    this2->buf_free = event->num_bytes;
+                    h->buf_free = event->num_bytes;
                 }
                 break;
             case AuElementNotifyKindState:
                 switch (event->cur_state) {
                 case AuStatePause:
                     if (event->reason != AuReasonUser) {
-                        if (this2->buf_free >= 0) {
-                            this2->really += event->num_bytes;
-                            gettimeofday(&this2->last_tv, 0);
-                            this2->buf_free += event->num_bytes;
+                        if (h->buf_free >= 0) {
+                            h->really += event->num_bytes;
+                            gettimeofday(&h->last_tv, 0);
+                            h->buf_free += event->num_bytes;
                         } else {
-                            this2->buf_free = event->num_bytes;
+                            h->buf_free = event->num_bytes;
                         }
                     }
                     break;
@@ -255,15 +302,29 @@
 }
 
 static AuDeviceID
-find_device(_THIS, int nch)
+find_device(_THIS)
 {
     /* These "Au" things are all macros, not functions... */
+    struct SDL_PrivateAudioData *h = this->hidden;
+    const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput;
+    const int numdevs = AuServerNumDevices(h->aud);
+    const int nch = this->spec.channels;
     int i;
-    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
-        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
-             AuComponentKindPhysicalOutput) &&
-            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
-            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
+
+    /* Try to find exact match on channels first... */
+    for (i = 0; i < numdevs; i++) {
+        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
+        if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) {
+            return AuDeviceIdentifier(dev);
+        }
+    }
+
+    /* Take anything, then... */
+    for (i = 0; i < numdevs; i++) {
+        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
+        if (AuDeviceKind(dev) == devicekind) {
+            this->spec.channels = AuDeviceNumTracks(dev);
+            return AuDeviceIdentifier(dev);
         }
     }
     return AuNone;
@@ -303,7 +364,7 @@
         return SDL_SetError("NAS: Couldn't open connection to NAS server");
     }
 
-    this->hidden->dev = find_device(this, this->spec.channels);
+    this->hidden->dev = find_device(this);
     if ((this->hidden->dev == AuNone)
         || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
         return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
@@ -319,28 +380,38 @@
     /* Calculate the final parameters for this audio specification */
     SDL_CalculateAudioSpec(&this->spec);
 
-    this2 = this->hidden;
+    if (iscapture) {
+        AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev,
+                                  AuUnlimitedSamples, 0, NULL);
+        AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format,
+                                  this->spec.channels, AuTrue, buffer_size,
+                                  buffer_size, 0, NULL);
+    } else {
+        AuMakeElementImportClient(elms, this->spec.freq, format,
+                                  this->spec.channels, AuTrue, buffer_size,
+                                  buffer_size / 4, 0, NULL);
+        AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
+                                  AuUnlimitedSamples, 0, NULL);
+    }
 
-    AuMakeElementImportClient(elms, this->spec.freq, format,
-                              this->spec.channels, AuTrue, buffer_size,
-                              buffer_size / 4, 0, NULL);
-    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
-                              AuUnlimitedSamples, 0, NULL);
-    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
-                      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);
+                               (AuPointer) this);
 
     NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
 
     /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        return SDL_OutOfMemory();
+    if (!iscapture) {
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     /* We're ready to rock and roll. :-) */
     return 0;
@@ -371,9 +442,14 @@
     impl->PlayDevice = NAS_PlayDevice;
     impl->WaitDevice = NAS_WaitDevice;
     impl->GetDeviceBuf = NAS_GetDeviceBuf;
+    impl->CaptureFromDevice = NAS_CaptureFromDevice;
+    impl->FlushCapture = NAS_FlushCapture;
     impl->CloseDevice = NAS_CloseDevice;
     impl->Deinitialize = NAS_Deinitialize;
-    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
+
+    impl->OnlyHasDefaultOutputDevice = 1;
+    impl->OnlyHasDefaultCaptureDevice = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }