Browse Source

audio: More effort to improve and simplify audio resamplers.

Ryan C. Gordon 8 years ago
parent
commit
f12ab8f2b3
3 changed files with 123 additions and 157 deletions
  1. 2 3
      src/audio/SDL_audio_c.h
  2. 88 96
      src/audio/SDL_audiocvt.c
  3. 33 58
      src/audio/SDL_audiotypecvt.c

+ 2 - 3
src/audio/SDL_audio_c.h

@@ -50,9 +50,8 @@ void SDLCALL SDL_Convert_F32_to_S16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
 void SDLCALL SDL_Convert_F32_to_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
 void SDLCALL SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format);
 void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
+void SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels);
 void SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
-void SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels);
-void SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels);
-void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels);
+void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels);
 
 /* vi: set ts=4 sw=4 expandtab: */

+ 88 - 96
src/audio/SDL_audiocvt.c

@@ -331,67 +331,31 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
     return retval;
 }
 
-static int
-SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
-{
-    int retval = 0;
-
-    /* If we only built with the arbitrary resamplers, ignore multiples. */
-    int lo, hi;
-    int div;
-
-    SDL_assert(src_rate != 0);
-    SDL_assert(dst_rate != 0);
-    SDL_assert(src_rate != dst_rate);
-
-    if (src_rate < dst_rate) {
-        lo = src_rate;
-        hi = dst_rate;
-    } else {
-        lo = dst_rate;
-        hi = src_rate;
-    }
-
-    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
-    if ((hi % lo) != 0)
-        return 0;               /* not a multiple. */
-
-    div = hi / lo;
-    retval = ((div == 2) || (div == 4)) ? div : 0;
-
-    return retval;
-}
 
+/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
+   !!! FIXME:  channel info or integer sample rates, so we have to have
+   !!! FIXME:  function entry points for each supported channel count and
+   !!! FIXME:  multiple vs arbitrary. When we rev the ABI, remove this. */
 #define RESAMPLER_FUNCS(chans) \
     static void SDLCALL \
-    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
-        SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Upsample_Arbitrary(cvt, chans); \
-    }\
-    static void SDLCALL \
-    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
-        SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Downsample_Arbitrary(cvt, chans); \
-    } \
-    static void SDLCALL \
-    SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
+    SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
         SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Upsample_x2(cvt, chans); \
+        SDL_Upsample_Multiple(cvt, chans); \
     } \
     static void SDLCALL \
-    SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
+    SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
         SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Downsample_Multiple(cvt, 2, chans); \
-    } \
+        SDL_Upsample_Arbitrary(cvt, chans); \
+    }\
     static void SDLCALL \
-    SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
+    SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
         SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Upsample_x4(cvt, chans); \
+        SDL_Downsample_Multiple(cvt, chans); \
     } \
     static void SDLCALL \
-    SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
+    SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
         SDL_assert(format == AUDIO_F32SYS); \
-        SDL_Downsample_Multiple(cvt, 4, chans); \
+        SDL_Downsample_Arbitrary(cvt, chans); \
     }
 RESAMPLER_FUNCS(1)
 RESAMPLER_FUNCS(2)
@@ -401,61 +365,89 @@ RESAMPLER_FUNCS(8)
 #undef RESAMPLER_FUNCS
 
 static int
-SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
-                          int src_rate, int dst_rate)
+SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
 {
-    if (src_rate != dst_rate) {
-        const int upsample = (src_rate < dst_rate) ? 1 : 0;
-        const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
-        SDL_AudioFilter filter = NULL;
+    int lo, hi;
 
-        #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
-            case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
-            case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
-            case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
-            case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
-            case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
-            default: break; \
-        }
+    SDL_assert(src_rate != 0);
+    SDL_assert(dst_rate != 0);
+    SDL_assert(src_rate != dst_rate);
 
-        if (upsample) {
-            if (multiple == 0) {
-                PICK_CHANNEL_FILTER(Upsample, Arbitrary);
-            } else if (multiple == 2) {
-                PICK_CHANNEL_FILTER(Upsample, x2);
-            } else if (multiple == 4) {
-                PICK_CHANNEL_FILTER(Upsample, x4);
-            }
-        } else {
-            if (multiple == 0) {
-                PICK_CHANNEL_FILTER(Downsample, Arbitrary);
-            } else if (multiple == 2) {
-                PICK_CHANNEL_FILTER(Downsample, x2);
-            } else if (multiple == 4) {
-                PICK_CHANNEL_FILTER(Downsample, x4);
-            }
-        }
+    if (src_rate < dst_rate) {
+        lo = src_rate;
+        hi = dst_rate;
+    } else {
+        lo = dst_rate;
+        hi = src_rate;
+    }
 
-        #undef PICK_CHANNEL_FILTER
+    if ((hi % lo) != 0)
+        return 0;               /* not a multiple. */
 
-        if (filter == NULL) {
-            return SDL_SetError("No conversion available for these rates");
-        }
+    return hi / lo;
+}
 
-        /* Update (cvt) with filter details... */
-        cvt->filters[cvt->filter_index++] = filter;
-        if (src_rate < dst_rate) {
-            const double mult = ((double) dst_rate) / ((double) src_rate);
-            cvt->len_mult *= (int) SDL_ceil(mult);
-            cvt->len_ratio *= mult;
+static SDL_AudioFilter
+ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
+{
+    const int upsample = (src_rate < dst_rate) ? 1 : 0;
+    const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
+    SDL_AudioFilter filter = NULL;
+
+    #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
+        case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
+        case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
+        case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
+        case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
+        case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
+        default: break; \
+    }
+
+    if (upsample) {
+        if (multiple) {
+            PICK_CHANNEL_FILTER(Upsample, Multiple);
+        } else {
+            PICK_CHANNEL_FILTER(Upsample, Arbitrary);
+        }
+    } else {
+        if (multiple) {
+            PICK_CHANNEL_FILTER(Downsample, Multiple);
         } else {
-            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
+            PICK_CHANNEL_FILTER(Downsample, Arbitrary);
         }
+    }
+
+    #undef PICK_CHANNEL_FILTER
+
+    return filter;
+}
+
+static int
+SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
+                          const int src_rate, const int dst_rate)
+{
+    SDL_AudioFilter filter;
+
+    if (src_rate == dst_rate) {
+        return 0;  /* no conversion necessary. */
+    }
+
+    filter = ChooseResampler(dst_channels, src_rate, dst_rate);
+    if (filter == NULL) {
+        return SDL_SetError("No conversion available for these rates");
+    }
 
-        return 1;               /* added a converter. */
+    /* Update (cvt) with filter details... */
+    cvt->filters[cvt->filter_index++] = filter;
+    if (src_rate < dst_rate) {
+        const double mult = ((double) dst_rate) / ((double) src_rate);
+        cvt->len_mult *= (int) SDL_ceil(mult);
+        cvt->len_ratio *= mult;
+    } else {
+        cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
     }
 
-    return 0;                   /* no conversion necessary. */
+    return 1;               /* added a converter. */
 }
 
 
@@ -514,7 +506,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
 
        The expectation is we can process data faster in float32
        (possibly with SIMD), and making several passes over the same
-       buffer in is likely to be CPU cache-friendly, avoiding the
+       buffer is likely to be CPU cache-friendly, avoiding the
        biggest performance hit in modern times. Previously we had
        (script-generated) custom converters for every data type and
        it was a bloat on SDL compile times and final library size. */
@@ -585,11 +577,11 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
     }
 
     /* Do rate conversion, if necessary. Updates (cvt). */
-    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
-        -1) {
+    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) {
         return -1;              /* shouldn't happen, but just in case... */
     }
 
+    /* Move to final data type. */
     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
         return -1;              /* shouldn't happen, but just in case... */
     }

+ 33 - 58
src/audio/SDL_audiotypecvt.c

@@ -220,14 +220,14 @@ void
 SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 {
     const int srcsize = cvt->len_cvt - (64 * channels);
-    const int dstsize = (int) (((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr) * (channels*4);
+    const int dstsize = (int) ((((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr)) * (channels*4);
     register int eps = 0;
     float *dst = ((float *) (cvt->buf + dstsize)) - channels;
     const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
     const float *target = ((const float *) cvt->buf);
     const size_t cpy = sizeof (float) * channels;
-    float last_sample[8];
     float sample[8];
+    float last_sample[8];
     int i;
 
 #if DEBUG_CONVERT
@@ -236,7 +236,9 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 
     SDL_assert(channels <= 8);
 
-    SDL_memcpy(sample, src, cpy);
+    for (i = 0; i < channels; i++) {
+        sample[i] = (float) ((((double) src[i]) + ((double) src[i - channels])) * 0.5);
+    }
     SDL_memcpy(last_sample, src, cpy);
 
     while (dst > target) {
@@ -244,11 +246,15 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
         dst -= channels;
         eps += srcsize;
         if ((eps << 1) >= dstsize) {
-            src -= channels;
-            for (i = 0; i < channels; i++) {
-                sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
+            if (src > target) {
+                src -= channels;
+                for (i = 0; i < channels; i++) {
+                    sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
+                }
+            } else {
+
             }
-            SDL_memcpy(last_sample, sample, cpy);
+            SDL_memcpy(last_sample, src, cpy);
             eps -= dstsize;
         }
     }
@@ -291,7 +297,7 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
             for (i = 0; i < channels; i++) {
                 sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
             }
-            SDL_memcpy(last_sample, sample, cpy);
+            SDL_memcpy(last_sample, src, cpy);
             eps -= srcsize;
         }
     }
@@ -303,75 +309,43 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 }
 
 void
-SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels)
+SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels)
 {
-    const int dstsize = cvt->len_cvt * 2;
-    float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 2);
+    const int multiple = (int) cvt->rate_incr;
+    const int dstsize = cvt->len_cvt * multiple;
+    float *buf = (float *) cvt->buf;
+    float *dst = ((float *) (cvt->buf + dstsize)) - channels;
     const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
-    const float *target = ((const float *) cvt->buf);
+    const float *target = buf + channels;
     const size_t cpy = sizeof (float) * channels;
     float last_sample[8];
     int i;
 
 #if DEBUG_CONVERT
-    fprintf(stderr, "Upsample (x2), %d channels.\n", channels);
+    fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels);
 #endif
 
     SDL_assert(channels <= 8);
+
     SDL_memcpy(last_sample, src, cpy);
 
     while (dst > target) {
+        SDL_assert(src >= buf);
+
         for (i = 0; i < channels; i++) {
             dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
         }
         dst -= channels;
-        SDL_memcpy(dst, src, cpy);
-        SDL_memcpy(last_sample, src, cpy);
-        src -= channels;
-        dst -= channels;
-    }
-
-    cvt->len_cvt = dstsize;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
-}
 
-void
-SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels)
-{
-    const int dstsize = cvt->len_cvt * 4;
-    float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 4);
-    const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
-    const float *target = ((const float *) cvt->buf);
-    const size_t cpy = sizeof (float) * channels;
-    float last_sample[8];
-    int i;
-
-#if DEBUG_CONVERT
-    fprintf(stderr, "Upsample (x4), %d channels.\n", channels);
-#endif
-
-    SDL_assert(channels <= 8);
-    SDL_memcpy(last_sample, src, cpy);
-
-    while (dst > target) {
-        for (i = 0; i < channels; i++) {
-            dst[i] = (float) ((((double) src[i]) + (3.0 * ((double) last_sample[i]))) * 0.25);
-        }
-        dst -= channels;
-        for (i = 0; i < channels; i++) {
-            dst[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.25);
-        }
-        dst -= channels;
-        for (i = 0; i < channels; i++) {
-            dst[i] = (float) (((3.0 * ((double) src[i])) + ((double) last_sample[i])) * 0.25);
+        for (i = 1; i < multiple; i++) {
+            SDL_memcpy(dst, dst + channels, cpy);
+            dst -= channels;
         }
-        dst -= channels;
-        SDL_memcpy(dst, src, cpy);
-        dst -= channels;
-        SDL_memcpy(last_sample, src, cpy);
+
         src -= channels;
+        if (src > buf) {
+            SDL_memcpy(last_sample, src - channels, cpy);
+        }
     }
 
     cvt->len_cvt = dstsize;
@@ -381,8 +355,9 @@ SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels)
 }
 
 void
-SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels)
+SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels)
 {
+    const int multiple = (int) (1.0 / cvt->rate_incr);
     const int dstsize = cvt->len_cvt / multiple;
     float *dst = (float *) cvt->buf;
     const float *src = (float *) cvt->buf;