Mercurial > sdl-ios-xcode
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: */