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_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
 void SDLCALL SDL_Convert_F32_to_S32(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_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_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: */
 /* 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;
     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) \
 #define RESAMPLER_FUNCS(chans) \
     static void SDLCALL \
     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_assert(format == AUDIO_F32SYS); \
-        SDL_Upsample_x2(cvt, chans); \
+        SDL_Upsample_Multiple(cvt, chans); \
     } \
     } \
     static void SDLCALL \
     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_assert(format == AUDIO_F32SYS); \
-        SDL_Downsample_Multiple(cvt, 2, chans); \
-    } \
+        SDL_Upsample_Arbitrary(cvt, chans); \
+    }\
     static void SDLCALL \
     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_assert(format == AUDIO_F32SYS); \
-        SDL_Upsample_x4(cvt, chans); \
+        SDL_Downsample_Multiple(cvt, chans); \
     } \
     } \
     static void SDLCALL \
     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_assert(format == AUDIO_F32SYS); \
-        SDL_Downsample_Multiple(cvt, 4, chans); \
+        SDL_Downsample_Arbitrary(cvt, chans); \
     }
     }
 RESAMPLER_FUNCS(1)
 RESAMPLER_FUNCS(1)
 RESAMPLER_FUNCS(2)
 RESAMPLER_FUNCS(2)
@@ -401,61 +365,89 @@ RESAMPLER_FUNCS(8)
 #undef RESAMPLER_FUNCS
 #undef RESAMPLER_FUNCS
 
 
 static int
 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 {
         } 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
        The expectation is we can process data faster in float32
        (possibly with SIMD), and making several passes over the same
        (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
        biggest performance hit in modern times. Previously we had
        (script-generated) custom converters for every data type and
        (script-generated) custom converters for every data type and
        it was a bloat on SDL compile times and final library size. */
        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). */
     /* 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... */
         return -1;              /* shouldn't happen, but just in case... */
     }
     }
 
 
+    /* Move to final data type. */
     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
     if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
         return -1;              /* shouldn't happen, but just in case... */
         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)
 SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 {
 {
     const int srcsize = cvt->len_cvt - (64 * 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;
     register int eps = 0;
     float *dst = ((float *) (cvt->buf + dstsize)) - channels;
     float *dst = ((float *) (cvt->buf + dstsize)) - channels;
     const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
     const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
     const float *target = ((const float *) cvt->buf);
     const float *target = ((const float *) cvt->buf);
     const size_t cpy = sizeof (float) * channels;
     const size_t cpy = sizeof (float) * channels;
-    float last_sample[8];
     float sample[8];
     float sample[8];
+    float last_sample[8];
     int i;
     int i;
 
 
 #if DEBUG_CONVERT
 #if DEBUG_CONVERT
@@ -236,7 +236,9 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 
 
     SDL_assert(channels <= 8);
     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);
     SDL_memcpy(last_sample, src, cpy);
 
 
     while (dst > target) {
     while (dst > target) {
@@ -244,11 +246,15 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
         dst -= channels;
         dst -= channels;
         eps += srcsize;
         eps += srcsize;
         if ((eps << 1) >= dstsize) {
         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;
             eps -= dstsize;
         }
         }
     }
     }
@@ -291,7 +297,7 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
             for (i = 0; i < channels; i++) {
             for (i = 0; i < channels; i++) {
                 sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
                 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;
             eps -= srcsize;
         }
         }
     }
     }
@@ -303,75 +309,43 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
 }
 }
 
 
 void
 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 *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;
     const size_t cpy = sizeof (float) * channels;
     float last_sample[8];
     float last_sample[8];
     int i;
     int i;
 
 
 #if DEBUG_CONVERT
 #if DEBUG_CONVERT
-    fprintf(stderr, "Upsample (x2), %d channels.\n", channels);
+    fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels);
 #endif
 #endif
 
 
     SDL_assert(channels <= 8);
     SDL_assert(channels <= 8);
+
     SDL_memcpy(last_sample, src, cpy);
     SDL_memcpy(last_sample, src, cpy);
 
 
     while (dst > target) {
     while (dst > target) {
+        SDL_assert(src >= buf);
+
         for (i = 0; i < channels; i++) {
         for (i = 0; i < channels; i++) {
             dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
             dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
         }
         }
         dst -= channels;
         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;
         src -= channels;
+        if (src > buf) {
+            SDL_memcpy(last_sample, src - channels, cpy);
+        }
     }
     }
 
 
     cvt->len_cvt = dstsize;
     cvt->len_cvt = dstsize;
@@ -381,8 +355,9 @@ SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels)
 }
 }
 
 
 void
 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;
     const int dstsize = cvt->len_cvt / multiple;
     float *dst = (float *) cvt->buf;
     float *dst = (float *) cvt->buf;
     const float *src = (float *) cvt->buf;
     const float *src = (float *) cvt->buf;