Commit 0d0f7080a37958e31d0697e0ce3d87e1b97c5692

Ryan C. Gordon 2016-08-02T13:50:21

audio: implemented higher level infrastructure for running capture devices.

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 934652a..b8b045a 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -206,6 +206,17 @@ SDL_AudioWaitDone_Default(_THIS)
 {                               /* no-op. */
 }
 
+static int
+SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
+{
+    return -1;  /* just fail immediately. */
+}
+
+static void
+SDL_AudioFlushCapture_Default(_THIS)
+{                               /* no-op. */
+}
+
 static void
 SDL_AudioCloseDevice_Default(_THIS)
 {                               /* no-op. */
@@ -279,6 +290,8 @@ finalize_audio_entry_points(void)
     FILL_STUB(GetPendingBytes);
     FILL_STUB(GetDeviceBuf);
     FILL_STUB(WaitDone);
+    FILL_STUB(CaptureFromDevice);
+    FILL_STUB(FlushCapture);
     FILL_STUB(CloseDevice);
     FILL_STUB(LockDevice);
     FILL_STUB(UnlockDevice);
@@ -592,7 +605,7 @@ SDL_ClearQueuedAudio(SDL_AudioDeviceID devid)
 
 
 /* The general mixing thread function */
-int SDLCALL
+static int SDLCALL
 SDL_RunAudio(void *devicep)
 {
     SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
@@ -601,7 +614,9 @@ SDL_RunAudio(void *devicep)
     const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
     Uint8 *stream;
     void *udata = device->spec.userdata;
-    void (SDLCALL *fill) (void *, Uint8 *, int) = device->spec.callback;
+    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
+
+    SDL_assert(!device->iscapture);
 
     /* The audio mixing is always a high priority thread */
     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
@@ -635,7 +650,7 @@ SDL_RunAudio(void *devicep)
         if (SDL_AtomicGet(&device->paused)) {
             SDL_memset(stream, silence, stream_len);
         } else {
-            (*fill) (udata, stream, stream_len);
+            (*callback) (udata, stream, stream_len);
         }
         SDL_UnlockMutex(device->mixer_lock);
 
@@ -661,11 +676,92 @@ SDL_RunAudio(void *devicep)
     }
 
     /* Wait for the audio to drain. */
+    /* !!! FIXME: can we rename this WaitDrain? */
     current_audio.impl.WaitDone(device);
 
     return 0;
 }
 
+/* The general capture thread function */
+static int SDLCALL
+SDL_CaptureAudio(void *devicep)
+{
+    SDL_AudioDevice *device = (SDL_AudioDevice *) devicep;
+    const int silence = (int) device->spec.silence;
+    const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);
+    const int stream_len = (device->convert.needed) ? device->convert.len : device->spec.size;
+    Uint8 *stream;
+    void *udata = device->spec.userdata;
+    void (SDLCALL *callback) (void *, Uint8 *, int) = device->spec.callback;
+
+    SDL_assert(device->iscapture);
+
+    /* The audio mixing is always a high priority thread */
+    SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
+
+    /* Perform any thread setup */
+    device->threadid = SDL_ThreadID();
+    current_audio.impl.ThreadInit(device);
+
+    /* Loop, filling the audio buffers */
+    while (!SDL_AtomicGet(&device->shutdown)) {
+        int still_need;
+        Uint8 *ptr;
+
+        if (!SDL_AtomicGet(&device->enabled) || SDL_AtomicGet(&device->paused)) {
+            SDL_Delay(delay);  /* just so we don't cook the CPU. */
+            current_audio.impl.FlushCapture(device);  /* dump anything pending. */
+            continue;
+        }
+
+        /* Fill the current buffer with sound */
+        still_need = stream_len;
+        if (device->convert.needed) {
+            ptr = stream = device->convert.buf;
+        } else {
+            /* just use the "fake" stream to hold data read from the device. */
+            ptr = stream = device->fake_stream;
+        }
+
+        /* We still read from the device when "paused" to keep the state sane,
+           and block when there isn't data so this thread isn't eating CPU.
+           But we don't process it further or call the app's callback. */
+
+        while (still_need > 0) {
+            const int rc = current_audio.impl.CaptureFromDevice(device, ptr, still_need);
+            SDL_assert(rc != 0);  /* device should have blocked, failed, or returned data. */
+            SDL_assert(rc <= still_need);  /* device should not overflow buffer. :) */
+            if (rc > 0) {
+                still_need -= rc;
+                ptr += rc;
+            } else {  /* uhoh, device failed for some reason! */
+                SDL_OpenedAudioDeviceDisconnected(device);
+                break;
+            }
+        }
+
+        if (still_need > 0) {
+            /* Keep any data we already read, silence the rest. */
+            SDL_memset(ptr, silence, still_need);
+        }
+
+        if (device->convert.needed) {
+            SDL_ConvertAudio(&device->convert);
+        }
+
+        /* !!! FIXME: this should be LockDevice. */
+        SDL_LockMutex(device->mixer_lock);
+        if (!SDL_AtomicGet(&device->paused)) {
+            (*callback)(udata, stream, stream_len);
+        }
+        SDL_UnlockMutex(device->mixer_lock);
+    }
+
+    current_audio.impl.FlushCapture(device);
+
+    return 0;
+}
+
 
 static SDL_AudioFormat
 SDL_ParseAudioFormat(const char *string)
@@ -1198,10 +1294,11 @@ open_audio_device(const char *devname, int iscapture,
         /* !!! FIXME: we don't force the audio thread stack size here because it calls into user code, but maybe we should? */
         /* buffer queueing callback only needs a few bytes, so make the stack tiny. */
         char name[64];
-        const size_t stacksize = (device->spec.callback == SDL_BufferQueueDrainCallback) ? 64 * 1024 : 0;
+        const SDL_bool is_internal_thread = (device->spec.callback == SDL_BufferQueueDrainCallback);
+        const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
 
         SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
-        device->thread = SDL_CreateThreadInternal(SDL_RunAudio, name, stacksize, device);
+        device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, name, stacksize, device);
 
         if (device->thread == NULL) {
             SDL_CloseAudioDevice(device->id);
diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h
index b03a915..7cde3a6 100644
--- a/src/audio/SDL_audio_c.h
+++ b/src/audio/SDL_audio_c.h
@@ -29,9 +29,6 @@ extern SDL_AudioFormat SDL_NextAudioFormat(void);
 /* Function to calculate the size and silence for a SDL_AudioSpec */
 extern void SDL_CalculateAudioSpec(SDL_AudioSpec * spec);
 
-/* The actual mixing thread function */
-extern int SDLCALL SDL_RunAudio(void *audiop);
-
 /* this is used internally to access some autogenerated code. */
 typedef struct
 {
diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h
index 0b35b27..d3a48cc 100644
--- a/src/audio/SDL_sysaudio.h
+++ b/src/audio/SDL_sysaudio.h
@@ -76,6 +76,8 @@ typedef struct SDL_AudioDriverImpl
     int (*GetPendingBytes) (_THIS);
     Uint8 *(*GetDeviceBuf) (_THIS);
     void (*WaitDone) (_THIS);
+    int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
+    void (*FlushCapture) (_THIS);
     void (*CloseDevice) (_THIS);
     void (*LockDevice) (_THIS);
     void (*UnlockDevice) (_THIS);