/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" /* Functions for audio drivers to perform runtime conversion of audio format */ #include "SDL_audio.h" #include "SDL_audio_c.h" /* Effectively mix right and left channels into a single channel */ static void SDLCALL SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; Sint32 sample; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting to mono\n"); #endif switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { case AUDIO_U8: { Uint8 *src, *dst; src = cvt->buf; dst = cvt->buf; for (i = cvt->len_cvt / 2; i; --i) { sample = src[0] + src[1]; if (sample > 255) { *dst = 255; } else { *dst = (Uint8) sample; } src += 2; dst += 1; } } break; case AUDIO_S8: { Sint8 *src, *dst; src = (Sint8 *) cvt->buf; dst = (Sint8 *) cvt->buf; for (i = cvt->len_cvt / 2; i; --i) { sample = src[0] + src[1]; if (sample > 127) { *dst = 127; } else if (sample < -128) { *dst = -128; } else { *dst = (Sint8) sample; } src += 2; dst += 1; } } break; case AUDIO_U16: { Uint8 *src, *dst; src = cvt->buf; dst = cvt->buf; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { sample = (Uint16) ((src[0] << 8) | src[1]) + (Uint16) ((src[2] << 8) | src[3]); if (sample > 65535) { dst[0] = 0xFF; dst[1] = 0xFF; } else { dst[1] = (sample & 0xFF); sample >>= 8; dst[0] = (sample & 0xFF); } src += 4; dst += 2; } } else { for (i = cvt->len_cvt / 4; i; --i) { sample = (Uint16) ((src[1] << 8) | src[0]) + (Uint16) ((src[3] << 8) | src[2]); if (sample > 65535) { dst[0] = 0xFF; dst[1] = 0xFF; } else { dst[0] = (sample & 0xFF); sample >>= 8; dst[1] = (sample & 0xFF); } src += 4; dst += 2; } } } break; case AUDIO_S16: { Uint8 *src, *dst; src = cvt->buf; dst = cvt->buf; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { sample = (Sint16) ((src[0] << 8) | src[1]) + (Sint16) ((src[2] << 8) | src[3]); if (sample > 32767) { dst[0] = 0x7F; dst[1] = 0xFF; } else if (sample < -32768) { dst[0] = 0x80; dst[1] = 0x00; } else { dst[1] = (sample & 0xFF); sample >>= 8; dst[0] = (sample & 0xFF); } src += 4; dst += 2; } } else { for (i = cvt->len_cvt / 4; i; --i) { sample = (Sint16) ((src[1] << 8) | src[0]) + (Sint16) ((src[3] << 8) | src[2]); if (sample > 32767) { dst[1] = 0x7F; dst[0] = 0xFF; } else if (sample < -32768) { dst[1] = 0x80; dst[0] = 0x00; } else { dst[0] = (sample & 0xFF); sample >>= 8; dst[1] = (sample & 0xFF); } src += 4; dst += 2; } } } break; case AUDIO_S32: { const Uint32 *src = (const Uint32 *) cvt->buf; Uint32 *dst = (Uint32 *) cvt->buf; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 8; i; --i, src += 2) { const Sint64 added = (((Sint64) (Sint32) SDL_SwapBE32(src[0])) + ((Sint64) (Sint32) SDL_SwapBE32(src[1]))); *(dst++) = SDL_SwapBE32((Uint32) ((Sint32) (added >> 1))); } } else { for (i = cvt->len_cvt / 8; i; --i, src += 2) { const Sint64 added = (((Sint64) (Sint32) SDL_SwapLE32(src[0])) + ((Sint64) (Sint32) SDL_SwapLE32(src[1]))); *(dst++) = SDL_SwapLE32((Uint32) ((Sint32) (added >> 1))); } } } break; case AUDIO_F32: { const float *src = (const float *) cvt->buf; float *dst = (float *) cvt->buf; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 8; i; --i, src += 2) { float src1, src2; src1 = SDL_SwapFloatBE(src[0]); src2 = SDL_SwapFloatBE(src[1]); const double added = ((double) src1) + ((double) src2); src1 = (float) (added * 0.5); *(dst++) = SDL_SwapFloatBE(src1); } } else { for (i = cvt->len_cvt / 8; i; --i, src += 2) { float src1, src2; src1 = SDL_SwapFloatLE(src[0]); src2 = SDL_SwapFloatLE(src[1]); const double added = ((double) src1) + ((double) src2); src1 = (float) (added * 0.5); *(dst++) = SDL_SwapFloatLE(src1); } } } break; } cvt->len_cvt /= 2; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* Discard top 4 channels */ static void SDLCALL SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting down from 6 channels to stereo\n"); #endif #define strip_chans_6_to_2(type) \ { \ const type *src = (const type *) cvt->buf; \ type *dst = (type *) cvt->buf; \ for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \ dst[0] = src[0]; \ dst[1] = src[1]; \ src += 6; \ dst += 2; \ } \ } /* this function only cares about typesize, and data as a block of bits. */ switch (SDL_AUDIO_BITSIZE(format)) { case 8: strip_chans_6_to_2(Uint8); break; case 16: strip_chans_6_to_2(Uint16); break; case 32: strip_chans_6_to_2(Uint32); break; } #undef strip_chans_6_to_2 cvt->len_cvt /= 3; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* Discard top 2 channels of 6 */ static void SDLCALL SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting 6 down to quad\n"); #endif #define strip_chans_6_to_4(type) \ { \ const type *src = (const type *) cvt->buf; \ type *dst = (type *) cvt->buf; \ for (i = cvt->len_cvt / (sizeof (type) * 6); i; --i) { \ dst[0] = src[0]; \ dst[1] = src[1]; \ dst[2] = src[2]; \ dst[3] = src[3]; \ src += 6; \ dst += 4; \ } \ } /* this function only cares about typesize, and data as a block of bits. */ switch (SDL_AUDIO_BITSIZE(format)) { case 8: strip_chans_6_to_4(Uint8); break; case 16: strip_chans_6_to_4(Uint16); break; case 32: strip_chans_6_to_4(Uint32); break; } #undef strip_chans_6_to_4 cvt->len_cvt /= 6; cvt->len_cvt *= 4; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* Duplicate a mono channel to both stereo channels */ static void SDLCALL SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting to stereo\n"); #endif #define dup_chans_1_to_2(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 / 2; i; --i, --src) { \ const type val = *src; \ dst -= 2; \ dst[0] = dst[1] = val; \ } \ } /* this function only cares about typesize, and data as a block of bits. */ switch (SDL_AUDIO_BITSIZE(format)) { case 8: dup_chans_1_to_2(Uint8); break; case 16: dup_chans_1_to_2(Uint16); break; case 32: dup_chans_1_to_2(Uint32); break; } #undef dup_chans_1_to_2 cvt->len_cvt *= 2; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* Duplicate a stereo channel to a pseudo-5.1 stream */ static void SDLCALL SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting stereo to surround\n"); #endif switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { case AUDIO_U8: { Uint8 *src, *dst, lf, rf, ce; src = (Uint8 *) (cvt->buf + cvt->len_cvt); dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 3); for (i = cvt->len_cvt; i; --i) { dst -= 6; src -= 2; lf = src[0]; rf = src[1]; ce = (lf / 2) + (rf / 2); dst[0] = lf; dst[1] = rf; dst[2] = lf - ce; dst[3] = rf - ce; dst[4] = ce; dst[5] = ce; } } break; case AUDIO_S8: { Sint8 *src, *dst, lf, rf, ce; src = (Sint8 *) cvt->buf + cvt->len_cvt; dst = (Sint8 *) cvt->buf + cvt->len_cvt * 3; for (i = cvt->len_cvt; i; --i) { dst -= 6; src -= 2; lf = src[0]; rf = src[1]; ce = (lf / 2) + (rf / 2); dst[0] = lf; dst[1] = rf; dst[2] = lf - ce; dst[3] = rf - ce; dst[4] = ce; dst[5] = ce; } } break; case AUDIO_U16: { Uint8 *src, *dst; Uint16 lf, rf, ce, lr, rr; src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 3; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { dst -= 12; src -= 4; lf = (Uint16) ((src[0] << 8) | src[1]); rf = (Uint16) ((src[2] << 8) | src[3]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[1] = (lf & 0xFF); dst[0] = ((lf >> 8) & 0xFF); dst[3] = (rf & 0xFF); dst[2] = ((rf >> 8) & 0xFF); dst[1 + 4] = (lr & 0xFF); dst[0 + 4] = ((lr >> 8) & 0xFF); dst[3 + 4] = (rr & 0xFF); dst[2 + 4] = ((rr >> 8) & 0xFF); dst[1 + 8] = (ce & 0xFF); dst[0 + 8] = ((ce >> 8) & 0xFF); dst[3 + 8] = (ce & 0xFF); dst[2 + 8] = ((ce >> 8) & 0xFF); } } else { for (i = cvt->len_cvt / 4; i; --i) { dst -= 12; src -= 4; lf = (Uint16) ((src[1] << 8) | src[0]); rf = (Uint16) ((src[3] << 8) | src[2]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[0] = (lf & 0xFF); dst[1] = ((lf >> 8) & 0xFF); dst[2] = (rf & 0xFF); dst[3] = ((rf >> 8) & 0xFF); dst[0 + 4] = (lr & 0xFF); dst[1 + 4] = ((lr >> 8) & 0xFF); dst[2 + 4] = (rr & 0xFF); dst[3 + 4] = ((rr >> 8) & 0xFF); dst[0 + 8] = (ce & 0xFF); dst[1 + 8] = ((ce >> 8) & 0xFF); dst[2 + 8] = (ce & 0xFF); dst[3 + 8] = ((ce >> 8) & 0xFF); } } } break; case AUDIO_S16: { Uint8 *src, *dst; Sint16 lf, rf, ce, lr, rr; src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 3; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { dst -= 12; src -= 4; lf = (Sint16) ((src[0] << 8) | src[1]); rf = (Sint16) ((src[2] << 8) | src[3]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[1] = (lf & 0xFF); dst[0] = ((lf >> 8) & 0xFF); dst[3] = (rf & 0xFF); dst[2] = ((rf >> 8) & 0xFF); dst[1 + 4] = (lr & 0xFF); dst[0 + 4] = ((lr >> 8) & 0xFF); dst[3 + 4] = (rr & 0xFF); dst[2 + 4] = ((rr >> 8) & 0xFF); dst[1 + 8] = (ce & 0xFF); dst[0 + 8] = ((ce >> 8) & 0xFF); dst[3 + 8] = (ce & 0xFF); dst[2 + 8] = ((ce >> 8) & 0xFF); } } else { for (i = cvt->len_cvt / 4; i; --i) { dst -= 12; src -= 4; lf = (Sint16) ((src[1] << 8) | src[0]); rf = (Sint16) ((src[3] << 8) | src[2]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[0] = (lf & 0xFF); dst[1] = ((lf >> 8) & 0xFF); dst[2] = (rf & 0xFF); dst[3] = ((rf >> 8) & 0xFF); dst[0 + 4] = (lr & 0xFF); dst[1 + 4] = ((lr >> 8) & 0xFF); dst[2 + 4] = (rr & 0xFF); dst[3 + 4] = ((rr >> 8) & 0xFF); dst[0 + 8] = (ce & 0xFF); dst[1 + 8] = ((ce >> 8) & 0xFF); dst[2 + 8] = (ce & 0xFF); dst[3 + 8] = ((ce >> 8) & 0xFF); } } } break; case AUDIO_S32: { Sint32 lf, rf, ce; const Uint32 *src = (const Uint32 *) cvt->buf + cvt->len_cvt; Uint32 *dst = (Uint32 *) cvt->buf + cvt->len_cvt * 3; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 8; i; --i) { dst -= 6; src -= 2; lf = (Sint32) SDL_SwapBE32(src[0]); rf = (Sint32) SDL_SwapBE32(src[1]); ce = (lf / 2) + (rf / 2); dst[0] = SDL_SwapBE32((Uint32) lf); dst[1] = SDL_SwapBE32((Uint32) rf); dst[2] = SDL_SwapBE32((Uint32) (lf - ce)); dst[3] = SDL_SwapBE32((Uint32) (rf - ce)); dst[4] = SDL_SwapBE32((Uint32) ce); dst[5] = SDL_SwapBE32((Uint32) ce); } } else { for (i = cvt->len_cvt / 8; i; --i) { dst -= 6; src -= 2; lf = (Sint32) SDL_SwapLE32(src[0]); rf = (Sint32) SDL_SwapLE32(src[1]); ce = (lf / 2) + (rf / 2); dst[0] = src[0]; dst[1] = src[1]; dst[2] = SDL_SwapLE32((Uint32) (lf - ce)); dst[3] = SDL_SwapLE32((Uint32) (rf - ce)); dst[4] = SDL_SwapLE32((Uint32) ce); dst[5] = SDL_SwapLE32((Uint32) ce); } } } break; case AUDIO_F32: { float lf, rf, ce; const float *src = (const float *) cvt->buf + cvt->len_cvt; float *dst = (float *) cvt->buf + cvt->len_cvt * 3; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 8; i; --i) { dst -= 6; src -= 2; lf = SDL_SwapFloatBE(src[0]); rf = SDL_SwapFloatBE(src[1]); ce = (lf * 0.5f) + (rf * 0.5f); dst[0] = src[0]; dst[1] = src[1]; dst[2] = SDL_SwapFloatBE(lf - ce); dst[3] = SDL_SwapFloatBE(rf - ce); dst[4] = dst[5] = SDL_SwapFloatBE(ce); } } else { for (i = cvt->len_cvt / 8; i; --i) { dst -= 6; src -= 2; lf = SDL_SwapFloatLE(src[0]); rf = SDL_SwapFloatLE(src[1]); ce = (lf * 0.5f) + (rf * 0.5f); dst[0] = src[0]; dst[1] = src[1]; dst[2] = SDL_SwapFloatLE(lf - ce); dst[3] = SDL_SwapFloatLE(rf - ce); dst[4] = dst[5] = SDL_SwapFloatLE(ce); } } } break; } cvt->len_cvt *= 3; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* Duplicate a stereo channel to a pseudo-4.0 stream */ static void SDLCALL SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; #ifdef DEBUG_CONVERT fprintf(stderr, "Converting stereo to quad\n"); #endif switch (format & (SDL_AUDIO_MASK_SIGNED | SDL_AUDIO_MASK_BITSIZE)) { case AUDIO_U8: { Uint8 *src, *dst, lf, rf, ce; src = (Uint8 *) (cvt->buf + cvt->len_cvt); dst = (Uint8 *) (cvt->buf + cvt->len_cvt * 2); for (i = cvt->len_cvt; i; --i) { dst -= 4; src -= 2; lf = src[0]; rf = src[1]; ce = (lf / 2) + (rf / 2); dst[0] = lf; dst[1] = rf; dst[2] = lf - ce; dst[3] = rf - ce; } } break; case AUDIO_S8: { Sint8 *src, *dst, lf, rf, ce; src = (Sint8 *) cvt->buf + cvt->len_cvt; dst = (Sint8 *) cvt->buf + cvt->len_cvt * 2; for (i = cvt->len_cvt; i; --i) { dst -= 4; src -= 2; lf = src[0]; rf = src[1]; ce = (lf / 2) + (rf / 2); dst[0] = lf; dst[1] = rf; dst[2] = lf - ce; dst[3] = rf - ce; } } break; case AUDIO_U16: { Uint8 *src, *dst; Uint16 lf, rf, ce, lr, rr; src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 2; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { dst -= 8; src -= 4; lf = (Uint16) ((src[0] << 8) | src[1]); rf = (Uint16) ((src[2] << 8) | src[3]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[1] = (lf & 0xFF); dst[0] = ((lf >> 8) & 0xFF); dst[3] = (rf & 0xFF); dst[2] = ((rf >> 8) & 0xFF); dst[1 + 4] = (lr & 0xFF); dst[0 + 4] = ((lr >> 8) & 0xFF); dst[3 + 4] = (rr & 0xFF); dst[2 + 4] = ((rr >> 8) & 0xFF); } } else { for (i = cvt->len_cvt / 4; i; --i) { dst -= 8; src -= 4; lf = (Uint16) ((src[1] << 8) | src[0]); rf = (Uint16) ((src[3] << 8) | src[2]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[0] = (lf & 0xFF); dst[1] = ((lf >> 8) & 0xFF); dst[2] = (rf & 0xFF); dst[3] = ((rf >> 8) & 0xFF); dst[0 + 4] = (lr & 0xFF); dst[1 + 4] = ((lr >> 8) & 0xFF); dst[2 + 4] = (rr & 0xFF); dst[3 + 4] = ((rr >> 8) & 0xFF); } } } break; case AUDIO_S16: { Uint8 *src, *dst; Sint16 lf, rf, ce, lr, rr; src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 2; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 4; i; --i) { dst -= 8; src -= 4; lf = (Sint16) ((src[0] << 8) | src[1]); rf = (Sint16) ((src[2] << 8) | src[3]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[1] = (lf & 0xFF); dst[0] = ((lf >> 8) & 0xFF); dst[3] = (rf & 0xFF); dst[2] = ((rf >> 8) & 0xFF); dst[1 + 4] = (lr & 0xFF); dst[0 + 4] = ((lr >> 8) & 0xFF); dst[3 + 4] = (rr & 0xFF); dst[2 + 4] = ((rr >> 8) & 0xFF); } } else { for (i = cvt->len_cvt / 4; i; --i) { dst -= 8; src -= 4; lf = (Sint16) ((src[1] << 8) | src[0]); rf = (Sint16) ((src[3] << 8) | src[2]); ce = (lf / 2) + (rf / 2); rr = lf - ce; lr = rf - ce; dst[0] = (lf & 0xFF); dst[1] = ((lf >> 8) & 0xFF); dst[2] = (rf & 0xFF); dst[3] = ((rf >> 8) & 0xFF); dst[0 + 4] = (lr & 0xFF); dst[1 + 4] = ((lr >> 8) & 0xFF); dst[2 + 4] = (rr & 0xFF); dst[3 + 4] = ((rr >> 8) & 0xFF); } } } break; case AUDIO_S32: { const Uint32 *src = (const Uint32 *) (cvt->buf + cvt->len_cvt); Uint32 *dst = (Uint32 *) (cvt->buf + cvt->len_cvt * 2); Sint32 lf, rf, ce; if (SDL_AUDIO_ISBIGENDIAN(format)) { for (i = cvt->len_cvt / 8; i; --i) { dst -= 4; src -= 2; lf = (Sint32) SDL_SwapBE32(src[0]); rf = (Sint32) SDL_SwapBE32(src[1]); ce = (lf / 2) + (rf / 2); dst[0] = src[0]; dst[1] = src[1]; dst[2] = SDL_SwapBE32((Uint32) (lf - ce)); dst[3] = SDL_SwapBE32((Uint32) (rf - ce)); } } else { for (i = cvt->len_cvt / 8; i; --i) { dst -= 4; src -= 2; lf = (Sint32) SDL_SwapLE32(src[0]); rf = (Sint32) SDL_SwapLE32(src[1]); ce = (lf / 2) + (rf / 2); dst[0] = src[0]; dst[1] = src[1]; dst[2] = SDL_SwapLE32((Uint32) (lf - ce)); dst[3] = SDL_SwapLE32((Uint32) (rf - ce)); } } } break; } cvt->len_cvt *= 2; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } /* 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! */ fprintf(stderr, "FIXME: need 32-bit converter here!\n"); } } } 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! */ fprintf(stderr, "FIXME: need 32-bit converter here!\n"); } } } cvt->len_cvt = clen; if (cvt->filters[++cvt->filter_index]) { cvt->filters[cvt->filter_index] (cvt, format); } } int SDL_ConvertAudio(SDL_AudioCVT * cvt) { /* Make sure there's data to convert */ if (cvt->buf == NULL) { SDL_SetError("No buffer allocated for conversion"); return (-1); } /* Return okay if no conversion is necessary */ cvt->len_cvt = cvt->len; if (cvt->filters[0] == NULL) { return (0); } /* Set up the conversion and go! */ cvt->filter_index = 0; cvt->filters[0] (cvt, cvt->src_format); return (0); } static SDL_AudioFilter SDL_HandTunedTypeCVT(SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt) { /* * Fill in any future conversions that are specialized to a * processor, platform, compiler, or library here. */ return NULL; /* no specialized converter code available. */ } /* * Find a converter between two data types. We try to select a hand-tuned * asm/vectorized/optimized function first, and then fallback to an * autogenerated function that is customized to convert between two * specific data types. */ static int SDL_BuildAudioTypeCVT(SDL_AudioCVT * cvt, SDL_AudioFormat src_fmt, SDL_AudioFormat dst_fmt) { if (src_fmt != dst_fmt) { const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt); const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt); SDL_AudioFilter filter = SDL_HandTunedTypeCVT(src_fmt, dst_fmt); /* No hand-tuned converter? Try the autogenerated ones. */ if (filter == NULL) { int i; for (i = 0; sdl_audio_type_filters[i].filter != NULL; i++) { const SDL_AudioTypeFilters *filt = &sdl_audio_type_filters[i]; if ((filt->src_fmt == src_fmt) && (filt->dst_fmt == dst_fmt)) { 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_bitsize < dst_bitsize) { const int mult = (dst_bitsize / src_bitsize); cvt->len_mult *= mult; cvt->len_ratio *= mult; } else if (src_bitsize > dst_bitsize) { cvt->len_ratio /= (src_bitsize / dst_bitsize); } return 1; /* added a converter. */ } return 0; /* no conversion necessary. */ } /* Creates a set of audio filters to convert from one format to another. Returns -1 if the format conversion is not supported, 0 if there's no conversion needed, or 1 if the audio filter is set up. */ int SDL_BuildAudioCVT(SDL_AudioCVT * cvt, SDL_AudioFormat src_fmt, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_fmt, Uint8 dst_channels, int dst_rate) { /* 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; } if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) { 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); #endif /* Start off with no conversion necessary */ cvt->src_format = src_fmt; cvt->dst_format = dst_fmt; cvt->needed = 0; cvt->filter_index = 0; cvt->filters[0] = NULL; cvt->len_mult = 1; cvt->len_ratio = 1.0; /* Convert data types, if necessary. Updates (cvt). */ 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) { if ((src_channels == 1) && (dst_channels > 1)) { cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; cvt->len_mult *= 2; src_channels = 2; cvt->len_ratio *= 2; } if ((src_channels == 2) && (dst_channels == 6)) { cvt->filters[cvt->filter_index++] = SDL_ConvertSurround; src_channels = 6; cvt->len_mult *= 3; cvt->len_ratio *= 3; } if ((src_channels == 2) && (dst_channels == 4)) { cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4; src_channels = 4; cvt->len_mult *= 2; cvt->len_ratio *= 2; } while ((src_channels * 2) <= dst_channels) { cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; cvt->len_mult *= 2; src_channels *= 2; cvt->len_ratio *= 2; } if ((src_channels == 6) && (dst_channels <= 2)) { cvt->filters[cvt->filter_index++] = SDL_ConvertStrip; src_channels = 2; cvt->len_ratio /= 3; } if ((src_channels == 6) && (dst_channels == 4)) { cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2; src_channels = 4; cvt->len_ratio /= 2; } /* This assumes that 4 channel audio is in the format: Left {front/back} + Right {front/back} so converting to L/R stereo works properly. */ while (((src_channels % 2) == 0) && ((src_channels / 2) >= dst_channels)) { cvt->filters[cvt->filter_index++] = SDL_ConvertMono; src_channels /= 2; cvt->len_ratio /= 2; } if (src_channels != dst_channels) { /* Uh oh.. */ ; } } /* Do rate conversion */ 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; cvt->src_format = src_fmt; cvt->dst_format = dst_fmt; cvt->len = 0; cvt->buf = NULL; cvt->filters[cvt->filter_index] = NULL; } return (cvt->needed); } /* vi: set ts=4 sw=4 expandtab: */