slouken@0: /* slouken@0: SDL - Simple DirectMedia Layer slouken@1312: Copyright (C) 1997-2006 Sam Lantinga slouken@0: slouken@0: This library is free software; you can redistribute it and/or slouken@1312: modify it under the terms of the GNU Lesser General Public slouken@0: License as published by the Free Software Foundation; either slouken@1312: version 2.1 of the License, or (at your option) any later version. slouken@0: slouken@0: This library is distributed in the hope that it will be useful, slouken@0: but WITHOUT ANY WARRANTY; without even the implied warranty of slouken@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU slouken@1312: Lesser General Public License for more details. slouken@0: slouken@1312: You should have received a copy of the GNU Lesser General Public slouken@1312: License along with this library; if not, write to the Free Software slouken@1312: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA slouken@0: slouken@0: Sam Lantinga slouken@252: slouken@libsdl.org slouken@0: */ slouken@1402: #include "SDL_config.h" slouken@2728: slouken@2728: #define _USE_MATH_DEFINES slouken@2716: #include slouken@0: slouken@0: /* Functions for audio drivers to perform runtime conversion of audio format */ slouken@0: slouken@0: #include "SDL_audio.h" icculus@1982: #include "SDL_audio_c.h" slouken@0: slouken@2728: //#define DEBUG_CONVERT slouken@2716: slouken@2716: /* These are fractional multiplication routines. That is, their inputs slouken@2716: are two numbers in the range [-1, 1) and the result falls in that slouken@2716: same range. The output is the same size as the inputs, i.e. slouken@2716: 32-bit x 32-bit = 32-bit. slouken@2716: */ slouken@2716: slouken@2716: /* We hope here that the right shift includes sign extension */ slouken@2716: #ifdef SDL_HAS_64BIT_Type slouken@2716: #define SDL_FixMpy32(a, b) ((((Sint64)a * (Sint64)b) >> 31) & 0xffffffff) slouken@2716: #else slouken@2716: /* 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 */ slouken@2716: #define SDL_FixMpy32(a, b) ((((Sint64)a * (Sint64)b) >> 31) & 0xffffffff) slouken@2716: #endif slouken@2716: #define SDL_FixMpy16(a, b) ((((Sint32)a * (Sint32)b) >> 14) & 0xffff) slouken@2716: #define SDL_FixMpy8(a, b) ((((Sint16)a * (Sint16)b) >> 7) & 0xff) slouken@2716: /* This macro just makes the floating point filtering code not have to be a special case. */ slouken@2716: #define SDL_FloatMpy(a, b) (a * b) slouken@2716: slouken@2716: /* These macros take floating point numbers in the range [-1.0f, 1.0f) and slouken@2716: represent them as fixed-point numbers in that same range. There's no slouken@2716: checking that the floating point argument is inside the appropriate range. slouken@2716: */ slouken@2716: slouken@2716: #define SDL_Make_1_7(a) (Sint8)(a * 128.0f) slouken@2716: #define SDL_Make_1_15(a) (Sint16)(a * 32768.0f) slouken@2716: #define SDL_Make_1_31(a) (Sint32)(a * 2147483648.0f) slouken@2716: #define SDL_Make_2_6(a) (Sint8)(a * 64.0f) slouken@2716: #define SDL_Make_2_14(a) (Sint16)(a * 16384.0f) slouken@2716: #define SDL_Make_2_30(a) (Sint32)(a * 1073741824.0f) slouken@2716: slouken@0: /* Effectively mix right and left channels into a single channel */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@0: { slouken@1895: int i; slouken@1895: Sint32 sample; slouken@0: slouken@0: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting to mono\n"); slouken@0: #endif slouken@1985: switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { slouken@1895: case AUDIO_U8: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@0: slouken@1895: src = cvt->buf; slouken@1895: dst = cvt->buf; slouken@1895: for (i = cvt->len_cvt / 2; i; --i) { slouken@1895: sample = src[0] + src[1]; slouken@2042: *dst = (Uint8) (sample / 2); slouken@1895: src += 2; slouken@1895: dst += 1; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@0: slouken@1895: case AUDIO_S8: slouken@1895: { slouken@1895: Sint8 *src, *dst; slouken@0: slouken@1895: src = (Sint8 *) cvt->buf; slouken@1895: dst = (Sint8 *) cvt->buf; slouken@1895: for (i = cvt->len_cvt / 2; i; --i) { slouken@1895: sample = src[0] + src[1]; slouken@2042: *dst = (Sint8) (sample / 2); slouken@1895: src += 2; slouken@1895: dst += 1; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@0: slouken@1895: case AUDIO_U16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@0: slouken@1895: src = cvt->buf; slouken@1895: dst = cvt->buf; icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: sample = (Uint16) ((src[0] << 8) | src[1]) + slouken@1895: (Uint16) ((src[2] << 8) | src[3]); slouken@2042: sample /= 2; slouken@2042: dst[1] = (sample & 0xFF); slouken@2042: sample >>= 8; slouken@2042: dst[0] = (sample & 0xFF); slouken@1895: src += 4; slouken@1895: dst += 2; slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: sample = (Uint16) ((src[1] << 8) | src[0]) + slouken@1895: (Uint16) ((src[3] << 8) | src[2]); slouken@2042: sample /= 2; slouken@2042: dst[0] = (sample & 0xFF); slouken@2042: sample >>= 8; slouken@2042: dst[1] = (sample & 0xFF); slouken@1895: src += 4; slouken@1895: dst += 2; slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; slouken@0: slouken@1895: case AUDIO_S16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@0: slouken@1895: src = cvt->buf; slouken@1895: dst = cvt->buf; icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: sample = (Sint16) ((src[0] << 8) | src[1]) + slouken@1895: (Sint16) ((src[2] << 8) | src[3]); slouken@2042: sample /= 2; slouken@2042: dst[1] = (sample & 0xFF); slouken@2042: sample >>= 8; slouken@2042: dst[0] = (sample & 0xFF); slouken@1895: src += 4; slouken@1895: dst += 2; slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: sample = (Sint16) ((src[1] << 8) | src[0]) + slouken@1895: (Sint16) ((src[3] << 8) | src[2]); slouken@2042: sample /= 2; slouken@2042: dst[0] = (sample & 0xFF); slouken@2042: sample >>= 8; slouken@2042: dst[1] = (sample & 0xFF); slouken@1895: src += 4; slouken@1895: dst += 2; slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; icculus@1982: icculus@1982: case AUDIO_S32: icculus@1982: { icculus@1982: const Uint32 *src = (const Uint32 *) cvt->buf; icculus@1982: Uint32 *dst = (Uint32 *) cvt->buf; icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { icculus@1982: for (i = cvt->len_cvt / 8; i; --i, src += 2) { icculus@1982: const Sint64 added = slouken@1985: (((Sint64) (Sint32) SDL_SwapBE32(src[0])) + slouken@1985: ((Sint64) (Sint32) SDL_SwapBE32(src[1]))); icculus@2078: *(dst++) = SDL_SwapBE32((Uint32) ((Sint32) (added / 2))); icculus@1982: } icculus@1982: } else { icculus@1982: for (i = cvt->len_cvt / 8; i; --i, src += 2) { icculus@1982: const Sint64 added = slouken@1985: (((Sint64) (Sint32) SDL_SwapLE32(src[0])) + slouken@1985: ((Sint64) (Sint32) SDL_SwapLE32(src[1]))); icculus@2078: *(dst++) = SDL_SwapLE32((Uint32) ((Sint32) (added / 2))); icculus@1982: } icculus@1982: } icculus@1982: } icculus@1982: break; icculus@1982: icculus@1982: case AUDIO_F32: icculus@1982: { icculus@2014: const float *src = (const float *) cvt->buf; icculus@2014: float *dst = (float *) cvt->buf; icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { icculus@1982: for (i = cvt->len_cvt / 8; i; --i, src += 2) { icculus@2049: const float src1 = SDL_SwapFloatBE(src[0]); icculus@2049: const float src2 = SDL_SwapFloatBE(src[1]); icculus@1982: const double added = ((double) src1) + ((double) src2); icculus@2049: const float halved = (float) (added * 0.5); icculus@2049: *(dst++) = SDL_SwapFloatBE(halved); icculus@1982: } icculus@1982: } else { icculus@1982: for (i = cvt->len_cvt / 8; i; --i, src += 2) { icculus@2049: const float src1 = SDL_SwapFloatLE(src[0]); icculus@2049: const float src2 = SDL_SwapFloatLE(src[1]); icculus@1982: const double added = ((double) src1) + ((double) src2); icculus@2049: const float halved = (float) (added * 0.5); icculus@2049: *(dst++) = SDL_SwapFloatLE(halved); icculus@1982: } icculus@1982: } icculus@1982: } icculus@1982: break; slouken@1895: } icculus@1982: slouken@1895: cvt->len_cvt /= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@0: } slouken@0: icculus@1982: slouken@942: /* Discard top 4 channels */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting down from 6 channels to stereo\n"); slouken@942: #endif slouken@942: slouken@1985: #define strip_chans_6_to_2(type) \ icculus@1982: { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: dst[1] = src[1]; \ icculus@1982: src += 6; \ icculus@1982: dst += 2; \ icculus@1982: } \ icculus@1982: } slouken@942: icculus@1982: /* this function only cares about typesize, and data as a block of bits. */ icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1985: case 8: slouken@1985: strip_chans_6_to_2(Uint8); slouken@1985: break; slouken@1985: case 16: slouken@1985: strip_chans_6_to_2(Uint16); slouken@1985: break; slouken@1985: case 32: slouken@1985: strip_chans_6_to_2(Uint32); slouken@1985: break; icculus@1982: } slouken@942: slouken@1985: #undef strip_chans_6_to_2 slouken@942: slouken@1895: cvt->len_cvt /= 3; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: slouken@942: /* Discard top 2 channels of 6 */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting 6 down to quad\n"); slouken@942: #endif slouken@942: slouken@1985: #define strip_chans_6_to_4(type) \ icculus@1982: { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: dst[1] = src[1]; \ icculus@1982: dst[2] = src[2]; \ icculus@1982: dst[3] = src[3]; \ icculus@1982: src += 6; \ icculus@1982: dst += 4; \ icculus@1982: } \ icculus@1982: } slouken@942: icculus@1982: /* this function only cares about typesize, and data as a block of bits. */ icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1985: case 8: slouken@1985: strip_chans_6_to_4(Uint8); slouken@1985: break; slouken@1985: case 16: slouken@1985: strip_chans_6_to_4(Uint16); slouken@1985: break; slouken@1985: case 32: slouken@1985: strip_chans_6_to_4(Uint32); slouken@1985: break; icculus@1982: } slouken@942: slouken@1985: #undef strip_chans_6_to_4 slouken@942: icculus@1982: cvt->len_cvt /= 6; icculus@1982: cvt->len_cvt *= 4; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@0: slouken@0: /* Duplicate a mono channel to both stereo channels */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@0: { slouken@1895: int i; slouken@0: slouken@0: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting to stereo\n"); slouken@0: #endif slouken@0: slouken@1985: #define dup_chans_1_to_2(type) \ icculus@1982: { \ icculus@1982: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ icculus@1982: type *dst = (type *) (cvt->buf + cvt->len_cvt * 2); \ icculus@1982: for (i = cvt->len_cvt / 2; i; --i, --src) { \ icculus@1982: const type val = *src; \ icculus@1982: dst -= 2; \ icculus@1982: dst[0] = dst[1] = val; \ icculus@1982: } \ icculus@1982: } slouken@0: icculus@1982: /* this function only cares about typesize, and data as a block of bits. */ icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1985: case 8: slouken@1985: dup_chans_1_to_2(Uint8); slouken@1985: break; slouken@1985: case 16: slouken@1985: dup_chans_1_to_2(Uint16); slouken@1985: break; slouken@1985: case 32: slouken@1985: dup_chans_1_to_2(Uint32); slouken@1985: break; slouken@1895: } icculus@1982: slouken@1985: #undef dup_chans_1_to_2 icculus@1982: slouken@1895: cvt->len_cvt *= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@0: } slouken@0: slouken@942: slouken@942: /* Duplicate a stereo channel to a pseudo-5.1 stream */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting stereo to surround\n"); slouken@942: #endif slouken@942: slouken@1985: switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { slouken@1895: case AUDIO_U8: slouken@1895: { slouken@1895: Uint8 *src, *dst, lf, rf, ce; slouken@942: slouken@1895: src = (Uint8 *) (cvt->buf + cvt->len_cvt); slouken@1895: dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 3); slouken@1895: for (i = cvt->len_cvt; i; --i) { slouken@1895: dst -= 6; slouken@1895: src -= 2; slouken@1895: lf = src[0]; slouken@1895: rf = src[1]; slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: dst[0] = lf; slouken@1895: dst[1] = rf; slouken@1895: dst[2] = lf - ce; slouken@1895: dst[3] = rf - ce; slouken@1895: dst[4] = ce; slouken@1895: dst[5] = ce; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_S8: slouken@1895: { slouken@1895: Sint8 *src, *dst, lf, rf, ce; slouken@942: slouken@1895: src = (Sint8 *) cvt->buf + cvt->len_cvt; slouken@1895: dst = (Sint8 *) cvt->buf + cvt->len_cvt * 3; slouken@1895: for (i = cvt->len_cvt; i; --i) { slouken@1895: dst -= 6; slouken@1895: src -= 2; slouken@1895: lf = src[0]; slouken@1895: rf = src[1]; slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: dst[0] = lf; slouken@1895: dst[1] = rf; slouken@1895: dst[2] = lf - ce; slouken@1895: dst[3] = rf - ce; slouken@1895: dst[4] = ce; slouken@1895: dst[5] = ce; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_U16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@1895: Uint16 lf, rf, ce, lr, rr; slouken@942: slouken@1895: src = cvt->buf + cvt->len_cvt; slouken@1895: dst = cvt->buf + cvt->len_cvt * 3; slouken@942: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 12; slouken@1895: src -= 4; slouken@1895: lf = (Uint16) ((src[0] << 8) | src[1]); slouken@1895: rf = (Uint16) ((src[2] << 8) | src[3]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[1] = (lf & 0xFF); slouken@1895: dst[0] = ((lf >> 8) & 0xFF); slouken@1895: dst[3] = (rf & 0xFF); slouken@1895: dst[2] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 4] = (lr & 0xFF); slouken@1895: dst[0 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[3 + 4] = (rr & 0xFF); slouken@1895: dst[2 + 4] = ((rr >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 8] = (ce & 0xFF); slouken@1895: dst[0 + 8] = ((ce >> 8) & 0xFF); slouken@1895: dst[3 + 8] = (ce & 0xFF); slouken@1895: dst[2 + 8] = ((ce >> 8) & 0xFF); slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 12; slouken@1895: src -= 4; slouken@1895: lf = (Uint16) ((src[1] << 8) | src[0]); slouken@1895: rf = (Uint16) ((src[3] << 8) | src[2]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[0] = (lf & 0xFF); slouken@1895: dst[1] = ((lf >> 8) & 0xFF); slouken@1895: dst[2] = (rf & 0xFF); slouken@1895: dst[3] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 4] = (lr & 0xFF); slouken@1895: dst[1 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[2 + 4] = (rr & 0xFF); slouken@1895: dst[3 + 4] = ((rr >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 8] = (ce & 0xFF); slouken@1895: dst[1 + 8] = ((ce >> 8) & 0xFF); slouken@1895: dst[2 + 8] = (ce & 0xFF); slouken@1895: dst[3 + 8] = ((ce >> 8) & 0xFF); slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_S16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@1895: Sint16 lf, rf, ce, lr, rr; slouken@942: slouken@1895: src = cvt->buf + cvt->len_cvt; slouken@1895: dst = cvt->buf + cvt->len_cvt * 3; slouken@942: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 12; slouken@1895: src -= 4; slouken@1895: lf = (Sint16) ((src[0] << 8) | src[1]); slouken@1895: rf = (Sint16) ((src[2] << 8) | src[3]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[1] = (lf & 0xFF); slouken@1895: dst[0] = ((lf >> 8) & 0xFF); slouken@1895: dst[3] = (rf & 0xFF); slouken@1895: dst[2] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 4] = (lr & 0xFF); slouken@1895: dst[0 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[3 + 4] = (rr & 0xFF); slouken@1895: dst[2 + 4] = ((rr >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 8] = (ce & 0xFF); slouken@1895: dst[0 + 8] = ((ce >> 8) & 0xFF); slouken@1895: dst[3 + 8] = (ce & 0xFF); slouken@1895: dst[2 + 8] = ((ce >> 8) & 0xFF); slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 12; slouken@1895: src -= 4; slouken@1895: lf = (Sint16) ((src[1] << 8) | src[0]); slouken@1895: rf = (Sint16) ((src[3] << 8) | src[2]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[0] = (lf & 0xFF); slouken@1895: dst[1] = ((lf >> 8) & 0xFF); slouken@1895: dst[2] = (rf & 0xFF); slouken@1895: dst[3] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 4] = (lr & 0xFF); slouken@1895: dst[1 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[2 + 4] = (rr & 0xFF); slouken@1895: dst[3 + 4] = ((rr >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 8] = (ce & 0xFF); slouken@1895: dst[1 + 8] = ((ce >> 8) & 0xFF); slouken@1895: dst[2 + 8] = (ce & 0xFF); slouken@1895: dst[3 + 8] = ((ce >> 8) & 0xFF); slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; icculus@1982: icculus@1982: case AUDIO_S32: icculus@1982: { icculus@1982: Sint32 lf, rf, ce; icculus@1982: const Uint32 *src = (const Uint32 *) cvt->buf + cvt->len_cvt; icculus@1982: Uint32 *dst = (Uint32 *) cvt->buf + cvt->len_cvt * 3; icculus@1982: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 6; icculus@1982: src -= 2; icculus@1982: lf = (Sint32) SDL_SwapBE32(src[0]); icculus@1982: rf = (Sint32) SDL_SwapBE32(src[1]); icculus@1982: ce = (lf / 2) + (rf / 2); icculus@1982: dst[0] = SDL_SwapBE32((Uint32) lf); icculus@1982: dst[1] = SDL_SwapBE32((Uint32) rf); icculus@1982: dst[2] = SDL_SwapBE32((Uint32) (lf - ce)); icculus@1982: dst[3] = SDL_SwapBE32((Uint32) (rf - ce)); icculus@1982: dst[4] = SDL_SwapBE32((Uint32) ce); icculus@1982: dst[5] = SDL_SwapBE32((Uint32) ce); icculus@1982: } icculus@1982: } else { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 6; icculus@1982: src -= 2; icculus@1982: lf = (Sint32) SDL_SwapLE32(src[0]); icculus@1982: rf = (Sint32) SDL_SwapLE32(src[1]); icculus@1982: ce = (lf / 2) + (rf / 2); icculus@1982: dst[0] = src[0]; icculus@1982: dst[1] = src[1]; icculus@1982: dst[2] = SDL_SwapLE32((Uint32) (lf - ce)); icculus@1982: dst[3] = SDL_SwapLE32((Uint32) (rf - ce)); icculus@1982: dst[4] = SDL_SwapLE32((Uint32) ce); icculus@1982: dst[5] = SDL_SwapLE32((Uint32) ce); icculus@1982: } icculus@1982: } icculus@1982: } icculus@1982: break; icculus@1982: icculus@1982: case AUDIO_F32: icculus@1982: { icculus@1982: float lf, rf, ce; icculus@2014: const float *src = (const float *) cvt->buf + cvt->len_cvt; icculus@2014: float *dst = (float *) cvt->buf + cvt->len_cvt * 3; icculus@1982: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 6; icculus@1982: src -= 2; icculus@2014: lf = SDL_SwapFloatBE(src[0]); icculus@2014: rf = SDL_SwapFloatBE(src[1]); icculus@1982: ce = (lf * 0.5f) + (rf * 0.5f); icculus@1982: dst[0] = src[0]; icculus@1982: dst[1] = src[1]; icculus@2014: dst[2] = SDL_SwapFloatBE(lf - ce); icculus@2014: dst[3] = SDL_SwapFloatBE(rf - ce); icculus@2014: dst[4] = dst[5] = SDL_SwapFloatBE(ce); icculus@1982: } icculus@1982: } else { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 6; icculus@1982: src -= 2; icculus@2014: lf = SDL_SwapFloatLE(src[0]); icculus@2014: rf = SDL_SwapFloatLE(src[1]); icculus@1982: ce = (lf * 0.5f) + (rf * 0.5f); icculus@1982: dst[0] = src[0]; icculus@1982: dst[1] = src[1]; icculus@2014: dst[2] = SDL_SwapFloatLE(lf - ce); icculus@2014: dst[3] = SDL_SwapFloatLE(rf - ce); icculus@2014: dst[4] = dst[5] = SDL_SwapFloatLE(ce); icculus@1982: } icculus@1982: } icculus@1982: } icculus@1982: break; icculus@1982: slouken@1895: } slouken@1895: cvt->len_cvt *= 3; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: slouken@942: /* Duplicate a stereo channel to a pseudo-4.0 stream */ icculus@1982: static void SDLCALL icculus@1982: SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting stereo to quad\n"); slouken@942: #endif slouken@942: slouken@1985: switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { slouken@1895: case AUDIO_U8: slouken@1895: { slouken@1895: Uint8 *src, *dst, lf, rf, ce; slouken@942: slouken@1895: src = (Uint8 *) (cvt->buf + cvt->len_cvt); slouken@1895: dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 2); slouken@1895: for (i = cvt->len_cvt; i; --i) { slouken@1895: dst -= 4; slouken@1895: src -= 2; slouken@1895: lf = src[0]; slouken@1895: rf = src[1]; slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: dst[0] = lf; slouken@1895: dst[1] = rf; slouken@1895: dst[2] = lf - ce; slouken@1895: dst[3] = rf - ce; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_S8: slouken@1895: { slouken@1895: Sint8 *src, *dst, lf, rf, ce; slouken@942: slouken@1895: src = (Sint8 *) cvt->buf + cvt->len_cvt; slouken@1895: dst = (Sint8 *) cvt->buf + cvt->len_cvt * 2; slouken@1895: for (i = cvt->len_cvt; i; --i) { slouken@1895: dst -= 4; slouken@1895: src -= 2; slouken@1895: lf = src[0]; slouken@1895: rf = src[1]; slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: dst[0] = lf; slouken@1895: dst[1] = rf; slouken@1895: dst[2] = lf - ce; slouken@1895: dst[3] = rf - ce; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_U16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@1895: Uint16 lf, rf, ce, lr, rr; slouken@942: slouken@1895: src = cvt->buf + cvt->len_cvt; slouken@1895: dst = cvt->buf + cvt->len_cvt * 2; slouken@942: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 8; slouken@1895: src -= 4; slouken@1895: lf = (Uint16) ((src[0] << 8) | src[1]); slouken@1895: rf = (Uint16) ((src[2] << 8) | src[3]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[1] = (lf & 0xFF); slouken@1895: dst[0] = ((lf >> 8) & 0xFF); slouken@1895: dst[3] = (rf & 0xFF); slouken@1895: dst[2] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 4] = (lr & 0xFF); slouken@1895: dst[0 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[3 + 4] = (rr & 0xFF); slouken@1895: dst[2 + 4] = ((rr >> 8) & 0xFF); slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 8; slouken@1895: src -= 4; slouken@1895: lf = (Uint16) ((src[1] << 8) | src[0]); slouken@1895: rf = (Uint16) ((src[3] << 8) | src[2]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[0] = (lf & 0xFF); slouken@1895: dst[1] = ((lf >> 8) & 0xFF); slouken@1895: dst[2] = (rf & 0xFF); slouken@1895: dst[3] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 4] = (lr & 0xFF); slouken@1895: dst[1 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[2 + 4] = (rr & 0xFF); slouken@1895: dst[3 + 4] = ((rr >> 8) & 0xFF); slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: slouken@1895: case AUDIO_S16: slouken@1895: { slouken@1895: Uint8 *src, *dst; slouken@1895: Sint16 lf, rf, ce, lr, rr; slouken@942: slouken@1895: src = cvt->buf + cvt->len_cvt; slouken@1895: dst = cvt->buf + cvt->len_cvt * 2; slouken@942: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 8; slouken@1895: src -= 4; slouken@1895: lf = (Sint16) ((src[0] << 8) | src[1]); slouken@1895: rf = (Sint16) ((src[2] << 8) | src[3]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[1] = (lf & 0xFF); slouken@1895: dst[0] = ((lf >> 8) & 0xFF); slouken@1895: dst[3] = (rf & 0xFF); slouken@1895: dst[2] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[1 + 4] = (lr & 0xFF); slouken@1895: dst[0 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[3 + 4] = (rr & 0xFF); slouken@1895: dst[2 + 4] = ((rr >> 8) & 0xFF); slouken@1895: } slouken@1895: } else { slouken@1895: for (i = cvt->len_cvt / 4; i; --i) { slouken@1895: dst -= 8; slouken@1895: src -= 4; slouken@1895: lf = (Sint16) ((src[1] << 8) | src[0]); slouken@1895: rf = (Sint16) ((src[3] << 8) | src[2]); slouken@1895: ce = (lf / 2) + (rf / 2); slouken@1895: rr = lf - ce; slouken@1895: lr = rf - ce; slouken@1895: dst[0] = (lf & 0xFF); slouken@1895: dst[1] = ((lf >> 8) & 0xFF); slouken@1895: dst[2] = (rf & 0xFF); slouken@1895: dst[3] = ((rf >> 8) & 0xFF); slouken@942: slouken@1895: dst[0 + 4] = (lr & 0xFF); slouken@1895: dst[1 + 4] = ((lr >> 8) & 0xFF); slouken@1895: dst[2 + 4] = (rr & 0xFF); slouken@1895: dst[3 + 4] = ((rr >> 8) & 0xFF); slouken@1895: } slouken@1895: } slouken@1895: } slouken@1895: break; slouken@942: icculus@1982: case AUDIO_S32: icculus@1982: { icculus@1982: const Uint32 *src = (const Uint32 *) (cvt->buf + cvt->len_cvt); icculus@1982: Uint32 *dst = (Uint32 *) (cvt->buf + cvt->len_cvt * 2); icculus@1982: Sint32 lf, rf, ce; slouken@942: icculus@1982: if (SDL_AUDIO_ISBIGENDIAN(format)) { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 4; icculus@1982: src -= 2; icculus@1982: lf = (Sint32) SDL_SwapBE32(src[0]); icculus@1982: rf = (Sint32) SDL_SwapBE32(src[1]); icculus@1982: ce = (lf / 2) + (rf / 2); icculus@1982: dst[0] = src[0]; icculus@1982: dst[1] = src[1]; icculus@1982: dst[2] = SDL_SwapBE32((Uint32) (lf - ce)); icculus@1982: dst[3] = SDL_SwapBE32((Uint32) (rf - ce)); icculus@1982: } icculus@1982: } else { icculus@1982: for (i = cvt->len_cvt / 8; i; --i) { icculus@1982: dst -= 4; icculus@1982: src -= 2; icculus@1982: lf = (Sint32) SDL_SwapLE32(src[0]); icculus@1982: rf = (Sint32) SDL_SwapLE32(src[1]); icculus@1982: ce = (lf / 2) + (rf / 2); icculus@1982: dst[0] = src[0]; icculus@1982: dst[1] = src[1]; icculus@1982: dst[2] = SDL_SwapLE32((Uint32) (lf - ce)); icculus@1982: dst[3] = SDL_SwapLE32((Uint32) (rf - ce)); icculus@1982: } icculus@1982: } slouken@1895: } slouken@1895: break; slouken@1895: } slouken@1895: cvt->len_cvt *= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@0: } slouken@0: icculus@1982: /* Convert rate up by multiple of 2 */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateMUL2(SDL_AudioCVT * cvt, SDL_AudioFormat format) icculus@1982: { icculus@1982: int i; icculus@1982: icculus@1982: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate * 2 (mono)\n"); icculus@1982: #endif icculus@1982: slouken@1985: #define mul2_mono(type) { \ icculus@1982: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ icculus@1982: type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \ icculus@1982: for (i = cvt->len_cvt / sizeof (type); i; --i) { \ icculus@1982: src--; \ icculus@1982: dst[-1] = dst[-2] = src[0]; \ icculus@1982: dst -= 2; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { icculus@1982: case 8: icculus@1982: mul2_mono(Uint8); icculus@1982: break; icculus@1982: case 16: icculus@1982: mul2_mono(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: mul2_mono(Uint32); icculus@1982: break; icculus@1982: } icculus@1982: slouken@1985: #undef mul2_mono icculus@1982: icculus@1982: cvt->len_cvt *= 2; icculus@1982: if (cvt->filters[++cvt->filter_index]) { icculus@1982: cvt->filters[cvt->filter_index] (cvt, format); icculus@1982: } icculus@1982: } icculus@1982: slouken@942: slouken@942: /* Convert rate up by multiple of 2, for stereo */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateMUL2_c2(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate * 2 (stereo)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define mul2_stereo(type) { \ icculus@1982: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ icculus@1982: type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 2); i; --i) { \ icculus@1982: const type r = src[-1]; \ icculus@1982: const type l = src[-2]; \ icculus@1982: src -= 2; \ icculus@1982: dst[-1] = r; \ icculus@1982: dst[-2] = l; \ icculus@1982: dst[-3] = r; \ icculus@1982: dst[-4] = l; \ icculus@1982: dst -= 4; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: mul2_stereo(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: mul2_stereo(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: mul2_stereo(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef mul2_stereo icculus@1982: slouken@1895: cvt->len_cvt *= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: /* Convert rate up by multiple of 2, for quad */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateMUL2_c4(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate * 2 (quad)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define mul2_quad(type) { \ icculus@1982: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ icculus@1982: type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 4); i; --i) { \ icculus@1982: const type c1 = src[-1]; \ icculus@1982: const type c2 = src[-2]; \ icculus@1982: const type c3 = src[-3]; \ icculus@1982: const type c4 = src[-4]; \ icculus@1982: src -= 4; \ icculus@1982: dst[-1] = c1; \ icculus@1982: dst[-2] = c2; \ icculus@1982: dst[-3] = c3; \ icculus@1982: dst[-4] = c4; \ icculus@1982: dst[-5] = c1; \ icculus@1982: dst[-6] = c2; \ icculus@1982: dst[-7] = c3; \ icculus@1982: dst[-8] = c4; \ icculus@1982: dst -= 8; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: mul2_quad(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: mul2_quad(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: mul2_quad(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef mul2_quad icculus@1982: slouken@1895: cvt->len_cvt *= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: slouken@942: /* Convert rate up by multiple of 2, for 5.1 */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateMUL2_c6(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate * 2 (six channels)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define mul2_chansix(type) { \ icculus@1982: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ icculus@1982: type *dst = (type *) (cvt->buf + (cvt->len_cvt * 2)); \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \ icculus@1982: const type c1 = src[-1]; \ icculus@1982: const type c2 = src[-2]; \ icculus@1982: const type c3 = src[-3]; \ icculus@1982: const type c4 = src[-4]; \ icculus@1982: const type c5 = src[-5]; \ icculus@1982: const type c6 = src[-6]; \ icculus@1982: src -= 6; \ icculus@1982: dst[-1] = c1; \ icculus@1982: dst[-2] = c2; \ icculus@1982: dst[-3] = c3; \ icculus@1982: dst[-4] = c4; \ icculus@1982: dst[-5] = c5; \ icculus@1982: dst[-6] = c6; \ icculus@1982: dst[-7] = c1; \ icculus@1982: dst[-8] = c2; \ icculus@1982: dst[-9] = c3; \ icculus@1982: dst[-10] = c4; \ icculus@1982: dst[-11] = c5; \ icculus@1982: dst[-12] = c6; \ icculus@1982: dst -= 12; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: mul2_chansix(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: mul2_chansix(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: mul2_chansix(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef mul2_chansix icculus@1982: slouken@1895: cvt->len_cvt *= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@0: /* Convert rate down by multiple of 2 */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateDIV2(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@0: { slouken@1895: int i; slouken@0: slouken@0: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate / 2 (mono)\n"); slouken@0: #endif icculus@1982: slouken@1985: #define div2_mono(type) { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 2); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: src += 2; \ icculus@1982: dst++; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: div2_mono(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: div2_mono(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: div2_mono(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef div2_mono icculus@1982: slouken@1895: cvt->len_cvt /= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@0: } slouken@0: slouken@942: slouken@942: /* Convert rate down by multiple of 2, for stereo */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateDIV2_c2(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate / 2 (stereo)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define div2_stereo(type) { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 4); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: dst[1] = src[1]; \ icculus@1982: src += 4; \ icculus@1982: dst += 2; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: div2_stereo(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: div2_stereo(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: div2_stereo(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef div2_stereo icculus@1982: slouken@1895: cvt->len_cvt /= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: slouken@942: /* Convert rate down by multiple of 2, for quad */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateDIV2_c4(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate / 2 (quad)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define div2_quad(type) { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 8); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: dst[1] = src[1]; \ icculus@1982: dst[2] = src[2]; \ icculus@1982: dst[3] = src[3]; \ icculus@1982: src += 8; \ icculus@1982: dst += 4; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: div2_quad(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: div2_quad(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: div2_quad(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef div2_quad icculus@1982: slouken@1895: cvt->len_cvt /= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@942: /* Convert rate down by multiple of 2, for 5.1 */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateDIV2_c6(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@942: { slouken@1895: int i; slouken@942: slouken@942: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "Converting audio rate / 2 (six channels)\n"); slouken@942: #endif icculus@1982: slouken@1985: #define div2_chansix(type) { \ icculus@1982: const type *src = (const type *) cvt->buf; \ icculus@1982: type *dst = (type *) cvt->buf; \ icculus@1982: for (i = cvt->len_cvt / (sizeof (type) * 12); i; --i) { \ icculus@1982: dst[0] = src[0]; \ icculus@1982: dst[1] = src[1]; \ icculus@1982: dst[2] = src[2]; \ icculus@1982: dst[3] = src[3]; \ icculus@1982: dst[4] = src[4]; \ icculus@1982: dst[5] = src[5]; \ icculus@1982: src += 12; \ icculus@1982: dst += 6; \ icculus@1982: } \ icculus@1982: } icculus@1982: icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: icculus@1982: div2_chansix(Uint8); slouken@1895: break; slouken@1895: case 16: icculus@1982: div2_chansix(Uint16); icculus@1982: break; icculus@1982: case 32: icculus@1982: div2_chansix(Uint32); slouken@1895: break; slouken@1895: } icculus@1982: slouken@1985: #undef div_chansix icculus@1982: slouken@1895: cvt->len_cvt /= 2; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@942: } slouken@942: slouken@0: /* Very slow rate conversion routine */ icculus@1982: static void SDLCALL icculus@1982: SDL_RateSLOW(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@0: { slouken@1895: double ipos; slouken@1895: int i, clen; slouken@0: slouken@0: #ifdef DEBUG_CONVERT slouken@1895: fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0 / cvt->rate_incr); slouken@0: #endif slouken@1895: clen = (int) ((double) cvt->len_cvt / cvt->rate_incr); slouken@1895: if (cvt->rate_incr > 1.0) { icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: slouken@1895: { slouken@1895: Uint8 *output; slouken@0: slouken@1895: output = cvt->buf; slouken@1895: ipos = 0.0; slouken@1895: for (i = clen; i; --i) { slouken@1895: *output = cvt->buf[(int) ipos]; slouken@1895: ipos += cvt->rate_incr; slouken@1895: output += 1; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@0: slouken@1895: case 16: slouken@1895: { slouken@1895: Uint16 *output; slouken@0: slouken@1895: clen &= ~1; slouken@1895: output = (Uint16 *) cvt->buf; slouken@1895: ipos = 0.0; slouken@1895: for (i = clen / 2; i; --i) { slouken@1895: *output = ((Uint16 *) cvt->buf)[(int) ipos]; slouken@1895: ipos += cvt->rate_incr; slouken@1895: output += 1; slouken@1895: } slouken@1895: } slouken@1895: break; icculus@1982: icculus@1982: case 32: icculus@1982: { icculus@1982: /* !!! FIXME: need 32-bit converter here! */ slouken@2130: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "FIXME: need 32-bit converter here!\n"); slouken@2130: #endif icculus@1982: } slouken@1895: } slouken@1895: } else { icculus@1982: switch (SDL_AUDIO_BITSIZE(format)) { slouken@1895: case 8: slouken@1895: { slouken@1895: Uint8 *output; slouken@0: slouken@1895: output = cvt->buf + clen; slouken@1895: ipos = (double) cvt->len_cvt; slouken@1895: for (i = clen; i; --i) { slouken@1895: ipos -= cvt->rate_incr; slouken@1895: output -= 1; slouken@1895: *output = cvt->buf[(int) ipos]; slouken@1895: } slouken@1895: } slouken@1895: break; slouken@0: slouken@1895: case 16: slouken@1895: { slouken@1895: Uint16 *output; slouken@0: slouken@1895: clen &= ~1; slouken@1895: output = (Uint16 *) (cvt->buf + clen); slouken@1895: ipos = (double) cvt->len_cvt / 2; slouken@1895: for (i = clen / 2; i; --i) { slouken@1895: ipos -= cvt->rate_incr; slouken@1895: output -= 1; slouken@1895: *output = ((Uint16 *) cvt->buf)[(int) ipos]; slouken@1895: } slouken@1895: } slouken@1895: break; icculus@1982: icculus@1982: case 32: icculus@1982: { icculus@1982: /* !!! FIXME: need 32-bit converter here! */ slouken@2130: #ifdef DEBUG_CONVERT icculus@1982: fprintf(stderr, "FIXME: need 32-bit converter here!\n"); slouken@2130: #endif icculus@1982: } slouken@1895: } slouken@1895: } icculus@1982: slouken@1895: cvt->len_cvt = clen; slouken@1895: if (cvt->filters[++cvt->filter_index]) { slouken@1895: cvt->filters[cvt->filter_index] (cvt, format); slouken@1895: } slouken@0: } slouken@0: slouken@1895: int slouken@1895: SDL_ConvertAudio(SDL_AudioCVT * cvt) slouken@0: { slouken@1895: /* Make sure there's data to convert */ slouken@1895: if (cvt->buf == NULL) { slouken@1895: SDL_SetError("No buffer allocated for conversion"); slouken@1895: return (-1); slouken@1895: } slouken@1895: /* Return okay if no conversion is necessary */ slouken@1895: cvt->len_cvt = cvt->len; slouken@1895: if (cvt->filters[0] == NULL) { slouken@1895: return (0); slouken@1895: } slouken@0: slouken@1895: /* Set up the conversion and go! */ slouken@1895: cvt->filter_index = 0; slouken@1895: cvt->filters[0] (cvt, cvt->src_format); slouken@1895: return (0); slouken@0: } slouken@0: icculus@1982: icculus@1982: static SDL_AudioFilter icculus@1982: SDL_HandTunedTypeCVT(SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt) icculus@1982: { icculus@1982: /* icculus@1982: * Fill in any future conversions that are specialized to a icculus@1982: * processor, platform, compiler, or library here. icculus@1982: */ icculus@1982: slouken@1985: return NULL; /* no specialized converter code available. */ icculus@1982: } icculus@1982: icculus@1982: icculus@1982: /* icculus@1982: * Find a converter between two data types. We try to select a hand-tuned icculus@1982: * asm/vectorized/optimized function first, and then fallback to an icculus@1982: * autogenerated function that is customized to convert between two icculus@1982: * specific data types. icculus@1982: */ icculus@1982: static int icculus@1982: SDL_BuildAudioTypeCVT(SDL_AudioCVT * cvt, icculus@1982: SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt) icculus@1982: { icculus@1982: if (src_fmt != dst_fmt) { icculus@1982: const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt); icculus@1982: const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt); icculus@1982: SDL_AudioFilter filter = SDL_HandTunedTypeCVT(src_fmt, dst_fmt); icculus@1982: icculus@1982: /* No hand-tuned converter? Try the autogenerated ones. */ icculus@1982: if (filter == NULL) { icculus@1982: int i; icculus@1982: for (i = 0; sdl_audio_type_filters[i].filter != NULL; i++) { icculus@1982: const SDL_AudioTypeFilters *filt = &sdl_audio_type_filters[i]; icculus@1982: if ((filt->src_fmt == src_fmt) && (filt->dst_fmt == dst_fmt)) { icculus@1982: filter = filt->filter; icculus@1982: break; icculus@1982: } icculus@1982: } icculus@1982: icculus@1982: if (filter == NULL) { slouken@1985: return -1; /* Still no matching converter?! */ icculus@1982: } icculus@1982: } icculus@1982: icculus@1982: /* Update (cvt) with filter details... */ icculus@1982: cvt->filters[cvt->filter_index++] = filter; icculus@1982: if (src_bitsize < dst_bitsize) { icculus@1982: const int mult = (dst_bitsize / src_bitsize); icculus@1982: cvt->len_mult *= mult; icculus@1982: cvt->len_ratio *= mult; icculus@1982: } else if (src_bitsize > dst_bitsize) { icculus@1982: cvt->len_ratio /= (src_bitsize / dst_bitsize); icculus@1982: } icculus@1982: slouken@1985: return 1; /* added a converter. */ icculus@1982: } icculus@1982: slouken@1985: return 0; /* no conversion necessary. */ icculus@1982: } icculus@1982: slouken@2716: /* Generate the necessary IIR lowpass coefficients for resampling. slouken@2716: Assume that the SDL_AudioCVT struct is already set up with slouken@2716: the correct values for len_mult and len_div, and use the slouken@2716: type of dst_format. Also assume the buffer is allocated. slouken@2716: Note the buffer needs to be 6 units long. slouken@2716: For now, use RBJ's cookbook coefficients. It might be more slouken@2716: optimal to create a Butterworth filter, but this is more difficult. slouken@2716: */ slouken@2716: int slouken@2716: SDL_BuildIIRLowpass(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@2716: { slouken@2716: float fc; /* cutoff frequency */ slouken@2716: float coeff[6]; /* floating point iir coefficients b0, b1, b2, a0, a1, a2 */ slouken@2716: float scale; slouken@2716: float w0, alpha, cosw0; slouken@2716: int i; slouken@2716: slouken@2716: /* The higher Q is, the higher CUTOFF can be. Need to find a good balance to avoid aliasing */ slouken@2716: static const float Q = 5.0f; slouken@2716: static const float CUTOFF = 0.4f; slouken@2716: slouken@2716: fc = (cvt->len_mult > slouken@2716: cvt->len_div) ? CUTOFF / (float) cvt->len_mult : CUTOFF / slouken@2716: (float) cvt->len_div; slouken@2716: slouken@2716: w0 = 2.0f * M_PI * fc; slouken@2716: cosw0 = cosf(w0); slouken@2728: alpha = sinf(w0) / (2.0f * Q); slouken@2716: slouken@2716: /* Compute coefficients, normalizing by a0 */ slouken@2716: scale = 1.0f / (1.0f + alpha); slouken@2716: slouken@2716: coeff[0] = (1.0f - cosw0) / 2.0f * scale; slouken@2716: coeff[1] = (1.0f - cosw0) * scale; slouken@2716: coeff[2] = coeff[0]; slouken@2716: slouken@2716: coeff[3] = 1.0f; /* a0 is normalized to 1 */ slouken@2716: coeff[4] = -2.0f * cosw0 * scale; slouken@2716: coeff[5] = (1.0f - alpha) * scale; slouken@2716: slouken@2716: /* Copy the coefficients to the struct. If necessary, convert coefficients to fixed point, using the range (-2.0, 2.0) */ slouken@2716: #define convert_fixed(type, fix) { \ slouken@2716: type *cvt_coeff = (type *)cvt->coeff; \ slouken@2716: for(i = 0; i < 6; ++i) { \ slouken@2716: cvt_coeff[i] = fix(coeff[i]); \ slouken@2716: } \ slouken@2716: } slouken@2716: slouken@2716: if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { slouken@2716: float *cvt_coeff = (float *) cvt->coeff; slouken@2716: for (i = 0; i < 6; ++i) { slouken@2716: cvt_coeff[i] = coeff[i]; slouken@2716: } slouken@2716: } else { slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: convert_fixed(Uint8, SDL_Make_2_6); slouken@2716: break; slouken@2716: case 16: slouken@2716: convert_fixed(Uint16, SDL_Make_2_14); slouken@2716: break; slouken@2716: case 32: slouken@2716: convert_fixed(Uint32, SDL_Make_2_30); slouken@2716: break; slouken@2716: } slouken@2716: } slouken@2716: slouken@2716: #ifdef DEBUG_CONVERT slouken@2716: #define debug_iir(type) { \ slouken@2716: type *cvt_coeff = (type *)cvt->coeff; \ slouken@2716: for(i = 0; i < 6; ++i) { \ slouken@2716: printf("coeff[%u] = %f = 0x%x\n", i, coeff[i], cvt_coeff[i]); \ slouken@2716: } \ slouken@2716: } slouken@2716: if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { slouken@2716: float *cvt_coeff = (float *) cvt->coeff; slouken@2716: for (i = 0; i < 6; ++i) { slouken@2716: printf("coeff[%u] = %f = %f\n", i, coeff[i], cvt_coeff[i]); slouken@2716: } slouken@2716: } else { slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: debug_iir(Uint8); slouken@2716: break; slouken@2716: case 16: slouken@2716: debug_iir(Uint16); slouken@2716: break; slouken@2716: case 32: slouken@2716: debug_iir(Uint32); slouken@2716: break; slouken@2716: } slouken@2716: } slouken@2716: #undef debug_iir slouken@2716: #endif slouken@2716: slouken@2716: /* Initialize the state buffer to all zeroes, and set initial position */ slouken@2728: SDL_memset(cvt->state_buf, 0, 4 * SDL_AUDIO_BITSIZE(format) / 4); slouken@2716: cvt->state_pos = 0; slouken@2716: #undef convert_fixed slouken@2716: } slouken@2716: slouken@2716: /* Apply the lowpass IIR filter to the given SDL_AudioCVT struct */ slouken@2716: /* This was implemented because it would be much faster than the fir filter, slouken@2716: but it doesn't seem to have a steep enough cutoff so we'd need several slouken@2716: cascaded biquads, which probably isn't a great idea. Therefore, this slouken@2716: function can probably be discarded. slouken@2716: */ slouken@2716: static void slouken@2716: SDL_FilterIIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@2716: { slouken@2716: Uint32 i, n; slouken@2716: slouken@2716: /* TODO: Check that n is calculated right */ slouken@2716: n = 8 * cvt->len_cvt / SDL_AUDIO_BITSIZE(format); slouken@2716: slouken@2716: /* 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. */ slouken@2716: /* cvt->state_pos = 1: state[0] = x_n-1, state[1] = x_n-2, state[2] = y_n-1, state[3] - y_n-2 */ slouken@2716: #define iir_fix(type, mult) {\ slouken@2716: type *coeff = (type *)cvt->coeff; \ slouken@2716: type *state = (type *)cvt->state_buf; \ slouken@2716: type *buf = (type *)cvt->buf; \ slouken@2716: type temp; \ slouken@2716: for(i = 0; i < n; ++i) { \ slouken@2716: temp = buf[i] >> 1; \ slouken@2716: if(cvt->state_pos) { \ slouken@2716: 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]); \ slouken@2716: state[1] = temp; \ slouken@2716: state[3] = buf[i]; \ slouken@2716: cvt->state_pos = 0; \ slouken@2716: } else { \ slouken@2716: 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]); \ slouken@2716: state[0] = temp; \ slouken@2716: state[2] = buf[i]; \ slouken@2716: cvt->state_pos = 1; \ slouken@2716: } \ slouken@2716: } \ slouken@2716: } slouken@2716: /* Need to test to see if the previous method or this one is faster */ slouken@2716: /*#define iir_fix(type, mult) {\ slouken@2716: type *coeff = (type *)cvt->coeff; \ slouken@2716: type *state = (type *)cvt->state_buf; \ slouken@2716: type *buf = (type *)cvt->buf; \ slouken@2716: type temp; \ slouken@2716: for(i = 0; i < n; ++i) { \ slouken@2716: temp = buf[i] >> 1; \ slouken@2716: 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]); \ slouken@2716: state[1] = state[0]; \ slouken@2716: state[0] = temp; \ slouken@2716: state[3] = state[2]; \ slouken@2716: state[2] = buf[i]; \ slouken@2716: } \ slouken@2716: }*/ slouken@2716: slouken@2716: if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { slouken@2716: float *coeff = (float *) cvt->coeff; slouken@2716: float *state = (float *) cvt->state_buf; slouken@2716: float *buf = (float *) cvt->buf; slouken@2716: float temp; slouken@2716: slouken@2716: for (i = 0; i < n; ++i) { slouken@2716: /* y[n] = b0 * x[n] + b1 * x[n-1] + b2 * x[n-2] - a1 * y[n-1] - a[2] * y[n-2] */ slouken@2716: temp = buf[i]; slouken@2716: if (cvt->state_pos) { slouken@2716: buf[i] = slouken@2716: coeff[0] * buf[n] + coeff[1] * state[0] + slouken@2716: coeff[2] * state[1] - coeff[4] * state[2] - slouken@2716: coeff[5] * state[3]; slouken@2716: state[1] = temp; slouken@2716: state[3] = buf[i]; slouken@2716: cvt->state_pos = 0; slouken@2716: } else { slouken@2716: buf[i] = slouken@2716: coeff[0] * buf[n] + coeff[1] * state[1] + slouken@2716: coeff[2] * state[0] - coeff[4] * state[3] - slouken@2716: coeff[5] * state[2]; slouken@2716: state[0] = temp; slouken@2716: state[2] = buf[i]; slouken@2716: cvt->state_pos = 1; slouken@2716: } slouken@2716: } slouken@2716: } else { slouken@2716: /* Treat everything as signed! */ slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: iir_fix(Sint8, SDL_FixMpy8); slouken@2716: break; slouken@2716: case 16: slouken@2716: iir_fix(Sint16, SDL_FixMpy16); slouken@2716: break; slouken@2716: case 32: slouken@2716: iir_fix(Sint32, SDL_FixMpy32); slouken@2716: break; slouken@2716: } slouken@2716: } slouken@2716: #undef iir_fix slouken@2716: } slouken@2716: slouken@2716: /* Apply the windowed sinc FIR filter to the given SDL_AudioCVT struct. slouken@2716: */ slouken@2716: static void slouken@2716: SDL_FilterFIR(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@2716: { slouken@2716: int n = 8 * cvt->len_cvt / SDL_AUDIO_BITSIZE(format); slouken@2716: int m = cvt->len_sinc; slouken@2716: int i, j; slouken@2716: slouken@2716: /* slouken@2716: Note: We can make a big optimization here by taking advantage slouken@2716: of the fact that the signal is zero stuffed, so we can do slouken@2716: significantly fewer multiplications and additions. However, this slouken@2716: depends on the zero stuffing ratio, so it may not pay off. This would slouken@2716: basically be a polyphase filter. slouken@2716: */ slouken@2716: /* One other way to do this fast is to look at the fir filter from a different angle: slouken@2716: After we zero stuff, we have input of all zeroes, except for every len_mult slouken@2716: sample. If we choose a sinc length equal to len_mult, then the fir filter becomes slouken@2716: much more simple: we're just taking a windowed sinc, shifting it to start at each slouken@2716: len_mult sample, and scaling it by the value of that sample. If we do this, then slouken@2716: we don't even need to worry about the sample histories, and the inner loop here is slouken@2716: unnecessary. This probably sacrifices some quality but could really speed things up as well. slouken@2716: */ slouken@2716: /* We only calculate the values of samples which are 0 (mod len_div) because slouken@2716: those are the only ones used. All the other ones are discarded in the slouken@2716: third step of resampling. This is a huge speedup. As a warning, though, slouken@2716: if for some reason this is used elsewhere where there are no samples discarded, slouken@2716: the output will not be corrrect if len_div is not 1. To make this filter a slouken@2716: generic FIR filter, simply remove the if statement "if(i % cvt->len_div == 0)" slouken@2716: around the inner loop so that every sample is processed. slouken@2716: */ slouken@2716: /* This is basically just a FIR filter. i.e. for input x_n and m coefficients, slouken@2716: y_n = x_n*sinc_0 + x_(n-1)*sinc_1 + x_(n-2)*sinc_2 + ... + x_(n-m+1)*sinc_(m-1) slouken@2716: */ slouken@2716: #define filter_sinc(type, mult) { \ slouken@2716: type *sinc = (type *)cvt->coeff; \ slouken@2716: type *state = (type *)cvt->state_buf; \ slouken@2716: type *buf = (type *)cvt->buf; \ slouken@2716: for(i = 0; i < n; ++i) { \ slouken@2716: state[cvt->state_pos] = buf[i]; \ slouken@2716: buf[i] = 0; \ slouken@2716: if( i % cvt->len_div == 0 ) { \ slouken@2716: for(j = 0; j < m; ++j) { \ slouken@2716: buf[i] += mult(sinc[j], state[(cvt->state_pos + j) % m]); \ slouken@2716: } \ slouken@2716: }\ slouken@2716: cvt->state_pos = (cvt->state_pos + 1) % m; \ slouken@2716: } \ slouken@2716: } slouken@2716: slouken@2716: if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { slouken@2716: filter_sinc(float, SDL_FloatMpy); slouken@2716: } else { slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: filter_sinc(Sint8, SDL_FixMpy8); slouken@2716: break; slouken@2716: case 16: slouken@2716: filter_sinc(Sint16, SDL_FixMpy16); slouken@2716: break; slouken@2716: case 32: slouken@2716: filter_sinc(Sint32, SDL_FixMpy32); slouken@2716: break; slouken@2716: } slouken@2716: } slouken@2716: slouken@2716: #undef filter_sinc slouken@2716: slouken@2716: } slouken@2716: slouken@2716: /* Generate the necessary windowed sinc filter for resampling. slouken@2716: Assume that the SDL_AudioCVT struct is already set up with slouken@2716: the correct values for len_mult and len_div, and use the slouken@2716: type of dst_format. Also assume the buffer is allocated. slouken@2716: Note the buffer needs to be m+1 units long. slouken@2716: */ slouken@2716: int slouken@2716: SDL_BuildWindowedSinc(SDL_AudioCVT * cvt, SDL_AudioFormat format, slouken@2716: unsigned int m) slouken@2716: { slouken@2716: float fScale; /* scale factor for fixed point */ slouken@2716: float *fSinc; /* floating point sinc buffer, to be converted to fixed point */ slouken@2716: float fc; /* cutoff frequency */ slouken@2716: float two_pi_fc, two_pi_over_m, four_pi_over_m, m_over_two; slouken@2716: float norm_sum, norm_fact; slouken@2716: unsigned int i; slouken@2716: slouken@2716: /* Check that the buffer is allocated */ slouken@2716: if (cvt->coeff == NULL) { slouken@2716: return -1; slouken@2716: } slouken@2716: slouken@2716: /* Set the length */ slouken@2716: cvt->len_sinc = m + 1; slouken@2716: slouken@2716: /* Allocate the floating point windowed sinc. */ slouken@2728: fSinc = SDL_stack_alloc(float, (m + 1)); slouken@2716: if (fSinc == NULL) { slouken@2716: return -1; slouken@2716: } slouken@2716: slouken@2716: /* Set up the filter parameters */ slouken@2716: fc = (cvt->len_mult > slouken@2716: cvt->len_div) ? 0.5f / (float) cvt->len_mult : 0.5f / slouken@2716: (float) cvt->len_div; slouken@2716: #ifdef DEBUG_CONVERT slouken@2716: printf("Lowpass cutoff frequency = %f\n", fc); slouken@2716: #endif slouken@2716: two_pi_fc = 2.0f * M_PI * fc; slouken@2716: two_pi_over_m = 2.0f * M_PI / (float) m; slouken@2716: four_pi_over_m = 2.0f * two_pi_over_m; slouken@2716: m_over_two = (float) m / 2.0f; slouken@2716: norm_sum = 0.0f; slouken@2716: slouken@2716: for (i = 0; i <= m; ++i) { slouken@2716: if (i == m / 2) { slouken@2716: fSinc[i] = two_pi_fc; slouken@2716: } else { slouken@2716: fSinc[i] = slouken@2716: sinf(two_pi_fc * ((float) i - m_over_two)) / ((float) i - slouken@2716: m_over_two); slouken@2716: /* Apply blackman window */ slouken@2716: fSinc[i] *= slouken@2716: 0.42f - 0.5f * cosf(two_pi_over_m * (float) i) + slouken@2716: 0.08f * cosf(four_pi_over_m * (float) i); slouken@2716: } slouken@2716: norm_sum += fabs(fSinc[i]); slouken@2716: } slouken@2716: slouken@2716: norm_fact = 1.0f / norm_sum; slouken@2716: slouken@2716: #define convert_fixed(type, fix) { \ slouken@2716: type *dst = (type *)cvt->coeff; \ slouken@2716: for( i = 0; i <= m; ++i ) { \ slouken@2716: dst[i] = fix(fSinc[i] * norm_fact); \ slouken@2716: } \ slouken@2716: } slouken@2716: slouken@2716: /* If we're using floating point, we only need to normalize */ slouken@2716: if (SDL_AUDIO_ISFLOAT(format) && SDL_AUDIO_BITSIZE(format) == 32) { slouken@2716: float *fDest = (float *) cvt->coeff; slouken@2716: for (i = 0; i <= m; ++i) { slouken@2716: fDest[i] = fSinc[i] * norm_fact; slouken@2716: } slouken@2716: } else { slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: convert_fixed(Uint8, SDL_Make_1_7); slouken@2716: break; slouken@2716: case 16: slouken@2716: convert_fixed(Uint16, SDL_Make_1_15); slouken@2716: break; slouken@2716: case 32: slouken@2716: convert_fixed(Uint32, SDL_Make_1_31); slouken@2716: break; slouken@2716: } slouken@2716: } slouken@2716: slouken@2716: /* Initialize the state buffer to all zeroes, and set initial position */ slouken@2728: SDL_memset(cvt->state_buf, 0, slouken@2728: cvt->len_sinc * SDL_AUDIO_BITSIZE(format) / 4); slouken@2716: cvt->state_pos = 0; slouken@2716: slouken@2716: /* Clean up */ slouken@2716: #undef convert_fixed slouken@2728: SDL_stack_free(fSinc); slouken@2716: } slouken@2716: slouken@2716: /* This is used to reduce the resampling ratio */ slouken@2728: static __inline__ int slouken@2716: SDL_GCD(int a, int b) slouken@2716: { slouken@2716: int temp; slouken@2716: while (b != 0) { slouken@2716: temp = a % b; slouken@2716: a = b; slouken@2716: b = temp; slouken@2716: } slouken@2716: return a; slouken@2716: } slouken@2716: slouken@2716: /* Perform proper resampling. This is pretty slow but it's the best-sounding method. */ slouken@2716: static void SDLCALL slouken@2716: SDL_Resample(SDL_AudioCVT * cvt, SDL_AudioFormat format) slouken@2716: { slouken@2716: int i, j; slouken@2716: slouken@2716: #ifdef DEBUG_CONVERT slouken@2716: printf("Converting audio rate via proper resampling (mono)\n"); slouken@2716: #endif slouken@2716: slouken@2716: #define zerostuff_mono(type) { \ slouken@2716: const type *src = (const type *) (cvt->buf + cvt->len_cvt); \ slouken@2716: type *dst = (type *) (cvt->buf + (cvt->len_cvt * cvt->len_mult)); \ slouken@2716: for (i = cvt->len_cvt / sizeof (type); i; --i) { \ slouken@2716: src--; \ slouken@2716: dst[-1] = src[0]; \ slouken@2716: for( j = -cvt->len_mult; j < -1; ++j ) { \ slouken@2716: dst[j] = 0; \ slouken@2716: } \ slouken@2716: dst -= cvt->len_mult; \ slouken@2716: } \ slouken@2716: } slouken@2716: slouken@2716: #define discard_mono(type) { \ slouken@2716: const type *src = (const type *) (cvt->buf); \ slouken@2716: type *dst = (type *) (cvt->buf); \ slouken@2716: for (i = 0; i < (cvt->len_cvt / sizeof(type)) / cvt->len_div; ++i) { \ slouken@2716: dst[0] = src[0]; \ slouken@2716: src += cvt->len_div; \ slouken@2716: ++dst; \ slouken@2716: } \ slouken@2716: } slouken@2716: slouken@2716: /* Step 1: Zero stuff the conversion buffer. This upsamples by a factor of len_mult, slouken@2716: creating aliasing at frequencies above the original nyquist frequency. slouken@2716: */ slouken@2716: #ifdef DEBUG_CONVERT slouken@2716: printf("Zero-stuffing by a factor of %u\n", cvt->len_mult); slouken@2716: #endif slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: zerostuff_mono(Uint8); slouken@2716: break; slouken@2716: case 16: slouken@2716: zerostuff_mono(Uint16); slouken@2716: break; slouken@2716: case 32: slouken@2716: zerostuff_mono(Uint32); slouken@2716: break; slouken@2716: } slouken@2716: slouken@2716: cvt->len_cvt *= cvt->len_mult; slouken@2716: slouken@2716: /* Step 2: Use a windowed sinc FIR filter (lowpass filter) to remove the alias slouken@2716: frequencies. This is the slow part. slouken@2716: */ slouken@2716: SDL_FilterFIR(cvt, format); slouken@2716: slouken@2716: /* Step 3: Now downsample by discarding samples. */ slouken@2716: slouken@2716: #ifdef DEBUG_CONVERT slouken@2716: printf("Discarding samples by a factor of %u\n", cvt->len_div); slouken@2716: #endif slouken@2716: switch (SDL_AUDIO_BITSIZE(format)) { slouken@2716: case 8: slouken@2716: discard_mono(Uint8); slouken@2716: break; slouken@2716: case 16: slouken@2716: discard_mono(Uint16); slouken@2716: break; slouken@2716: case 32: slouken@2716: discard_mono(Uint32); slouken@2716: break; slouken@2716: } slouken@2716: slouken@2716: #undef zerostuff_mono slouken@2716: #undef discard_mono slouken@2716: slouken@2716: cvt->len_cvt /= cvt->len_div; slouken@2716: slouken@2716: if (cvt->filters[++cvt->filter_index]) { slouken@2716: cvt->filters[cvt->filter_index] (cvt, format); slouken@2716: } slouken@2716: } icculus@1982: icculus@1982: icculus@1982: /* Creates a set of audio filters to convert from one format to another. icculus@1982: Returns -1 if the format conversion is not supported, 0 if there's icculus@1982: no conversion needed, or 1 if the audio filter is set up. slouken@0: */ slouken@1895: slouken@1895: int slouken@1895: SDL_BuildAudioCVT(SDL_AudioCVT * cvt, icculus@1982: SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate, icculus@1982: SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate) slouken@0: { icculus@1982: /* there are no unsigned types over 16 bits, so catch this upfront. */ icculus@1982: if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) { icculus@1982: return -1; icculus@1982: } icculus@1982: if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) { icculus@1982: return -1; icculus@1982: } slouken@1985: #ifdef DEBUG_CONVERT icculus@1982: printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n", slouken@1985: src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate); slouken@1985: #endif icculus@1982: slouken@1895: /* Start off with no conversion necessary */ icculus@1982: icculus@1982: cvt->src_format = src_fmt; icculus@1982: cvt->dst_format = dst_fmt; slouken@1895: cvt->needed = 0; slouken@1895: cvt->filter_index = 0; slouken@1895: cvt->filters[0] = NULL; slouken@1895: cvt->len_mult = 1; slouken@1895: cvt->len_ratio = 1.0; slouken@0: icculus@1982: /* Convert data types, if necessary. Updates (cvt). */ icculus@1982: if (SDL_BuildAudioTypeCVT(cvt, src_fmt, dst_fmt) == -1) slouken@1985: return -1; /* shouldn't happen, but just in case... */ slouken@0: icculus@1982: /* Channel conversion */ slouken@1895: if (src_channels != dst_channels) { slouken@1895: if ((src_channels == 1) && (dst_channels > 1)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; slouken@1895: cvt->len_mult *= 2; slouken@1895: src_channels = 2; slouken@1895: cvt->len_ratio *= 2; slouken@1895: } slouken@1895: if ((src_channels == 2) && (dst_channels == 6)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertSurround; slouken@1895: src_channels = 6; slouken@1895: cvt->len_mult *= 3; slouken@1895: cvt->len_ratio *= 3; slouken@1895: } slouken@1895: if ((src_channels == 2) && (dst_channels == 4)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4; slouken@1895: src_channels = 4; slouken@1895: cvt->len_mult *= 2; slouken@1895: cvt->len_ratio *= 2; slouken@1895: } slouken@1895: while ((src_channels * 2) <= dst_channels) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; slouken@1895: cvt->len_mult *= 2; slouken@1895: src_channels *= 2; slouken@1895: cvt->len_ratio *= 2; slouken@1895: } slouken@1895: if ((src_channels == 6) && (dst_channels <= 2)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertStrip; slouken@1895: src_channels = 2; slouken@1895: cvt->len_ratio /= 3; slouken@1895: } slouken@1895: if ((src_channels == 6) && (dst_channels == 4)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2; slouken@1895: src_channels = 4; slouken@1895: cvt->len_ratio /= 2; slouken@1895: } slouken@1895: /* This assumes that 4 channel audio is in the format: slouken@1895: Left {front/back} + Right {front/back} slouken@1895: so converting to L/R stereo works properly. slouken@1895: */ slouken@1895: while (((src_channels % 2) == 0) && slouken@1895: ((src_channels / 2) >= dst_channels)) { slouken@1895: cvt->filters[cvt->filter_index++] = SDL_ConvertMono; slouken@1895: src_channels /= 2; slouken@1895: cvt->len_ratio /= 2; slouken@1895: } slouken@1895: if (src_channels != dst_channels) { slouken@1895: /* Uh oh.. */ ; slouken@1895: } slouken@1895: } slouken@0: slouken@1895: /* Do rate conversion */ slouken@2716: if (src_rate != dst_rate) { slouken@2716: int rate_gcd; slouken@2716: rate_gcd = SDL_GCD(src_rate, dst_rate); slouken@2716: cvt->len_mult = dst_rate / rate_gcd; slouken@2716: cvt->len_div = src_rate / rate_gcd; slouken@2716: cvt->len_ratio = (double) cvt->len_mult / (double) cvt->len_div; slouken@2716: cvt->filters[cvt->filter_index++] = SDL_Resample; slouken@2716: SDL_BuildWindowedSinc(cvt, dst_fmt, 768); slouken@2716: } slouken@2716: slouken@2716: /* slouken@1895: cvt->rate_incr = 0.0; slouken@1895: if ((src_rate / 100) != (dst_rate / 100)) { slouken@1895: Uint32 hi_rate, lo_rate; slouken@1895: int len_mult; slouken@1895: double len_ratio; icculus@1982: SDL_AudioFilter rate_cvt = NULL; slouken@1895: slouken@1895: if (src_rate > dst_rate) { slouken@1895: hi_rate = src_rate; slouken@1895: lo_rate = dst_rate; slouken@1895: switch (src_channels) { slouken@1895: case 1: slouken@1895: rate_cvt = SDL_RateDIV2; slouken@1895: break; slouken@1895: case 2: slouken@1895: rate_cvt = SDL_RateDIV2_c2; slouken@1895: break; slouken@1895: case 4: slouken@1895: rate_cvt = SDL_RateDIV2_c4; slouken@1895: break; slouken@1895: case 6: slouken@1895: rate_cvt = SDL_RateDIV2_c6; slouken@1895: break; slouken@1895: default: slouken@1895: return -1; slouken@1895: } slouken@1895: len_mult = 1; slouken@1895: len_ratio = 0.5; slouken@1895: } else { slouken@1895: hi_rate = dst_rate; slouken@1895: lo_rate = src_rate; slouken@1895: switch (src_channels) { slouken@1895: case 1: slouken@1895: rate_cvt = SDL_RateMUL2; slouken@1895: break; slouken@1895: case 2: slouken@1895: rate_cvt = SDL_RateMUL2_c2; slouken@1895: break; slouken@1895: case 4: slouken@1895: rate_cvt = SDL_RateMUL2_c4; slouken@1895: break; slouken@1895: case 6: slouken@1895: rate_cvt = SDL_RateMUL2_c6; slouken@1895: break; slouken@1895: default: slouken@1895: return -1; slouken@1895: } slouken@1895: len_mult = 2; slouken@1895: len_ratio = 2.0; slouken@2716: }*/ slouken@2716: /* If hi_rate = lo_rate*2^x then conversion is easy */ slouken@2716: /* while (((lo_rate * 2) / 100) <= (hi_rate / 100)) { slouken@2716: cvt->filters[cvt->filter_index++] = rate_cvt; slouken@2716: cvt->len_mult *= len_mult; slouken@2716: lo_rate *= 2; slouken@2716: cvt->len_ratio *= len_ratio; slouken@2716: } */ slouken@2716: /* We may need a slow conversion here to finish up */ slouken@2716: /* if ((lo_rate / 100) != (hi_rate / 100)) { slouken@2716: #if 1 */ slouken@2716: /* The problem with this is that if the input buffer is slouken@2716: say 1K, and the conversion rate is say 1.1, then the slouken@2716: output buffer is 1.1K, which may not be an acceptable slouken@2716: buffer size for the audio driver (not a power of 2) slouken@2716: */ slouken@2716: /* For now, punt and hope the rate distortion isn't great. slouken@2716: */ slouken@2716: /*#else slouken@1895: if (src_rate < dst_rate) { slouken@1895: cvt->rate_incr = (double) lo_rate / hi_rate; slouken@1895: cvt->len_mult *= 2; slouken@1895: cvt->len_ratio /= cvt->rate_incr; slouken@1895: } else { slouken@1895: cvt->rate_incr = (double) hi_rate / lo_rate; slouken@1895: cvt->len_ratio *= cvt->rate_incr; slouken@1895: } slouken@1895: cvt->filters[cvt->filter_index++] = SDL_RateSLOW; slouken@0: #endif slouken@1895: } slouken@2716: }*/ slouken@0: slouken@1895: /* Set up the filter information */ slouken@1895: if (cvt->filter_index != 0) { slouken@1895: cvt->needed = 1; icculus@1982: cvt->src_format = src_fmt; icculus@1982: cvt->dst_format = dst_fmt; slouken@1895: cvt->len = 0; slouken@1895: cvt->buf = NULL; slouken@1895: cvt->filters[cvt->filter_index] = NULL; slouken@1895: } slouken@1895: return (cvt->needed); slouken@0: } slouken@1895: slouken@2716: #undef SDL_FixMpy8 slouken@2716: #undef SDL_FixMpy16 slouken@2716: #undef SDL_FixMpy32 slouken@2716: #undef SDL_FloatMpy slouken@2716: #undef SDL_Make_1_7 slouken@2716: #undef SDL_Make_1_15 slouken@2716: #undef SDL_Make_1_31 slouken@2716: #undef SDL_Make_2_6 slouken@2716: #undef SDL_Make_2_14 slouken@2716: #undef SDL_Make_2_30 slouken@2716: slouken@1895: /* vi: set ts=4 sw=4 expandtab: */