Commit 51d15233800eca1e4e0a818e8bdd173d3a473ce1

Ryan C. Gordon 2016-08-06T19:34:32

winmm: Implemented audio capture support.

diff --git a/src/audio/winmm/SDL_winmm.c b/src/audio/winmm/SDL_winmm.c
index da9312d..f4a9fea 100644
--- a/src/audio/winmm/SDL_winmm.c
+++ b/src/audio/winmm/SDL_winmm.c
@@ -27,6 +27,7 @@
 #include "../../core/windows/SDL_windows.h"
 #include <mmsystem.h>
 
+#include "SDL_assert.h"
 #include "SDL_timer.h"
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
@@ -152,30 +153,83 @@ WINMM_WaitDone(_THIS)
     } while (left > 0);
 }
 
+static int
+WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    const int nextbuf = this->hidden->next_buffer;
+    MMRESULT result;
+
+    SDL_assert(buflen == this->spec.size);
+
+    /* Wait for an audio chunk to finish */
+    WaitForSingleObject(this->hidden->audio_sem, INFINITE);
+
+    /* Copy it to caller's buffer... */
+    SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size);
+
+    /* requeue the buffer that just finished. */
+    result = waveInAddBuffer(this->hidden->hin,
+                             &this->hidden->wavebuf[nextbuf],
+                             sizeof (this->hidden->wavebuf[nextbuf]));
+    if (result != MMSYSERR_NOERROR) {
+        return -1;  /* uhoh! Disable the device. */
+    }
+
+    /* queue the next buffer in sequence, next time. */
+    this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
+    return this->spec.size;
+}
+
+static void
+WINMM_FlushCapture(_THIS)
+{
+    /* Wait for an audio chunk to finish */
+    if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) {
+        const int nextbuf = this->hidden->next_buffer;
+        /* requeue the buffer that just finished without reading from it. */
+        waveInAddBuffer(this->hidden->hin,
+                        &this->hidden->wavebuf[nextbuf],
+                        sizeof (this->hidden->wavebuf[nextbuf]));
+        this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
+    }
+}
+
 static void
 WINMM_CloseDevice(_THIS)
 {
     int i;
 
-    if (this->hidden->audio_sem) {
-        CloseHandle(this->hidden->audio_sem);
-    }
+    if (this->hidden->hout) {
+        waveOutReset(this->hidden->hout);
 
-    /* Clean up mixing buffers */
-    for (i = 0; i < NUM_BUFFERS; ++i) {
-        if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
-            waveOutUnprepareHeader(this->hidden->hout,
-                                   &this->hidden->wavebuf[i],
-                                   sizeof (this->hidden->wavebuf[i]));
+        /* Clean up mixing buffers */
+        for (i = 0; i < NUM_BUFFERS; ++i) {
+            if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
+                waveOutUnprepareHeader(this->hidden->hout,
+                                       &this->hidden->wavebuf[i],
+                                       sizeof (this->hidden->wavebuf[i]));
+            }
         }
+
+        waveOutClose(this->hidden->hout);
     }
 
     if (this->hidden->hin) {
+        waveInReset(this->hidden->hin);
+
+        /* Clean up mixing buffers */
+        for (i = 0; i < NUM_BUFFERS; ++i) {
+            if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
+                waveInUnprepareHeader(this->hidden->hin,
+                                       &this->hidden->wavebuf[i],
+                                       sizeof (this->hidden->wavebuf[i]));
+            }
+        }
         waveInClose(this->hidden->hin);
     }
 
-    if (this->hidden->hout) {
-        waveOutClose(this->hidden->hout);
+    if (this->hidden->audio_sem) {
+        CloseHandle(this->hidden->audio_sem);
     }
 
     SDL_free(this->hidden->mixbuf);
@@ -269,32 +323,44 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
                              CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            return SetMMerror("waveInOpen()", result);
+        }
     } else {
         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
                              CALLBACK_FUNCTION);
+        if (result != MMSYSERR_NOERROR) {
+            return SetMMerror("waveOutOpen()", result);
+        }
     }
 
-    if (result != MMSYSERR_NOERROR) {
-        return SetMMerror("waveOutOpen()", result);
-    }
 #ifdef SOUND_DEBUG
     /* Check the sound device we retrieved */
     {
-        WAVEOUTCAPS caps;
-
-        result = waveOutGetDevCaps((UINT) this->hidden->hout,
-                                   &caps, sizeof(caps));
-        if (result != MMSYSERR_NOERROR) {
-            return SetMMerror("waveOutGetDevCaps()", result);
+        if (iscapture) {
+            WAVEINCAPS caps;
+            result = waveInGetDevCaps((UINT) this->hidden->hout,
+                                      &caps, sizeof (caps));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInGetDevCaps()", result);
+            }
+            printf("Audio device: %s\n", caps.szPname);
+        } else {
+            WAVEOUTCAPS caps;
+            result = waveOutGetDevCaps((UINT) this->hidden->hout,
+                                       &caps, sizeof(caps));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveOutGetDevCaps()", result);
+            }
+            printf("Audio device: %s\n", caps.szPname);
         }
-        printf("Audio device: %s\n", caps.szPname);
     }
 #endif
 
     /* Create the audio buffer semaphore */
     this->hidden->audio_sem =
-        CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
+		CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
     if (this->hidden->audio_sem == NULL) {
         return SDL_SetError("Couldn't create semaphore");
     }
@@ -312,11 +378,35 @@ WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
         this->hidden->wavebuf[i].lpData =
             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
-        result = waveOutPrepareHeader(this->hidden->hout,
-                                      &this->hidden->wavebuf[i],
-                                      sizeof(this->hidden->wavebuf[i]));
+
+        if (iscapture) {
+            result = waveInPrepareHeader(this->hidden->hin,
+                                          &this->hidden->wavebuf[i],
+                                          sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInPrepareHeader()", result);
+            }
+
+            result = waveInAddBuffer(this->hidden->hin,
+                                     &this->hidden->wavebuf[i],
+                                     sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveInAddBuffer()", result);
+            }
+        } else {
+            result = waveOutPrepareHeader(this->hidden->hout,
+                                          &this->hidden->wavebuf[i],
+                                          sizeof(this->hidden->wavebuf[i]));
+            if (result != MMSYSERR_NOERROR) {
+                return SetMMerror("waveOutPrepareHeader()", result);
+            }
+        }
+    }
+
+    if (iscapture) {
+        result = waveInStart(this->hidden->hin);
         if (result != MMSYSERR_NOERROR) {
-            return SetMMerror("waveOutPrepareHeader()", result);
+            return SetMMerror("waveInStart()", result);
         }
     }
 
@@ -334,8 +424,12 @@ WINMM_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = WINMM_WaitDevice;
     impl->WaitDone = WINMM_WaitDone;
     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
+    impl->CaptureFromDevice = WINMM_CaptureFromDevice;
+    impl->FlushCapture = WINMM_FlushCapture;
     impl->CloseDevice = WINMM_CloseDevice;
 
+    impl->HasCaptureSupport = SDL_TRUE;
+
     return 1;   /* this audio target is available. */
 }