diff options
Diffstat (limited to 'macosx/plugins/Common/SDL/src/audio/SDL_audiocvt.c')
| -rw-r--r-- | macosx/plugins/Common/SDL/src/audio/SDL_audiocvt.c | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/macosx/plugins/Common/SDL/src/audio/SDL_audiocvt.c b/macosx/plugins/Common/SDL/src/audio/SDL_audiocvt.c new file mode 100644 index 00000000..3af35f13 --- /dev/null +++ b/macosx/plugins/Common/SDL/src/audio/SDL_audiocvt.c @@ -0,0 +1,1080 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2010 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) { + SDL_SetError("No conversion available for these formats"); + return -1; + } + } + + /* 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) { + SDL_SetError("No conversion available for these rates"); + return -1; + } + } + + /* 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) SDL_ceil(mult); + 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 up front. */ + if ((SDL_AUDIO_BITSIZE(src_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(src_fmt))) { + SDL_SetError("Invalid source format"); + return -1; + } + if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) { + SDL_SetError("Invalid destination format"); + return -1; + } + + /* prevent possible divisions by zero, etc. */ + if ((src_rate == 0) || (dst_rate == 0)) { + SDL_SetError("Source or destination rate is zero"); + 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: */ |
