diff src/audio/SDL_audiocvt.c @ 3021:f3dcf04412cf

First shot at new audio resampling code.
author Ryan C. Gordon <icculus@icculus.org>
date Sun, 11 Jan 2009 04:46:42 +0000
parents 7e30c2dc7783
children 77c3e67f0740
line wrap: on
line diff
--- a/src/audio/SDL_audiocvt.c	Sun Jan 11 04:46:14 2009 +0000
+++ b/src/audio/SDL_audiocvt.c	Sun Jan 11 04:46:42 2009 +0000
@@ -26,37 +26,12 @@
 #include "SDL_audio.h"
 #include "SDL_audio_c.h"
 
-//#define DEBUG_CONVERT
+/* #define DEBUG_CONVERT */
 
-/* These are fractional multiplication routines. That is, their inputs
-   are two numbers in the range [-1, 1) and the result falls in that
-   same range. The output is the same size as the inputs, i.e.
-   32-bit x 32-bit = 32-bit.
- */
-
-/* We hope here that the right shift includes sign extension */
-#ifdef SDL_HAS_64BIT_Type
-#define SDL_FixMpy32(a, b) ((((Sint64)a * (Sint64)b) >> 31) & 0xffffffff)
-#else
-/* If we don't have the 64-bit type, do something more complicated. See http://www.8052.com/mul16.phtml or http://www.cs.uaf.edu/~cs301/notes/Chapter5/node5.html */
-#define SDL_FixMpy32(a, b) ((((Sint64)a * (Sint64)b) >> 31) & 0xffffffff)
+/* !!! FIXME */
+#ifndef assert
+#define assert(x)
 #endif
-#define SDL_FixMpy16(a, b) ((((Sint32)a * (Sint32)b) >> 14) & 0xffff)
-#define SDL_FixMpy8(a, b) ((((Sint16)a * (Sint16)b) >> 7) & 0xff)
-/* This macro just makes the floating point filtering code not have to be a special case. */
-#define SDL_FloatMpy(a, b) (a * b)
-
-/* These macros take floating point numbers in the range [-1.0f, 1.0f) and
-   represent them as fixed-point numbers in that same range. There's no
-   checking that the floating point argument is inside the appropriate range.
- */
-
-#define SDL_Make_1_7(a) (Sint8)(a * 128.0f)
-#define SDL_Make_1_15(a) (Sint16)(a * 32768.0f)
-#define SDL_Make_1_31(a) (Sint32)(a * 2147483648.0f)
-#define SDL_Make_2_6(a) (Sint8)(a * 64.0f)
-#define SDL_Make_2_14(a) (Sint16)(a * 16384.0f)
-#define SDL_Make_2_30(a) (Sint32)(a * 1073741824.0f)
 
 /* Effectively mix right and left channels into a single channel */
 static void SDLCALL
@@ -798,472 +773,13 @@
     }
 }
 
-/* Convert rate up by multiple of 2 */
-static void SDLCALL
-SDL_RateMUL2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate * 2 (mono)\n");
-#endif
-
-#define mul2_mono(type) { \
-        const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
-        type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \
-        for (i = cvt->len_cvt / sizeof (type); i; --i) { \
-            src--; \
-            dst[-1] = dst[-2] = src[0]; \
-            dst -= 2; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        mul2_mono(Uint8);
-        break;
-    case 16:
-        mul2_mono(Uint16);
-        break;
-    case 32:
-        mul2_mono(Uint32);
-        break;
-    }
-
-#undef mul2_mono
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-
-/* Convert rate up by multiple of 2, for stereo */
-static void SDLCALL
-SDL_RateMUL2_c2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate * 2 (stereo)\n");
-#endif
-
-#define mul2_stereo(type) { \
-        const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
-        type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \
-        for (i = cvt->len_cvt / (sizeof (type) * 2); i; --i) { \
-            const type r = src[-1]; \
-            const type l = src[-2]; \
-            src -= 2; \
-            dst[-1] = r; \
-            dst[-2] = l; \
-            dst[-3] = r; \
-            dst[-4] = l; \
-            dst -= 4; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        mul2_stereo(Uint8);
-        break;
-    case 16:
-        mul2_stereo(Uint16);
-        break;
-    case 32:
-        mul2_stereo(Uint32);
-        break;
-    }
-
-#undef mul2_stereo
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-/* Convert rate up by multiple of 2, for quad */
-static void SDLCALL
-SDL_RateMUL2_c4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate * 2 (quad)\n");
-#endif
-
-#define mul2_quad(type) { \
-        const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
-        type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \
-        for (i = cvt->len_cvt / (sizeof (type) * 4); i; --i) { \
-            const type c1 = src[-1]; \
-            const type c2 = src[-2]; \
-            const type c3 = src[-3]; \
-            const type c4 = src[-4]; \
-            src -= 4; \
-            dst[-1] = c1; \
-            dst[-2] = c2; \
-            dst[-3] = c3; \
-            dst[-4] = c4; \
-            dst[-5] = c1; \
-            dst[-6] = c2; \
-            dst[-7] = c3; \
-            dst[-8] = c4; \
-            dst -= 8; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        mul2_quad(Uint8);
-        break;
-    case 16:
-        mul2_quad(Uint16);
-        break;
-    case 32:
-        mul2_quad(Uint32);
-        break;
-    }
-
-#undef mul2_quad
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-
-/* Convert rate up by multiple of 2, for 5.1 */
-static void SDLCALL
-SDL_RateMUL2_c6(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate * 2 (six channels)\n");
-#endif
-
-#define mul2_chansix(type) { \
-        const type *src = (const type *) (cvt->buf + cvt->len_cvt); \
-        type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \
-        for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \
-            const type c1 = src[-1]; \
-            const type c2 = src[-2]; \
-            const type c3 = src[-3]; \
-            const type c4 = src[-4]; \
-            const type c5 = src[-5]; \
-            const type c6 = src[-6]; \
-            src -= 6; \
-            dst[-1] = c1; \
-            dst[-2] = c2; \
-            dst[-3] = c3; \
-            dst[-4] = c4; \
-            dst[-5] = c5; \
-            dst[-6] = c6; \
-            dst[-7] = c1; \
-            dst[-8] = c2; \
-            dst[-9] = c3; \
-            dst[-10] = c4; \
-            dst[-11] = c5; \
-            dst[-12] = c6; \
-            dst -= 12; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        mul2_chansix(Uint8);
-        break;
-    case 16:
-        mul2_chansix(Uint16);
-        break;
-    case 32:
-        mul2_chansix(Uint32);
-        break;
-    }
-
-#undef mul2_chansix
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-/* Convert rate down by multiple of 2 */
-static void SDLCALL
-SDL_RateDIV2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate / 2 (mono)\n");
-#endif
-
-#define div2_mono(type) { \
-        const type *src = (const type *) cvt->buf; \
-        type *dst = (type *) cvt->buf; \
-        for (i = cvt->len_cvt / (sizeof (type) * 2); i; --i) { \
-            dst[0] = src[0]; \
-            src += 2; \
-            dst++; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        div2_mono(Uint8);
-        break;
-    case 16:
-        div2_mono(Uint16);
-        break;
-    case 32:
-        div2_mono(Uint32);
-        break;
-    }
-
-#undef div2_mono
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-
-/* Convert rate down by multiple of 2, for stereo */
-static void SDLCALL
-SDL_RateDIV2_c2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate / 2 (stereo)\n");
-#endif
-
-#define div2_stereo(type) { \
-        const type *src = (const type *) cvt->buf; \
-        type *dst = (type *) cvt->buf; \
-        for (i = cvt->len_cvt / (sizeof (type) * 4); i; --i) { \
-            dst[0] = src[0]; \
-            dst[1] = src[1]; \
-            src += 4; \
-            dst += 2; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        div2_stereo(Uint8);
-        break;
-    case 16:
-        div2_stereo(Uint16);
-        break;
-    case 32:
-        div2_stereo(Uint32);
-        break;
-    }
-
-#undef div2_stereo
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-
-/* Convert rate down by multiple of 2, for quad */
-static void SDLCALL
-SDL_RateDIV2_c4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate / 2 (quad)\n");
-#endif
-
-#define div2_quad(type) { \
-        const type *src = (const type *) cvt->buf; \
-        type *dst = (type *) cvt->buf; \
-        for (i = cvt->len_cvt / (sizeof (type) * 8); i; --i) { \
-            dst[0] = src[0]; \
-            dst[1] = src[1]; \
-            dst[2] = src[2]; \
-            dst[3] = src[3]; \
-            src += 8; \
-            dst += 4; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        div2_quad(Uint8);
-        break;
-    case 16:
-        div2_quad(Uint16);
-        break;
-    case 32:
-        div2_quad(Uint32);
-        break;
-    }
-
-#undef div2_quad
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-/* Convert rate down by multiple of 2, for 5.1 */
-static void SDLCALL
-SDL_RateDIV2_c6(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate / 2 (six channels)\n");
-#endif
-
-#define div2_chansix(type) { \
-        const type *src = (const type *) cvt->buf; \
-        type *dst = (type *) cvt->buf; \
-        for (i = cvt->len_cvt / (sizeof (type) * 12); i; --i) { \
-            dst[0] = src[0]; \
-            dst[1] = src[1]; \
-            dst[2] = src[2]; \
-            dst[3] = src[3]; \
-            dst[4] = src[4]; \
-            dst[5] = src[5]; \
-            src += 12; \
-            dst += 6; \
-        } \
-    }
-
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        div2_chansix(Uint8);
-        break;
-    case 16:
-        div2_chansix(Uint16);
-        break;
-    case 32:
-        div2_chansix(Uint32);
-        break;
-    }
-
-#undef div_chansix
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
-
-/* Very slow rate conversion routine */
-static void SDLCALL
-SDL_RateSLOW(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    double ipos;
-    int i, clen;
-
-#ifdef DEBUG_CONVERT
-    fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0 / cvt->rate_incr);
-#endif
-    clen = (int) ((double) cvt->len_cvt / cvt->rate_incr);
-    if (cvt->rate_incr > 1.0) {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            {
-                Uint8 *output;
-
-                output = cvt->buf;
-                ipos = 0.0;
-                for (i = clen; i; --i) {
-                    *output = cvt->buf[(int) ipos];
-                    ipos += cvt->rate_incr;
-                    output += 1;
-                }
-            }
-            break;
-
-        case 16:
-            {
-                Uint16 *output;
-
-                clen &= ~1;
-                output = (Uint16 *) cvt->buf;
-                ipos = 0.0;
-                for (i = clen / 2; i; --i) {
-                    *output = ((Uint16 *) cvt->buf)[(int) ipos];
-                    ipos += cvt->rate_incr;
-                    output += 1;
-                }
-            }
-            break;
-
-        case 32:
-            {
-                /* !!! FIXME: need 32-bit converter here! */
-#ifdef DEBUG_CONVERT
-                fprintf(stderr, "FIXME: need 32-bit converter here!\n");
-#endif
-            }
-        }
-    } else {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            {
-                Uint8 *output;
-
-                output = cvt->buf + clen;
-                ipos = (double) cvt->len_cvt;
-                for (i = clen; i; --i) {
-                    ipos -= cvt->rate_incr;
-                    output -= 1;
-                    *output = cvt->buf[(int) ipos];
-                }
-            }
-            break;
-
-        case 16:
-            {
-                Uint16 *output;
-
-                clen &= ~1;
-                output = (Uint16 *) (cvt->buf + clen);
-                ipos = (double) cvt->len_cvt / 2;
-                for (i = clen / 2; i; --i) {
-                    ipos -= cvt->rate_incr;
-                    output -= 1;
-                    *output = ((Uint16 *) cvt->buf)[(int) ipos];
-                }
-            }
-            break;
-
-        case 32:
-            {
-                /* !!! FIXME: need 32-bit converter here! */
-#ifdef DEBUG_CONVERT
-                fprintf(stderr, "FIXME: need 32-bit converter here!\n");
-#endif
-            }
-        }
-    }
-
-    cvt->len_cvt = clen;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
-}
 
 int
 SDL_ConvertAudio(SDL_AudioCVT * cvt)
 {
+    /* !!! FIXME: (cvt) should be const; stack-copy it here. */
+    /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */
+
     /* Make sure there's data to convert */
     if (cvt->buf == NULL) {
         SDL_SetError("No buffer allocated for conversion");
@@ -1341,478 +857,96 @@
     return 0;                   /* no conversion necessary. */
 }
 
-/* Generate the necessary IIR lowpass coefficients for resampling.
-   Assume that the SDL_AudioCVT struct is already set up with
-   the correct values for len_mult and len_div, and use the
-   type of dst_format. Also assume the buffer is allocated.
-   Note the buffer needs to be 6 units long.
-   For now, use RBJ's cookbook coefficients. It might be more
-   optimal to create a Butterworth filter, but this is more difficult.
-*/
-#if 0
-int
-SDL_BuildIIRLowpass(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    float fc;                   /* cutoff frequency */
-    float coeff[6];             /* floating point iir coefficients b0, b1, b2, a0, a1, a2 */
-    float scale;
-    float w0, alpha, cosw0;
-    int i;
-
-    /* The higher Q is, the higher CUTOFF can be. Need to find a good balance to avoid aliasing */
-    static const float Q = 5.0f;
-    static const float CUTOFF = 0.4f;
-
-    fc = (cvt->len_mult >
-          cvt->len_div) ? CUTOFF / (float) cvt->len_mult : CUTOFF /
-        (float) cvt->len_div;
-
-    w0 = 2.0f * M_PI * fc;
-    cosw0 = cosf(w0);
-    alpha = sinf(w0) / (2.0f * Q);
-
-    /* Compute coefficients, normalizing by a0 */
-    scale = 1.0f / (1.0f + alpha);
-
-    coeff[0] = (1.0f - cosw0) / 2.0f * scale;
-    coeff[1] = (1.0f - cosw0) * scale;
-    coeff[2] = coeff[0];
-
-    coeff[3] = 1.0f;            /* a0 is normalized to 1 */
-    coeff[4] = -2.0f * cosw0 * scale;
-    coeff[5] = (1.0f - alpha) * scale;
-
-    /* Copy the coefficients to the struct. If necessary, convert coefficients to fixed point, using the range (-2.0, 2.0) */
-#define convert_fixed(type, fix) { \
-            type *cvt_coeff = (type *)cvt->coeff; \
-            for(i = 0; i < 6; ++i) { \
-                cvt_coeff[i] = fix(coeff[i]); \
-            } \
-        }
-
-    if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
-        float *cvt_coeff = (float *) cvt->coeff;
-        for (i = 0; i < 6; ++i) {
-            cvt_coeff[i] = coeff[i];
-        }
-    } else {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            convert_fixed(Uint8, SDL_Make_2_6);
-            break;
-        case 16:
-            convert_fixed(Uint16, SDL_Make_2_14);
-            break;
-        case 32:
-            convert_fixed(Uint32, SDL_Make_2_30);
-            break;
-        }
-    }
-
-#ifdef DEBUG_CONVERT
-#define debug_iir(type) { \
-            type *cvt_coeff = (type *)cvt->coeff; \
-            for(i = 0; i < 6; ++i) { \
-                printf("coeff[%u] = %f = 0x%x\n", i, coeff[i], cvt_coeff[i]); \
-            } \
-        }
-    if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
-        float *cvt_coeff = (float *) cvt->coeff;
-        for (i = 0; i < 6; ++i) {
-            printf("coeff[%u] = %f = %f\n", i, coeff[i], cvt_coeff[i]);
-        }
-    } else {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            debug_iir(Uint8);
-            break;
-        case 16:
-            debug_iir(Uint16);
-            break;
-        case 32:
-            debug_iir(Uint32);
-            break;
-        }
-    }
-#undef debug_iir
-#endif
 
-    /* Initialize the state buffer to all zeroes, and set initial position */
-    SDL_memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4);
-    cvt->state_pos = 0;
-#undef convert_fixed
-
-    return 0;
-}
-#endif
-
-/* Apply the lowpass IIR filter to the given SDL_AudioCVT struct */
-/* This was implemented because it would be much faster than the fir filter, 
-   but it doesn't seem to have a steep enough cutoff so we'd need several
-   cascaded biquads, which probably isn't a great idea. Therefore, this
-   function can probably be discarded.
-*/
-static void
-SDL_FilterIIR(SDL_AudioCVT * cvt, SDL_AudioFormat format)
+static SDL_AudioFilter
+SDL_HandTunedResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
+                         int src_rate, int dst_rate)
 {
-    Uint32 i, n;
-
-    /* TODO: Check that n is calculated right */
-    n = 8 * cvt->len_cvt / SDL_AUDIO_BITSIZE(format);
+    /*
+     * Fill in any future conversions that are specialized to a
+     *  processor, platform, compiler, or library here.
+     */
 
-    /* Note that the coefficients are 2_x and the input is 1_x. Do we need to shift left at the end here? The right shift temp = buf[n] >> 1 needs to depend on whether the type is signed or not for sign extension. */
-    /* cvt->state_pos = 1: state[0] = x_n-1, state[1] = x_n-2, state[2] = y_n-1, state[3] - y_n-2 */
-#define iir_fix(type, mult) {\
-            type *coeff = (type *)cvt->coeff; \
-            type *state = (type *)cvt->state_buf; \
-            type *buf = (type *)cvt->buf; \
-            type temp; \
-            for(i = 0; i < n; ++i) { \
-                    temp = buf[i] >> 1; \
-                    if(cvt->state_pos) { \
-                        buf[i] = mult(coeff[0], temp) + mult(coeff[1], state[0]) + mult(coeff[2], state[1]) - mult(coeff[4], state[2]) - mult(coeff[5], state[3]); \
-                        state[1] = temp; \
-                        state[3] = buf[i]; \
-                        cvt->state_pos = 0; \
-                    } else { \
-                        buf[i] = mult(coeff[0], temp) + mult(coeff[1], state[1]) + mult(coeff[2], state[0]) - mult(coeff[4], state[3]) - mult(coeff[5], state[2]); \
-                        state[0] = temp; \
-                        state[2] = buf[i]; \
-                        cvt->state_pos = 1; \
-                    } \
-                } \
-        }
-/* Need to test to see if the previous method or this one is faster */
-/*#define iir_fix(type, mult) {\
-            type *coeff = (type *)cvt->coeff; \
-            type *state = (type *)cvt->state_buf; \
-            type *buf = (type *)cvt->buf; \
-            type temp; \
-            for(i = 0; i < n; ++i) { \
-                    temp = buf[i] >> 1; \
-                    buf[i] = mult(coeff[0], temp) + mult(coeff[1], state[0]) + mult(coeff[2], state[1]) - mult(coeff[4], state[2]) - mult(coeff[5], state[3]); \
-                    state[1] = state[0]; \
-                    state[0] = temp; \
-                    state[3] = state[2]; \
-                    state[2] = buf[i]; \
-                } \
-        }*/
-
-    if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
-        float *coeff = (float *) cvt->coeff;
-        float *state = (float *) cvt->state_buf;
-        float *buf = (float *) cvt->buf;
-        float temp;
-
-        for (i = 0; i < n; ++i) {
-            /* y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a[2] * y[n-2] */
-            temp = buf[i];
-            if (cvt->state_pos) {
-                buf[i] =
-                    coeff[0] * buf[n] + coeff[1] * state[0] +
-                    coeff[2] * state[1] - coeff[4] * state[2] -
-                    coeff[5] * state[3];
-                state[1] = temp;
-                state[3] = buf[i];
-                cvt->state_pos = 0;
-            } else {
-                buf[i] =
-                    coeff[0] * buf[n] + coeff[1] * state[1] +
-                    coeff[2] * state[0] - coeff[4] * state[3] -
-                    coeff[5] * state[2];
-                state[0] = temp;
-                state[2] = buf[i];
-                cvt->state_pos = 1;
-            }
-        }
-    } else {
-        /* Treat everything as signed! */
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            iir_fix(Sint8, SDL_FixMpy8);
-            break;
-        case 16:
-            iir_fix(Sint16, SDL_FixMpy16);
-            break;
-        case 32:
-            iir_fix(Sint32, SDL_FixMpy32);
-            break;
-        }
-    }
-#undef iir_fix
+    return NULL;                /* no specialized converter code available. */
 }
 
-/* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct.
-*/
-static void
-SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format)
+static int
+SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
 {
-    /* !!! FIXME: (n) is incorrect, or my allocation of state_buf is wrong. */
-    const int n = 8 * cvt->len_cvt / SDL_AUDIO_BITSIZE(format);
-    int m = cvt->len_sinc;
-    int i, j;
+    int retval = 0;
 
-    /* 
-       Note: We can make a big optimization here by taking advantage
-       of the fact that the signal is zero stuffed, so we can do
-       significantly fewer multiplications and additions. However, this
-       depends on the zero stuffing ratio, so it may not pay off. This would
-       basically be a polyphase filter.
-     */
-    /* One other way to do this fast is to look at the fir filter from a different angle:
-       After we zero stuff, we have input of all zeroes, except for every len_mult
-       sample. If we choose a sinc length equal to len_mult, then the fir filter becomes
-       much more simple: we're just taking a windowed sinc, shifting it to start at each
-       len_mult sample, and scaling it by the value of that sample. If we do this, then
-       we don't even need to worry about the sample histories, and the inner loop here is
-       unnecessary. This probably sacrifices some quality but could really speed things up as well.
-     */
-    /* We only calculate the values of samples which are 0 (mod len_div) because
-       those are the only ones used. All the other ones are discarded in the
-       third step of resampling. This is a huge speedup. As a warning, though,
-       if for some reason this is used elsewhere where there are no samples discarded,
-       the output will not be corrrect if len_div is not 1. To make this filter a
-       generic FIR filter, simply remove the if statement "if(i % cvt->len_div == 0)"
-       around the inner loop so that every sample is processed.
-     */
-    /* This is basically just a FIR filter. i.e. for input x_n and m coefficients,
-       y_n = x_n*sinc_0 + x_(n-1)*sinc_1 +  x_(n-2)*sinc_2 + ... + x_(n-m+1)*sinc_(m-1)
-     */
-#define filter_sinc(type, mult) { \
-            type *sinc = (type *)cvt->coeff; \
-            type *state = (type *)cvt->state_buf; \
-            type *buf = (type *)cvt->buf; \
-            for(i = 0; i < n; ++i) { \
-                state[cvt->state_pos] = buf[i]; \
-                buf[i] = 0; \
-                if( i % cvt->len_div == 0 ) { \
-                    for(j = 0; j < m;  ++j) { \
-                        buf[i] += mult(sinc[j], state[(cvt->state_pos + j) % m]); \
-                    } \
-                }\
-                cvt->state_pos = (cvt->state_pos + 1) % m; \
-            } \
-        }
+    /* If we only built with the arbitrary resamplers, ignore multiples. */
+#if !LESS_RESAMPLERS
+    int lo, hi;
+    int div;
 
-    if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
-        filter_sinc(float, SDL_FloatMpy);
+    assert(src_rate != 0);
+    assert(dst_rate != 0);
+    assert(src_rate != dst_rate);
+
+    if (src_rate < dst_rate) {
+        lo = src_rate;
+        hi = dst_rate;
     } else {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            filter_sinc(Sint8, SDL_FixMpy8);
-            break;
-        case 16:
-            filter_sinc(Sint16, SDL_FixMpy16);
-            break;
-        case 32:
-            filter_sinc(Sint32, SDL_FixMpy32);
-            break;
-        }
-    }
-
-#undef filter_sinc
-
-}
-
-/* Generate the necessary windowed sinc filter for resampling.
-   Assume that the SDL_AudioCVT struct is already set up with
-   the correct values for len_mult and len_div, and use the
-   type of dst_format. Also assume the buffer is allocated.
-   Note the buffer needs to be m+1 units long.
-*/
-int
-SDL_BuildWindowedSinc(SDL_AudioCVT * cvt, SDL_AudioFormat format,
-                      unsigned int m)
-{
-    float *fSinc;               /* floating point sinc buffer, to be converted to fixed point */
-    float fc;                   /* cutoff frequency */
-    float two_pi_fc, two_pi_over_m, four_pi_over_m, m_over_two;
-    float norm_sum, norm_fact;
-    unsigned int i;
-
-    /* Set the length */
-    cvt->len_sinc = m + 1;
-
-    /* Allocate the floating point windowed sinc. */
-    fSinc = SDL_stack_alloc(float, (m + 1));
-    if (fSinc == NULL) {
-        return -1;
-    }
-
-    /* Set up the filter parameters */
-    fc = (cvt->len_mult >
-          cvt->len_div) ? 0.5f / (float) cvt->len_mult : 0.5f /
-        (float) cvt->len_div;
-#ifdef DEBUG_CONVERT
-    printf("Lowpass cutoff frequency = %f\n", fc);
-#endif
-    two_pi_fc = 2.0f * M_PI * fc;
-    two_pi_over_m = 2.0f * M_PI / (float) m;
-    four_pi_over_m = 2.0f * two_pi_over_m;
-    m_over_two = (float) m / 2.0f;
-    norm_sum = 0.0f;
-
-    for (i = 0; i <= m; ++i) {
-        if (i == m / 2) {
-            fSinc[i] = two_pi_fc;
-        } else {
-            fSinc[i] = SDL_sinf(two_pi_fc * ((float) i - m_over_two)) / ((float) i - m_over_two);
-            /* Apply blackman window */
-            fSinc[i] *= 0.42f - 0.5f * SDL_cosf(two_pi_over_m * (float) i) + 0.08f * SDL_cosf(four_pi_over_m * (float) i);
-        }
-        norm_sum += fSinc[i] < 0 ? -fSinc[i] : fSinc[i];        /* fabs(fSinc[i]); */
-    }
-
-    norm_fact = 1.0f / norm_sum;
-
-#define convert_fixed(type, fix) { \
-        type *dst = (type *)cvt->coeff; \
-        for( i = 0; i <= m; ++i ) { \
-            dst[i] = fix(fSinc[i] * norm_fact); \
-        } \
+        lo = dst_rate;
+        hi = src_rate;
     }
 
-    /* !!! FIXME: this memory leaks. */
-    cvt->coeff =
-        (Uint8 *) SDL_malloc((SDL_AUDIO_BITSIZE(format) / 8) * (m + 1));
-    if (cvt->coeff == NULL) {
-        return -1;
-    }
-
-    /* If we're using floating point, we only need to normalize */
-    if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) {
-        float *fDest = (float *) cvt->coeff;
-        for (i = 0; i <= m; ++i) {
-            fDest[i] = fSinc[i] * norm_fact;
-        }
-    } else {
-        switch (SDL_AUDIO_BITSIZE(format)) {
-        case 8:
-            convert_fixed(Uint8, SDL_Make_1_7);
-            break;
-        case 16:
-            convert_fixed(Uint16, SDL_Make_1_15);
-            break;
-        case 32:
-            convert_fixed(Uint32, SDL_Make_1_31);
-            break;
-        }
-    }
+    /* zero means "not a supported multiple" ... we only do 2x and 4x. */
+    if ((hi % lo) != 0)
+        return 0;   /* not a multiple. */
 
-    /* Initialize the state buffer to all zeroes, and set initial position */
-    /* !!! FIXME: this memory leaks. */
-    cvt->state_buf =
-        (Uint8 *) SDL_malloc(cvt->len_sinc * SDL_AUDIO_BITSIZE(format) / 4);
-    if (cvt->state_buf == NULL) {
-        return -1;
-    }
-    SDL_memset(cvt->state_buf, 0,
-               cvt->len_sinc * SDL_AUDIO_BITSIZE(format) / 4);
-    cvt->state_pos = 0;
-
-    /* Clean up */
-#undef convert_fixed
-    SDL_stack_free(fSinc);
-
-    return 0;
-}
-
-/* This is used to reduce the resampling ratio */
-static __inline__ int
-SDL_GCD(int a, int b)
-{
-    int temp;
-    while (b != 0) {
-        temp = a % b;
-        a = b;
-        b = temp;
-    }
-    return a;
-}
-
-/* Perform proper resampling. This is pretty slow but it's the best-sounding method. */
-static void SDLCALL
-SDL_Resample(SDL_AudioCVT * cvt, SDL_AudioFormat format)
-{
-    int i;
-
-#ifdef DEBUG_CONVERT
-    printf("Converting audio rate via proper resampling (mono)\n");
+    div = hi / lo;
+    retval = ((div == 2) || (div == 4)) ? div : 0;
 #endif
 
-#define zerostuff_mono(type) { \
-        const type *src = (const type *) (cvt->buf + cvt->len); \
-        type *dst = (type *) (cvt->buf + (cvt->len * cvt->len_mult)); \
-        for (i = cvt->len / sizeof (type); i; --i) { \
-            src--; \
-            dst[-1] = src[0]; \
-            if (cvt->len_mult > 1) { \
-                SDL_memset(dst-cvt->len_mult, 0, cvt->len_mult-1); \
-            } \
-            dst -= cvt->len_mult; \
-        } \
-    }
+    return retval;
+}
+
+static int
+SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
+                          int src_rate, int dst_rate)
+{
+    if (src_rate != dst_rate) {
+        SDL_AudioFilter filter = SDL_HandTunedResampleCVT(cvt, dst_channels,
+                                                          src_rate, dst_rate);
+
+        /* No hand-tuned converter? Try the autogenerated ones. */
+        if (filter == NULL) {
+            int i;
+            const int upsample = (src_rate < dst_rate) ? 1 : 0;
+            const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
 
-#define discard_mono(type) { \
-        const type *src = (const type *) (cvt->buf); \
-        type *dst = (type *) (cvt->buf); \
-        for (i = 0; i < (cvt->len_cvt / sizeof(type)) / cvt->len_div; ++i) { \
-            dst[0] = src[0]; \
-            src += cvt->len_div; \
-            ++dst; \
-        } \
+            for (i = 0; sdl_audio_rate_filters[i].filter != NULL; i++) {
+                const SDL_AudioRateFilters *filt = &sdl_audio_rate_filters[i];
+                if ((filt->fmt == cvt->dst_format) &&
+                    (filt->channels == dst_channels) &&
+                    (filt->upsample == upsample) &&
+                    (filt->multiple == multiple)) {
+                    filter = filt->filter;
+                    break;
+                }
+            }
+
+            if (filter == NULL) {
+                return -1;      /* Still no matching 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) ceil(mult);  /* !!! FIXME: C runtime dependency. */
+            cvt->len_ratio *= mult;
+        } else {
+            cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
+        }
+
+        return 1;               /* added a converter. */
     }
 
-    /* Step 1: Zero stuff the conversion buffer. This upsamples by a factor of len_mult,
-       creating aliasing at frequencies above the original nyquist frequency.
-     */
-#ifdef DEBUG_CONVERT
-    printf("Zero-stuffing by a factor of %u\n", cvt->len_mult);
-#endif
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        zerostuff_mono(Uint8);
-        break;
-    case 16:
-        zerostuff_mono(Uint16);
-        break;
-    case 32:
-        zerostuff_mono(Uint32);
-        break;
-    }
-
-    cvt->len_cvt *= cvt->len_mult;
-
-    /* Step 2: Use a windowed sinc FIR filter (lowpass filter) to remove the alias
-       frequencies. This is the slow part.
-     */
-    SDL_FilterFIR(cvt, format);
-
-    /* Step 3: Now downsample by discarding samples. */
-
-#ifdef DEBUG_CONVERT
-    printf("Discarding samples by a factor of %u\n", cvt->len_div);
-#endif
-    switch (SDL_AUDIO_BITSIZE(format)) {
-    case 8:
-        discard_mono(Uint8);
-        break;
-    case 16:
-        discard_mono(Uint16);
-        break;
-    case 32:
-        discard_mono(Uint32);
-        break;
-    }
-
-#undef zerostuff_mono
-#undef discard_mono
-
-    cvt->len_cvt /= cvt->len_div;
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index] (cvt, format);
-    }
+    return 0;                   /* no conversion necessary. */
 }
 
 
@@ -1826,6 +960,14 @@
                   SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate,
                   SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate)
 {
+    /*
+     * !!! FIXME: reorder filters based on which grow/shrink the buffer.
+     * !!! FIXME: ideally, we should do everything that shrinks the buffer
+     * !!! FIXME: first, so we don't have to process as many bytes in a given
+     * !!! FIXME: filter and abuse the CPU cache less. This might not be as
+     * !!! FIXME: good in practice as it sounds in theory, though.
+     */
+
     /* there are no unsigned types over 16 bits, so catch this upfront. */
     if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) {
         return -1;
@@ -1833,6 +975,12 @@
     if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) {
         return -1;
     }
+
+    /* prevent possible divisions by zero, etc. */
+    if ((src_rate == 0) || (dst_rate == 0)) {
+        return -1;
+    }
+
 #ifdef DEBUG_CONVERT
     printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n",
            src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate);
@@ -1847,10 +995,12 @@
     cvt->filters[0] = NULL;
     cvt->len_mult = 1;
     cvt->len_ratio = 1.0;
+    cvt->rate_incr = ((double) dst_rate) / ((double) src_rate);
 
     /* Convert data types, if necessary. Updates (cvt). */
-    if (SDL_BuildAudioTypeCVT(cvt, src_fmt, dst_fmt) == -1)
+    if (SDL_BuildAudioTypeCVT(cvt, src_fmt, dst_fmt) == -1) {
         return -1;              /* shouldn't happen, but just in case... */
+    }
 
     /* Channel conversion */
     if (src_channels != dst_channels) {
@@ -1903,100 +1053,11 @@
         }
     }
 
-    /* Do rate conversion */
-    if (src_rate != dst_rate) {
-        int rate_gcd;
-        rate_gcd = SDL_GCD(src_rate, dst_rate);
-        cvt->len_mult = dst_rate / rate_gcd;
-        cvt->len_div = src_rate / rate_gcd;
-        cvt->len_ratio = (double) cvt->len_mult / (double) cvt->len_div;
-        cvt->filters[cvt->filter_index++] = SDL_Resample;
-        /* !!! FIXME: check return value. */
-        SDL_BuildWindowedSinc(cvt, dst_fmt, 768);
+    /* Do rate conversion, if necessary. Updates (cvt). */
+    if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) {
+        return -1;              /* shouldn't happen, but just in case... */
     }
 
-/*
-    cvt->rate_incr = 0.0;
-    if ((src_rate / 100) != (dst_rate / 100)) {
-        Uint32 hi_rate, lo_rate;
-        int len_mult;
-        double len_ratio;
-        SDL_AudioFilter rate_cvt = NULL;
-
-        if (src_rate > dst_rate) {
-            hi_rate = src_rate;
-            lo_rate = dst_rate;
-            switch (src_channels) {
-            case 1:
-                rate_cvt = SDL_RateDIV2;
-                break;
-            case 2:
-                rate_cvt = SDL_RateDIV2_c2;
-                break;
-            case 4:
-                rate_cvt = SDL_RateDIV2_c4;
-                break;
-            case 6:
-                rate_cvt = SDL_RateDIV2_c6;
-                break;
-            default:
-                return -1;
-            }
-            len_mult = 1;
-            len_ratio = 0.5;
-        } else {
-            hi_rate = dst_rate;
-            lo_rate = src_rate;
-            switch (src_channels) {
-            case 1:
-                rate_cvt = SDL_RateMUL2;
-                break;
-            case 2:
-                rate_cvt = SDL_RateMUL2_c2;
-                break;
-            case 4:
-                rate_cvt = SDL_RateMUL2_c4;
-                break;
-            case 6:
-                rate_cvt = SDL_RateMUL2_c6;
-                break;
-            default:
-                return -1;
-            }
-            len_mult = 2;
-            len_ratio = 2.0;
-        }*/
-    /* If hi_rate = lo_rate*2^x then conversion is easy */
-    /*   while (((lo_rate * 2) / 100) <= (hi_rate / 100)) {
-       cvt->filters[cvt->filter_index++] = rate_cvt;
-       cvt->len_mult *= len_mult;
-       lo_rate *= 2;
-       cvt->len_ratio *= len_ratio;
-       } */
-    /* We may need a slow conversion here to finish up */
-    /*    if ((lo_rate / 100) != (hi_rate / 100)) {
-       #if 1 */
-    /* The problem with this is that if the input buffer is
-       say 1K, and the conversion rate is say 1.1, then the
-       output buffer is 1.1K, which may not be an acceptable
-       buffer size for the audio driver (not a power of 2)
-     */
-    /* For now, punt and hope the rate distortion isn't great.
-     */
-/*#else
-            if (src_rate < dst_rate) {
-                cvt->rate_incr = (double) lo_rate / hi_rate;
-                cvt->len_mult *= 2;
-                cvt->len_ratio /= cvt->rate_incr;
-            } else {
-                cvt->rate_incr = (double) hi_rate / lo_rate;
-                cvt->len_ratio *= cvt->rate_incr;
-            }
-            cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
-#endif
-        }
-    }*/
-
     /* Set up the filter information */
     if (cvt->filter_index != 0) {
         cvt->needed = 1;
@@ -2009,15 +1070,5 @@
     return (cvt->needed);
 }
 
-#undef SDL_FixMpy8
-#undef SDL_FixMpy16
-#undef SDL_FixMpy32
-#undef SDL_FloatMpy
-#undef SDL_Make_1_7
-#undef SDL_Make_1_15
-#undef SDL_Make_1_31
-#undef SDL_Make_2_6
-#undef SDL_Make_2_14
-#undef SDL_Make_2_30
 
 /* vi: set ts=4 sw=4 expandtab: */