SDL: audio: Simplified GetFirstAudioFormat/GetNextAudioFormat.

From e191bc8491c7c82a4a8638cc9ba8e5b94a31342b Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 2 May 2023 18:35:42 -0400
Subject: [PATCH] audio: Simplified GetFirstAudioFormat/GetNextAudioFormat.

Now it just returns an iterable array and needs no global state.
---
 src/audio/SDL_audio.c                      | 42 +++++-------
 src/audio/SDL_audio_c.h                    |  5 +-
 src/audio/alsa/SDL_alsa_audio.c            |  4 +-
 src/audio/android/SDL_androidaudio.c       |  4 +-
 src/audio/coreaudio/SDL_coreaudio.m        |  4 +-
 src/audio/directsound/SDL_directsound.c    |  4 +-
 src/audio/dsp/SDL_dspaudio.c               | 15 ++---
 src/audio/emscripten/SDL_emscriptenaudio.c |  4 +-
 src/audio/haiku/SDL_haikuaudio.cc          |  5 +-
 src/audio/n3ds/SDL_n3dsaudio.c             | 18 ++---
 src/audio/netbsd/SDL_netbsdaudio.c         |  4 +-
 src/audio/openslES/SDL_openslES.c          |  3 +-
 src/audio/pulseaudio/SDL_pulseaudio.c      |  4 +-
 src/audio/qnx/SDL_qsa_audio.c              | 77 +++++-----------------
 src/audio/sndio/SDL_sndioaudio.c           |  4 +-
 src/audio/vita/SDL_vitaaudio.c             |  4 +-
 src/audio/wasapi/SDL_wasapi.c              |  4 +-
 17 files changed, 80 insertions(+), 125 deletions(-)

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 238ea911f497..65729a1f617f 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1520,38 +1520,26 @@ void SDL_QuitAudio(void)
 }
 
 #define NUM_FORMATS 8
-static int format_idx;  /* !!! FIXME: whoa, why are there globals in use here?! */
-static int format_idx_sub;
-static SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS] = {
-    { SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB },
-    { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB },
-    { SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
-    { SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
-    { SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
-    { SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
-    { SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
-    { SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8 },
+static const SDL_AudioFormat format_list[NUM_FORMATS][NUM_FORMATS + 1] = {
+    { SDL_AUDIO_U8, SDL_AUDIO_S8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
+    { SDL_AUDIO_S8, SDL_AUDIO_U8, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, 0 },
+    { SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
+    { SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
+    { SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
+    { SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
+    { SDL_AUDIO_F32LSB, SDL_AUDIO_F32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
+    { SDL_AUDIO_F32MSB, SDL_AUDIO_F32LSB, SDL_AUDIO_S32MSB, SDL_AUDIO_S32LSB, SDL_AUDIO_S16MSB, SDL_AUDIO_S16LSB, SDL_AUDIO_U8, SDL_AUDIO_S8, 0 },
 };
 
-SDL_AudioFormat
-SDL_GetFirstAudioFormat(SDL_AudioFormat format)
+const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format)
 {
-    for (format_idx = 0; format_idx < NUM_FORMATS; ++format_idx) {
-        if (format_list[format_idx][0] == format) {
-            break;
+    int i;
+    for (i = 0; i < NUM_FORMATS; i++) {
+        if (format_list[i][0] == format) {
+            return &format_list[i][0];
         }
     }
-    format_idx_sub = 0;
-    return SDL_GetNextAudioFormat();
-}
-
-SDL_AudioFormat
-SDL_GetNextAudioFormat(void)
-{
-    if ((format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS)) {
-        return 0;
-    }
-    return format_list[format_idx][format_idx_sub++];
+    return &format_list[0][NUM_FORMATS]; /* not found; return what looks like a list with only a zero in it. */
 }
 
 Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format)
diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h
index a8a18adb3956..d69184a54cfb 100644
--- a/src/audio/SDL_audio_c.h
+++ b/src/audio/SDL_audio_c.h
@@ -35,9 +35,8 @@
 
 /* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */
 
-/* Functions to get a list of "close" audio formats */
-extern SDL_AudioFormat SDL_GetFirstAudioFormat(SDL_AudioFormat format);
-extern SDL_AudioFormat SDL_GetNextAudioFormat(void);
+/* Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. */
+const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format);
 
 /* Function to calculate the size and silence for a SDL_AudioSpec */
 extern Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format);
diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c
index 6916a20c6109..d63e727c81c5 100644
--- a/src/audio/alsa/SDL_alsa_audio.c
+++ b/src/audio/alsa/SDL_alsa_audio.c
@@ -527,6 +527,7 @@ static int ALSA_OpenDevice(_THIS, const char *devname)
     snd_pcm_sw_params_t *swparams = NULL;
     snd_pcm_format_t format = 0;
     SDL_AudioFormat test_format = 0;
+    const SDL_AudioFormat *closefmts;
     unsigned int rate = 0;
     unsigned int channels = 0;
 #ifdef SND_CHMAP_API_VERSION
@@ -569,7 +570,8 @@ static int ALSA_OpenDevice(_THIS, const char *devname)
     }
 
     /* Try for a closest match on audio format */
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         switch (test_format) {
         case SDL_AUDIO_U8:
             format = SND_PCM_FORMAT_U8;
diff --git a/src/audio/android/SDL_androidaudio.c b/src/audio/android/SDL_androidaudio.c
index 2aefd7b4a755..e0dd333293d0 100644
--- a/src/audio/android/SDL_androidaudio.c
+++ b/src/audio/android/SDL_androidaudio.c
@@ -38,6 +38,7 @@ static SDL_AudioDevice *captureDevice = NULL;
 static int ANDROIDAUDIO_OpenDevice(_THIS, const char *devname)
 {
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     SDL_bool iscapture = this->iscapture;
 
     if (iscapture) {
@@ -63,7 +64,8 @@ static int ANDROIDAUDIO_OpenDevice(_THIS, const char *devname)
         return SDL_OutOfMemory();
     }
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         if ((test_format == SDL_AUDIO_U8) ||
             (test_format == SDL_AUDIO_S16) ||
             (test_format == SDL_AUDIO_F32)) {
diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m
index daa1f8f88df8..44a11b1a035a 100644
--- a/src/audio/coreaudio/SDL_coreaudio.m
+++ b/src/audio/coreaudio/SDL_coreaudio.m
@@ -1008,6 +1008,7 @@ output device (in which case we'll try again). */
 static int COREAUDIO_OpenDevice(_THIS, const char *devname)
 {
     AudioStreamBasicDescription *strdesc;
+    const SDL_AudioFormat *closefmts;
     SDL_AudioFormat test_format;
     SDL_bool iscapture = this->iscapture;
     SDL_AudioDevice **new_open_devices;
@@ -1065,7 +1066,8 @@ static int COREAUDIO_OpenDevice(_THIS, const char *devname)
     strdesc->mSampleRate = this->spec.freq;
     strdesc->mFramesPerPacket = 1;
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         /* CoreAudio handles most of SDL's formats natively. */
         switch (test_format) {
         case SDL_AUDIO_U8:
diff --git a/src/audio/directsound/SDL_directsound.c b/src/audio/directsound/SDL_directsound.c
index 65b517ecbe5c..6bc7a1f0fa6b 100644
--- a/src/audio/directsound/SDL_directsound.c
+++ b/src/audio/directsound/SDL_directsound.c
@@ -485,6 +485,7 @@ static int DSOUND_OpenDevice(_THIS, const char *devname)
     SDL_bool tried_format = SDL_FALSE;
     SDL_bool iscapture = this->iscapture;
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     LPGUID guid = (LPGUID)this->handle;
     DWORD bufsize;
 
@@ -514,7 +515,8 @@ static int DSOUND_OpenDevice(_THIS, const char *devname)
         }
     }
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         switch (test_format) {
         case SDL_AUDIO_U8:
         case SDL_AUDIO_S16:
diff --git a/src/audio/dsp/SDL_dspaudio.c b/src/audio/dsp/SDL_dspaudio.c
index d76be31a4952..174202811b88 100644
--- a/src/audio/dsp/SDL_dspaudio.c
+++ b/src/audio/dsp/SDL_dspaudio.c
@@ -58,10 +58,11 @@ static int DSP_OpenDevice(_THIS, const char *devname)
 {
     SDL_bool iscapture = this->iscapture;
     const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
-    int format;
+    int format = 0;
     int value;
     int frag_spec;
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
 
     /* We don't care what the devname is...we'll try to open anything. */
     /*  ...but default to first name in the list... */
@@ -112,9 +113,8 @@ static int DSP_OpenDevice(_THIS, const char *devname)
     }
 
     /* Try for a closest match on audio format */
-    format = 0;
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format);
-         !format && test_format;) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
 #ifdef DEBUG_AUDIO
         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
 #endif
@@ -146,12 +146,9 @@ static int DSP_OpenDevice(_THIS, const char *devname)
             break;
 #endif
         default:
-            format = 0;
-            break;
-        }
-        if (!format) {
-            test_format = SDL_GetNextAudioFormat();
+            continue;
         }
+        break;
     }
     if (format == 0) {
         return SDL_SetError("Couldn't find any hardware audio formats");
diff --git a/src/audio/emscripten/SDL_emscriptenaudio.c b/src/audio/emscripten/SDL_emscriptenaudio.c
index ac3f3306c9a5..1677ae5b69d2 100644
--- a/src/audio/emscripten/SDL_emscriptenaudio.c
+++ b/src/audio/emscripten/SDL_emscriptenaudio.c
@@ -199,6 +199,7 @@ static void EMSCRIPTENAUDIO_CloseDevice(_THIS)
 static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
 {
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     SDL_bool iscapture = this->iscapture;
     int result;
 
@@ -235,7 +236,8 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
         return SDL_SetError("Web Audio API is not available!");
     }
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         switch (test_format) {
         case SDL_AUDIO_F32: /* web audio only supports floats */
             break;
diff --git a/src/audio/haiku/SDL_haikuaudio.cc b/src/audio/haiku/SDL_haikuaudio.cc
index e21ff0363c4d..2f5499601bd3 100644
--- a/src/audio/haiku/SDL_haikuaudio.cc
+++ b/src/audio/haiku/SDL_haikuaudio.cc
@@ -119,6 +119,7 @@ static int HAIKUAUDIO_OpenDevice(_THIS, const char *devname)
 {
     media_raw_audio_format format;
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
 
     /* Initialize all variables that we clean on shutdown */
     _this->hidden = new SDL_PrivateAudioData;
@@ -132,7 +133,9 @@ static int HAIKUAUDIO_OpenDevice(_THIS, const char *devname)
     format.byte_order = B_MEDIA_LITTLE_ENDIAN;
     format.frame_rate = (float) _this->spec.freq;
     format.channel_count = _this->spec.channels;        /* !!! FIXME: support > 2? */
-    for (test_format = SDL_GetFirstAudioFormat(_this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+
+    closefmts = SDL_ClosestAudioFormats(_this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         switch (test_format) {
         case SDL_AUDIO_S8:
             format.format = media_raw_audio_format::B_AUDIO_CHAR;
diff --git a/src/audio/n3ds/SDL_n3dsaudio.c b/src/audio/n3ds/SDL_n3dsaudio.c
index 075aa5e28bde..4cd0b6e4522e 100644
--- a/src/audio/n3ds/SDL_n3dsaudio.c
+++ b/src/audio/n3ds/SDL_n3dsaudio.c
@@ -310,10 +310,9 @@ static void FreePrivateData(_THIS)
 
 static int FindAudioFormat(_THIS)
 {
-    SDL_bool found_valid_format = SDL_FALSE;
-    Uint16 test_format = SDL_GetFirstAudioFormat(this->spec.format);
-
-    while (!found_valid_format && test_format) {
+    SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         this->spec.format = test_format;
         switch (test_format) {
         case SDL_AUDIO_S8:
@@ -321,22 +320,17 @@ static int FindAudioFormat(_THIS)
             this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
             this->hidden->isSigned = 1;
             this->hidden->bytePerSample = this->spec.channels;
-            found_valid_format = SDL_TRUE;
-            break;
+            return 0;
         case SDL_AUDIO_S16:
             /* Signed 16-bit audio supported */
             this->hidden->format = (this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
             this->hidden->isSigned = 1;
             this->hidden->bytePerSample = this->spec.channels * 2;
-            found_valid_format = SDL_TRUE;
-            break;
-        default:
-            test_format = SDL_GetNextAudioFormat();
-            break;
+            return 0;
         }
     }
 
-    return found_valid_format ? 0 : -1;
+    return -1;
 }
 
 #endif /* SDL_AUDIO_DRIVER_N3DS */
diff --git a/src/audio/netbsd/SDL_netbsdaudio.c b/src/audio/netbsd/SDL_netbsdaudio.c
index e9503f20b5a9..167f434fc9b0 100644
--- a/src/audio/netbsd/SDL_netbsdaudio.c
+++ b/src/audio/netbsd/SDL_netbsdaudio.c
@@ -194,6 +194,7 @@ static int NETBSDAUDIO_OpenDevice(_THIS, const char *devname)
 {
     SDL_bool iscapture = this->iscapture;
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     int encoding = AUDIO_ENCODING_NONE;
     audio_info_t info, hwinfo;
     struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play;
@@ -235,7 +236,8 @@ static int NETBSDAUDIO_OpenDevice(_THIS, const char *devname)
     prinfo->sample_rate = this->spec.freq;
     prinfo->channels = this->spec.channels;
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         switch (test_format) {
         case SDL_AUDIO_U8:
             encoding = AUDIO_ENCODING_ULINEAR;
diff --git a/src/audio/openslES/SDL_openslES.c b/src/audio/openslES/SDL_openslES.c
index 58b475444415..a580832a503a 100644
--- a/src/audio/openslES/SDL_openslES.c
+++ b/src/audio/openslES/SDL_openslES.c
@@ -417,8 +417,9 @@ static int openslES_CreatePCMPlayer(_THIS)
         https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
     */
     if (SDL_GetAndroidSDKVersion() >= 21) {
+        const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(this->spec.format);
         SDL_AudioFormat test_format;
-        for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+        while ((test_format = *(closefmts++)) != 0) {
             if (SDL_AUDIO_ISSIGNED(test_format)) {
                 break;
             }
diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index 56e90d07be31..cb71bfebadd6 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -519,6 +519,7 @@ static int PULSEAUDIO_OpenDevice(_THIS, const char *devname)
 {
     struct SDL_PrivateAudioData *h = NULL;
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     pa_sample_spec paspec;
     pa_buffer_attr paattr;
     pa_channel_map pacmap;
@@ -536,7 +537,8 @@ static int PULSEAUDIO_OpenDevice(_THIS, const char *devname)
     SDL_zerop(this->hidden);
 
     /* Try for a closest match on audio format */
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
 #ifdef DEBUG_AUDIO
         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
 #endif
diff --git a/src/audio/qnx/SDL_qsa_audio.c b/src/audio/qnx/SDL_qsa_audio.c
index 9501fc2c9d64..dca73b874884 100644
--- a/src/audio/qnx/SDL_qsa_audio.c
+++ b/src/audio/qnx/SDL_qsa_audio.c
@@ -269,7 +269,7 @@ QSA_OpenDevice(_THIS, const char *devname)
     int status = 0;
     int format = 0;
     SDL_AudioFormat test_format = 0;
-    int found = 0;
+    const SDL_AudioFormat *closefmts;
     snd_pcm_channel_setup_t csetup;
     snd_pcm_channel_params_t cparams;
     SDL_bool iscapture = this->iscapture;
@@ -312,70 +312,23 @@ QSA_OpenDevice(_THIS, const char *devname)
     }
 
     /* Try for a closest match on audio format */
-    format = 0;
-    /* can't use format as SND_PCM_SFMT_U8 = 0 in qsa */
-    found = 0;
-
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); !found;) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         /* if match found set format to equivalent QSA format */
         switch (test_format) {
-        case SDL_AUDIO_U8:
-            {
-                format = SND_PCM_SFMT_U8;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_S8:
-            {
-                format = SND_PCM_SFMT_S8;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_S16LSB:
-            {
-                format = SND_PCM_SFMT_S16_LE;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_S16MSB:
-            {
-                format = SND_PCM_SFMT_S16_BE;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_S32LSB:
-            {
-                format = SND_PCM_SFMT_S32_LE;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_S32MSB:
-            {
-                format = SND_PCM_SFMT_S32_BE;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_F32LSB:
-            {
-                format = SND_PCM_SFMT_FLOAT_LE;
-                found = 1;
-            }
-            break;
-        case SDL_AUDIO_F32MSB:
-            {
-                format = SND_PCM_SFMT_FLOAT_BE;
-                found = 1;
-            }
-            break;
-        default:
-            {
-                break;
-            }
-        }
-
-        if (!found) {
-            test_format = SDL_GetNextAudioFormat();
+        #define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: format = SND_PCM_SFMT_##qsafmt; break
+        CHECKFMT(U8, U8);
+        CHECKFMT(S8, S8);
+        CHECKFMT(S16LSB, S16_LE);
+        CHECKFMT(S16MSB, S16_BE);
+        CHECKFMT(S32LSB, S32_LE);
+        CHECKFMT(S32MSB, S32_BE);
+        CHECKFMT(F32LSB, FLOAT_LE);
+        CHECKFMT(F32MSB, FLOAT_BE);
+        #undef CHECKFMT
+        default: continue;
         }
+        break;
     }
 
     /* assumes test_format not 0 on success */
diff --git a/src/audio/sndio/SDL_sndioaudio.c b/src/audio/sndio/SDL_sndioaudio.c
index 23a7f3704ecf..c192154afd6a 100644
--- a/src/audio/sndio/SDL_sndioaudio.c
+++ b/src/audio/sndio/SDL_sndioaudio.c
@@ -223,6 +223,7 @@ static void SNDIO_CloseDevice(_THIS)
 static int SNDIO_OpenDevice(_THIS, const char *devname)
 {
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
     struct sio_par par;
     SDL_bool iscapture = this->iscapture;
 
@@ -258,7 +259,8 @@ static int SNDIO_OpenDevice(_THIS, const char *devname)
     par.appbufsz = par.round * 2;
 
     /* Try for a closest match on audio format */
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         if (!SDL_AUDIO_ISFLOAT(test_format)) {
             par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
             par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
diff --git a/src/audio/vita/SDL_vitaaudio.c b/src/audio/vita/SDL_vitaaudio.c
index 401c397d0080..cdc98a8c7740 100644
--- a/src/audio/vita/SDL_vitaaudio.c
+++ b/src/audio/vita/SDL_vitaaudio.c
@@ -61,6 +61,7 @@ static int VITAAUD_OpenDevice(_THIS, const char *devname)
     int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN;
     int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME };
     SDL_AudioFormat test_format;
+    const SDL_AudioFormat *closefmts;
 
     this->hidden = (struct SDL_PrivateAudioData *)
         SDL_malloc(sizeof(*this->hidden));
@@ -69,7 +70,8 @@ static int VITAAUD_OpenDevice(_THIS, const char *devname)
     }
     SDL_memset(this->hidden, 0, sizeof(*this->hidden));
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         if (test_format == SDL_AUDIO_S16LSB) {
             this->spec.format = test_format;
             break;
diff --git a/src/audio/wasapi/SDL_wasapi.c b/src/audio/wasapi/SDL_wasapi.c
index 56da8fb7d355..e482463cbd89 100644
--- a/src/audio/wasapi/SDL_wasapi.c
+++ b/src/audio/wasapi/SDL_wasapi.c
@@ -397,6 +397,7 @@ int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
     WAVEFORMATEX *waveformat = NULL;
     SDL_AudioFormat test_format;
     SDL_AudioFormat wasapi_format = 0;
+    const SDL_AudioFormat *closefmts;
     HRESULT ret = S_OK;
     DWORD streamflags = 0;
 
@@ -425,7 +426,8 @@ int WASAPI_PrepDevice(_THIS, const SDL_bool updatestream)
     /* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
     wasapi_format = WaveFormatToSDLFormat(waveformat);
 
-    for (test_format = SDL_GetFirstAudioFormat(this->spec.format); test_format; test_format = SDL_GetNextAudioFormat()) {
+    closefmts = SDL_ClosestAudioFormats(this->spec.format);
+    while ((test_format = *(closefmts++)) != 0) {
         if (test_format == wasapi_format) {
             this->spec.format = test_format;
             break;

Hello,

I’m testing alsa code, and I have some questions. Before to create a new issue, i think this commit is relevant and has something to do with my problem. Please correct me if I’m wrong, and I’ll file a new issue.

Context : Linux Intel (amd64), using alsa + SDL2 (2.28 from the repo) + ffmpeg (C++ api) + OpenCV, to record audio + video from a webcam (Logitech C920). Video works, means convert cv:Mat into AVFrames is ok since a long time, but not audio (convert PCM from webcam into audio AVFrame).

Currently I can record audio+video using some hacks, but I’m not satisfied (crackling, noise, and .mp4 broken). So I’m learning the SDL2 code, here alsa driver to -try to- understand what exactly happens.

My questions :

  1. Are the changes above included in SDL2-2.28 ?

  2. why are we forced to initialize the sound card using SND_PCM_FORMAT_U8 instead of other like SND_PCM_FORMAT_S16_LE or even SND_PCM_FORMAT_S32_LE ? Doing the following changes (see below) looks like it works. The log says:

INFO: Using audio driver: alsa
NUM_FORMATS = 10, format_idx = 0, format_idx_sub = 0
NUM_FORMATS = 10, format_idx = 0, format_idx_sub = 1
NUM_FORMATS = 10, format_idx = 0, format_idx_sub = 2
ALSA: test_format = 0x8010 SND_PCM_FORMAT_S16_LE = 2, SND_PCM_FORMAT_S32_LE = 10

The diff:

diff -Naur old/audio/alsa/SDL_alsa_audio.c new/audio/alsa/SDL_alsa_audio.c
--- old/audio/alsa/SDL_alsa_audio.c	2023-07-21 21:24:48.054831581 +0200
+++ new/audio/alsa/SDL_alsa_audio.c	2023-07-21 21:23:21.961587180 +0200
@@ -571,9 +571,17 @@
         return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status));
     }
 
+    /* ericb // Set SND_PCM_FORMAT_S16_LE */
+    status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams,
+                                               SND_PCM_FORMAT_S16_LE);
+    if (status < 0) {
+        return SDL_SetError("ALSA: Couldn't set SND_PCM_FORMAT_S16_LE: %s", ALSA_snd_strerror(status));
+    }
+
     /* Try for a closest match on audio format */
     for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
         switch (test_format) {
+
         case AUDIO_U8:
             format = SND_PCM_FORMAT_U8;
             break;
@@ -614,8 +622,13 @@
     if (!test_format) {
         return SDL_SetError("%s: Unsupported audio format", "alsa");
     }
+    else
+        printf("ALSA: test_format = 0x%x SND_PCM_FORMAT_S16_LE = %d, SND_PCM_FORMAT_S32_LE = %d\n", test_format, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S32_LE);
+
     this->spec.format = test_format;
 
+
+
     /* Validate number of channels and determine if swizzling is necessary
      * Assume original swizzling, until proven otherwise.
      */

Second change, I tested other formats, and I obtain ;

diff -Naur old/audio/alsa/SDL_alsa_audio.c new/audio/alsa/SDL_alsa_audio.c
--- old/audio/alsa/SDL_alsa_audio.c	2023-07-21 21:24:48.054831581 +0200
+++ new/audio/alsa/SDL_alsa_audio.c	2023-07-21 22:41:02.335448114 +0200
@@ -571,9 +571,22 @@
         return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status));
     }
 
+    /* ericb // Set SND_PCM_FORMAT_FLOAT_LE */
+    status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams,
+                                               SND_PCM_FORMAT_FLOAT_LE);
+    if (status < 0) {
+        printf("SND_PCM_FORMAT_FLOAT_LE does not work. testing an alternative : SND_PCM_FORMAT_S16_LE\n");
+
+        status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams,
+                                               SND_PCM_FORMAT_S16_LE);
+        if (status < 0) {
+            return SDL_SetError("ALSA: Couldn't set SND_PCM_FORMAT_S16_LE: %s", ALSA_snd_strerror(status));
+        }
+    }
     /* Try for a closest match on audio format */
     for (test_format = SDL_FirstAudioFormat(this->spec.format); test_format; test_format = SDL_NextAudioFormat()) {
         switch (test_format) {
+
         case AUDIO_U8:
             format = SND_PCM_FORMAT_U8;
             break;
@@ -614,8 +627,13 @@
     if (!test_format) {
         return SDL_SetError("%s: Unsupported audio format", "alsa");
     }
+    else
+        printf("ALSA: test_format = 0x%x SND_PCM_FORMAT_S16_LE = %d, SND_PCM_FORMAT_S32_LE = %d\n", test_format, SND_PCM_FORMAT_FLOAT_LE, SND_PCM_FORMAT_FLOAT_BE);
+
     this->spec.format = test_format;
 
+
+
     /* Validate number of channels and determine if swizzling is necessary
      * Assume original swizzling, until proven otherwise.
      */

The sound is plain wrong, because I need to modifiy the types accordingly, but the log says interesting things:

INFO: Using audio driver: alsa
ALSA: test_format = 0x8120 SND_PCM_FORMAT_S16_LE = 14, SND_PCM_FORMAT_S32_LE = 15
ERROR: ALSA: period size = 128, periods = 3, buffer size = 384
SDL_OpenAudioDevice() done. input_dev = 2
nb_samples = 1152 
Output #0, avi, to 'a.avi':
  Stream #0:0: Video: mpeg4, yuv420p, 1280x720, q=2-31, 3000 kb/s, 24 tbn
  Stream #0:1: Audio: mp3, 44100 Hz, stereo, fltp

  1. would you mind to summarize me what works and what does not yet recording on Linux with ALSA driver + SDL2 ?

  2. Last but not least, does SDL_NextAudioFormat() work as expected ?

Thanks in advance and apologies if my investigations are plain wrong, but I’d like to understand why. To tell you more, I can record very easely any .wav using alsa API directly and I don’t understand why it does not work with alsa SDL2 implementation.

Thanks in advance for reading me.


ericb