/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2009 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" /* #define DEBUG_CONVERT */ /* !!! FIXME */ #ifndef assert #define assert(x) #endif /* 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]; *dst = (Uint8) (sample / 2); 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]; *dst = (Sint8) (sample / 2); 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]); sample /= 2; 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]); sample /= 2; 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]); sample /= 2; 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]); sample /= 2; 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 / 2))); } } 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 / 2))); } } } 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) { const float src1 = SDL_SwapFloatBE(src[0]); const float src2 = SDL_SwapFloatBE(src[1]); const double added = ((double) src1) + ((double) src2); const float halved = (float) (added * 0.5); *(dst++) = SDL_SwapFloatBE(halved); } } else { for (i = cvt->len_cvt / 8; i; --i, src += 2) { const float src1 = SDL_SwapFloatLE(src[0]); const float src2 = SDL_SwapFloatLE(src[1]); const double added = ((double) src1) + ((double) src2); const float halved = (float) (added * 0.5); *(dst++) = SDL_SwapFloatLE(halved); } } } 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); } } int SDL_ConvertAudio(SDL_AudioCVT * cvt) { /* !!! FIXME: (cvt) should be const; stack-copy it here. */ /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */ /* Make sure there's data to convert */ if (cvt->buf == NULL) { SDL_SetError("No buffer allocated for conversion"); 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. */ } static SDL_AudioFilter SDL_HandTunedResampleCVT(SDL_AudioCVT * cvt, int dst_channels, int src_rate, int dst_rate) { /* * Fill in any future conversions that are specialized to a * processor, platform, compiler, or library here. */ return NULL; /* no specialized converter code available. */ } static int SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate) { int retval = 0; /* If we only built with the arbitrary resamplers, ignore multiples. */ #if !LESS_RESAMPLERS int lo, hi; int div; assert(src_rate != 0); assert(dst_rate != 0); assert(src_rate != dst_rate); if (src_rate < dst_rate) { lo = src_rate; hi = dst_rate; } else { lo = dst_rate; hi = src_rate; } /* zero means "not a supported multiple" ... we only do 2x and 4x. */ if ((hi % lo) != 0) return 0; /* not a multiple. */ div = hi / lo; retval = ((div == 2) || (div == 4)) ? div : 0; #endif return retval; } static int SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels, int src_rate, int dst_rate) { if (src_rate != dst_rate) { SDL_AudioFilter filter = SDL_HandTunedResampleCVT(cvt, dst_channels, src_rate, dst_rate); /* No hand-tuned converter? Try the autogenerated ones. */ if (filter == NULL) { int i; const int upsample = (src_rate < dst_rate) ? 1 : 0; const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate); for (i = 0; sdl_audio_rate_filters[i].filter != NULL; i++) { const SDL_AudioRateFilters *filt = &sdl_audio_rate_filters[i]; if ((filt->fmt == cvt->dst_format) && (filt->channels == dst_channels) && (filt->upsample == upsample) && (filt->multiple == multiple)) { filter = filt->filter; break; } } if (filter == NULL) { return -1; /* Still no matching converter?! */ } } /* Update (cvt) with filter details... */ cvt->filters[cvt->filter_index++] = filter; if (src_rate < dst_rate) { const double mult = ((double) dst_rate) / ((double) src_rate); cvt->len_mult *= (int) ceil(mult); /* !!! FIXME: C runtime dependency. */ cvt->len_ratio *= mult; } else { cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); } return 1; /* added a converter. */ } 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) { /* * !!! FIXME: reorder filters based on which grow/shrink the buffer. * !!! FIXME: ideally, we should do everything that shrinks the buffer * !!! FIXME: first, so we don't have to process as many bytes in a given * !!! FIXME: filter and abuse the CPU cache less. This might not be as * !!! FIXME: good in practice as it sounds in theory, though. */ /* there are no unsigned types over 16 bits, so catch this upfront. */ if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) { return -1; } if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) { return -1; } /* prevent possible divisions by zero, etc. */ if ((src_rate == 0) || (dst_rate == 0)) { return -1; } #ifdef DEBUG_CONVERT printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n", src_fmt, dst_fmt, src_channels, dst_channels, src_rate, dst_rate); #endif /* Start off with no conversion necessary */ SDL_zerop(cvt); 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; cvt->rate_incr = ((double) dst_rate) / ((double) src_rate); /* Convert data types, if necessary. Updates (cvt). */ if (SDL_BuildAudioTypeCVT(cvt, src_fmt, dst_fmt) == -1) { 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, if necessary. Updates (cvt). */ if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) { return -1; /* shouldn't happen, but just in case... */ } /* 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: */