SDL: Rebuild full ResamplerFilter (left wing + right wing) at runtime

From 6a73f74b6ba9a457a17b84979b618b096b859b46 Mon Sep 17 00:00:00 2001
From: Brick <[EMAIL REDACTED]>
Date: Thu, 24 Aug 2023 00:18:52 +0100
Subject: [PATCH] Rebuild full ResamplerFilter (left wing + right wing) at
 runtime

---
 build-scripts/gen_audio_resampler_filter.c | 14 ++---
 src/audio/SDL_audio_resampler_filter.h     |  4 +-
 src/audio/SDL_audiocvt.c                   | 63 +++++++++++++++-------
 src/audio/SDL_audiotypecvt.c               |  5 ++
 4 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/build-scripts/gen_audio_resampler_filter.c b/build-scripts/gen_audio_resampler_filter.c
index b2ee5083bb64..19991bd964b1 100644
--- a/build-scripts/gen_audio_resampler_filter.c
+++ b/build-scripts/gen_audio_resampler_filter.c
@@ -50,7 +50,6 @@ gcc -o genfilter build-scripts/gen_audio_resampler_filter.c -lm && ./genfilter >
 #define RESAMPLER_BITS_PER_ZERO_CROSSING  ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
 #define RESAMPLER_SAMPLES_PER_ZERO_CROSSING  (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
 #define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
-#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)
 
 /* This is a "modified" bessel function, so you can't use POSIX j0() */
 static double
@@ -136,18 +135,13 @@ int main(void)
         "#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)\n"
         "#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)\n"
         "#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)\n"
-        "#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)\n"
         "\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE
     );
 
-    printf("static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {");
-    for (i = 0; i < RESAMPLER_TABLE_SIZE; i++) {
-        double v = 0.0;
-        if (i < RESAMPLER_FILTER_SIZE) {
-            j = (i % RESAMPLER_ZERO_CROSSINGS) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING + (i / RESAMPLER_ZERO_CROSSINGS);
-            v = ResamplerFilter[j];
-        }
-        printf("%s%12.9ff,", (i % RESAMPLER_ZERO_CROSSINGS) ? "" : "\n    ", v);
+    printf("static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {");
+    for (i = 0; i < RESAMPLER_FILTER_SIZE; i++) {
+        j = (i % RESAMPLER_ZERO_CROSSINGS) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING + (i / RESAMPLER_ZERO_CROSSINGS);
+        printf("%s%12.9ff,", (i % RESAMPLER_ZERO_CROSSINGS) ? "" : "\n    ", ResamplerFilter[j]);
     }
     printf("\n};\n\n");
 
diff --git a/src/audio/SDL_audio_resampler_filter.h b/src/audio/SDL_audio_resampler_filter.h
index b0097050a1de..1ea9c33ddec2 100644
--- a/src/audio/SDL_audio_resampler_filter.h
+++ b/src/audio/SDL_audio_resampler_filter.h
@@ -26,9 +26,8 @@
 #define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
 #define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
 #define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
-#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)
 
-static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {
+static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {
      1.000000000f, 0.000000000f,-0.000000000f, 0.000000000f,-0.000000000f,
      0.999993165f,-0.001679888f, 0.000529080f,-0.000151513f, 0.000027455f,
      0.999972661f,-0.003351212f, 0.001055794f,-0.000302183f, 0.000054683f,
@@ -541,6 +540,5 @@ static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {
      0.005090874f,-0.001601309f, 0.000459559f,-0.000083727f, 0.000003250f,
      0.003385399f,-0.001065208f, 0.000305539f,-0.000055591f, 0.000002140f,
      0.001688435f,-0.000531434f, 0.000152351f,-0.000027682f, 0.000001057f,
-     0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f,
 };
 
diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c
index e2549d6054a1..824e5a3103ad 100644
--- a/src/audio/SDL_audiocvt.c
+++ b/src/audio/SDL_audiocvt.c
@@ -84,11 +84,20 @@ static int GetHistoryBufferSampleFrames(const int required_resampler_frames)
 
 #define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
 
+#define RESAMPLER_FULL_FILTER_SIZE RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1)
+
 // TODO: Add SIMD-accelerated versions
-static void ResampleFrame(const float* src, float* dst, const float* filter, const int chans)
+static void ResampleFrame(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
 {
     int i, chan;
 
+    float filter[RESAMPLER_SAMPLES_PER_FRAME];
+
+    // Interpolate between the nearest two filters
+    for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
+        filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
+    }
+
     if (chans == 2) {
         float v0 = 0.0f;
         float v1 = 0.0f;
@@ -136,12 +145,40 @@ static void ResampleFrame(const float* src, float* dst, const float* filter, con
     }
 }
 
+static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
+
+void SDL_SetupAudioResampler()
+{
+    // Build a table combining the left and right wings, for faster access
+
+    int i, j;
+
+    for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
+        for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
+            int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
+            int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
+
+            float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
+            FullResamplerFilter[lwing] = value;
+            FullResamplerFilter[rwing] = value;
+        }
+    }
+
+    for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
+        int rwing = i + RESAMPLER_ZERO_CROSSINGS;
+        int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
+
+        FullResamplerFilter[lwing] = 0.0f;
+        FullResamplerFilter[rwing] = 0.0f;
+    }
+}
+
 static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes,
                          const Sint64 resample_rate, Sint64* resample_offset)
 {
     SDL_assert(resample_rate > 0);
     float *dst = outbuf;
-    int i, j;
+    int i;
 
     Sint64 srcpos = *resample_offset;
 
@@ -152,26 +189,12 @@ static void ResampleAudio(const int chans, const float *inbuf, const int inframe
 
         SDL_assert(srcindex >= -1 && srcindex < inframes);
 
-        const int filterindex = (int)(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_ZERO_CROSSINGS;
-
-        const float interpolation1 = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
-        const float interpolation2 = 1.0f - interpolation1;
-
-        float filter[RESAMPLER_SAMPLES_PER_FRAME];
-
-        for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
-            const int filt_ind1 = filterindex + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
-            const int filt_ind2 = (RESAMPLER_FILTER_SIZE - 1) - filt_ind1;
-
-            const float scale1 = (ResamplerFilter[filt_ind1] * interpolation2) + (ResamplerFilter[filt_ind1 + RESAMPLER_ZERO_CROSSINGS] * interpolation1);
-            const float scale2 = (ResamplerFilter[filt_ind2] * interpolation1) + (ResamplerFilter[filt_ind2 + RESAMPLER_ZERO_CROSSINGS] * interpolation2);
-
-            filter[j] = scale1;
-            filter[j + RESAMPLER_ZERO_CROSSINGS] = scale2;
-        }
+        const float* filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
+        const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
 
         const float* src = &inbuf[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
-        ResampleFrame(src, dst, filter, chans);
+        ResampleFrame(src, dst, filter, interp, chans);
+
         dst += chans;
     }
 
diff --git a/src/audio/SDL_audiotypecvt.c b/src/audio/SDL_audiotypecvt.c
index 416e9ab914cc..cbf19a998099 100644
--- a/src/audio/SDL_audiotypecvt.c
+++ b/src/audio/SDL_audiotypecvt.c
@@ -962,6 +962,8 @@ void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = N
 void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
 void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
 
+extern void SDL_SetupAudioResampler(void);
+
 void SDL_ChooseAudioConverters(void)
 {
     static SDL_bool converters_chosen = SDL_FALSE;
@@ -969,6 +971,9 @@ void SDL_ChooseAudioConverters(void)
         return;
     }
 
+    // FIXME: Hacks on top of hacks.
+    SDL_SetupAudioResampler();
+
 #define SET_CONVERTER_FUNCS(fntype) \
     SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
     SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \