SDL: test: testaudiocapture is updated for the SDL3 audio API.

From e1c78718d4b678111b745841757f7fe3e0e863eb Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 24 Jun 2023 14:57:12 -0400
Subject: [PATCH] test: testaudiocapture is updated for the SDL3 audio API.

---
 test/testaudiocapture.c | 107 +++++++++++++++++++++++-----------------
 1 file changed, 62 insertions(+), 45 deletions(-)

diff --git a/test/testaudiocapture.c b/test/testaudiocapture.c
index 01c267608c2b..87ee24c4b087 100644
--- a/test/testaudiocapture.c
+++ b/test/testaudiocapture.c
@@ -22,13 +22,14 @@
 
 static SDL_Window *window = NULL;
 static SDL_Renderer *renderer = NULL;
-static SDL_AudioSpec spec;
-static SDL_AudioDeviceID devid_in = 0;
-static SDL_AudioDeviceID devid_out = 0;
+static SDL_AudioStream *stream_in = NULL;
+static SDL_AudioStream *stream_out = NULL;
 static int done = 0;
 
 static void loop(void)
 {
+    const SDL_AudioDeviceID devid_in = SDL_GetAudioStreamBinding(stream_in);
+    const SDL_AudioDeviceID devid_out = SDL_GetAudioStreamBinding(stream_out);
     SDL_bool please_quit = SDL_FALSE;
     SDL_Event e;
 
@@ -42,17 +43,18 @@ static void loop(void)
         } else if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
             if (e.button.button == 1) {
                 SDL_PauseAudioDevice(devid_out);
-                SDL_PlayAudioDevice(devid_in);
+                SDL_UnpauseAudioDevice(devid_in);
             }
         } else if (e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
             if (e.button.button == 1) {
                 SDL_PauseAudioDevice(devid_in);
-                SDL_PlayAudioDevice(devid_out);
+                SDL_FlushAudioStream(stream_in);  /* so no samples are held back for resampling purposes. */
+                SDL_UnpauseAudioDevice(devid_out);
             }
         }
     }
 
-    if (SDL_GetAudioDeviceStatus(devid_in) == SDL_AUDIO_PLAYING) {
+    if (!SDL_IsAudioDevicePaused(devid_in)) {
         SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
     } else {
         SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
@@ -60,13 +62,26 @@ static void loop(void)
     SDL_RenderClear(renderer);
     SDL_RenderPresent(renderer);
 
+    /* Feed any new data we captured to the output stream. It'll play when we unpause the device. */
+    while (!please_quit && (SDL_GetAudioStreamAvailable(stream_in) > 0)) {
+        Uint8 buf[1024];
+        const int br = SDL_GetAudioStreamData(stream_in, buf, sizeof(buf));
+        if (br < 0) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read from input audio stream: %s\n", SDL_GetError());
+            please_quit = 1;
+        } else if (SDL_PutAudioStreamData(stream_out, buf, br) < 0) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to write to output audio stream: %s\n", SDL_GetError());
+            please_quit = 1;
+        }
+    }
+
     if (please_quit) {
         /* stop playing back, quit. */
         SDL_Log("Shutting down.\n");
-        SDL_PauseAudioDevice(devid_in);
         SDL_CloseAudioDevice(devid_in);
-        SDL_PauseAudioDevice(devid_out);
         SDL_CloseAudioDevice(devid_out);
+        SDL_DestroyAudioStream(stream_in);
+        SDL_DestroyAudioStream(stream_out);
         SDL_DestroyRenderer(renderer);
         SDL_DestroyWindow(window);
         SDL_Quit();
@@ -77,28 +92,17 @@ static void loop(void)
         done = 1;
         return;
     }
-
-    /* Note that it would be easier to just have a one-line function that
-        calls SDL_QueueAudio() as a capture device callback, but we're
-        trying to test the API, so we use SDL_DequeueAudio() here. */
-    while (SDL_TRUE) {
-        Uint8 buf[1024];
-        const Uint32 br = SDL_DequeueAudio(devid_in, buf, sizeof(buf));
-        SDL_QueueAudio(devid_out, buf, br);
-        if (br < sizeof(buf)) {
-            break;
-        }
-    }
 }
 
 int main(int argc, char **argv)
 {
-    /* (NULL means "open default device.") */
+    SDL_AudioDeviceID *devices;
+    SDLTest_CommonState *state;
+    SDL_AudioSpec spec;
+    SDL_AudioDeviceID device;
+    SDL_AudioDeviceID want_device = SDL_AUDIO_DEVICE_DEFAULT_CAPTURE;
     const char *devname = NULL;
-    SDL_AudioSpec wanted;
-    int devcount;
     int i;
-    SDLTest_CommonState *state;
 
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, 0);
@@ -121,7 +125,7 @@ int main(int argc, char **argv)
             }
         }
         if (consumed <= 0) {
-            static const char *options[] = { "[driver_name]", NULL };
+            static const char *options[] = { "[device_name]", NULL };
             SDLTest_CommonLogUsage(state, argv[0], options);
             exit(1);
         }
@@ -145,19 +149,20 @@ int main(int argc, char **argv)
 
     SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());
 
-    devcount = SDL_GetNumAudioDevices(SDL_TRUE);
-    for (i = 0; i < devcount; i++) {
-        SDL_Log(" Capture device #%d: '%s'\n", i, SDL_GetAudioDeviceName(i, SDL_TRUE));
+    devices = SDL_GetAudioCaptureDevices(NULL);
+    for (i = 0; devices[i] != 0; i++) {
+        char *name = SDL_GetAudioDeviceName(devices[i]);
+        SDL_Log(" Capture device #%d: '%s'\n", i, name);
+        if (devname && (SDL_strcmp(devname, name) == 0)) {
+            want_device = devices[i];
+        }
+        SDL_free(name);
     }
 
-    SDL_zero(wanted);
-    wanted.freq = 44100;
-    wanted.format = SDL_AUDIO_F32SYS;
-    wanted.channels = 1;
-    wanted.samples = 4096;
-    wanted.callback = NULL;
-
-    SDL_zero(spec);
+    if (devname && (want_device == SDL_AUDIO_DEVICE_DEFAULT_CAPTURE)) {
+        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Didn't see a capture device named '%s', using the system default instead.\n", devname);
+        devname = NULL;
+    }
 
     /* DirectSound can fail in some instances if you open the same hardware
        for both capture and output and didn't open the output end first,
@@ -166,24 +171,40 @@ int main(int argc, char **argv)
        circumstances. */
 
     SDL_Log("Opening default playback device...\n");
-    devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE);
-    if (!devid_out) {
+    device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, NULL);
+    if (!device) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for playback: %s!\n", SDL_GetError());
         SDL_Quit();
         exit(1);
     }
+    SDL_PauseAudioDevice(device);
+    SDL_GetAudioDeviceFormat(device, &spec);
+    stream_out = SDL_CreateAndBindAudioStream(device, &spec);
+    if (!stream_out) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!\n", SDL_GetError());
+        SDL_Quit();
+        exit(1);
+    }
 
     SDL_Log("Opening capture device %s%s%s...\n",
             devname ? "'" : "",
             devname ? devname : "[[default]]",
             devname ? "'" : "");
 
-    devid_in = SDL_OpenAudioDevice(devname, SDL_TRUE, &spec, &spec, 0);
-    if (!devid_in) {
+    device = SDL_OpenAudioDevice(want_device, NULL);
+    if (!device) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for capture: %s!\n", SDL_GetError());
         SDL_Quit();
         exit(1);
     }
+    SDL_PauseAudioDevice(device);
+    SDL_GetAudioDeviceFormat(device, &spec);
+    stream_in = SDL_CreateAndBindAudioStream(device, &spec);
+    if (!stream_in) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for capture: %s!\n", SDL_GetError());
+        SDL_Quit();
+        exit(1);
+    }
 
     SDL_Log("Ready! Hold down mouse or finger to record!\n");
 
@@ -198,11 +219,7 @@ int main(int argc, char **argv)
     }
 #endif
 
-    /* SDL_DestroyRenderer(renderer); */
-    /* SDL_DestroyWindow(window); */
-
-    /* SDL_Quit(); */
-    /* SDLTest_CommonDestroyState(state); */
+    SDLTest_CommonDestroyState(state);
 
     return 0;
 }