| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "../SDL_internal.h" |
| |
| /* Functions for audio drivers to perform runtime conversion of audio format */ |
| |
| #include "SDL_audio.h" |
| #include "SDL_audio_c.h" |
| |
| #include "SDL_assert.h" |
| |
| /* #define DEBUG_CONVERT */ |
| |
| /* 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 | |
| SDL_AUDIO_MASK_DATATYPE)) { |
| 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 / sizeof(type); i; --i) { \ |
| src -= 1; \ |
| dst -= 2; \ |
| dst[0] = dst[1] = *src; \ |
| } \ |
| } |
| |
| /* 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 | |
| SDL_AUDIO_MASK_DATATYPE)) { |
| 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 | |
| SDL_AUDIO_MASK_DATATYPE)) { |
| 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; |
| |
| case AUDIO_F32: |
| { |
| const float *src = (const float *) (cvt->buf + cvt->len_cvt); |
| float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); |
| float lf, rf, ce; |
| |
| if (SDL_AUDIO_ISBIGENDIAN(format)) { |
| for (i = cvt->len_cvt / 8; i; --i) { |
| dst -= 4; |
| src -= 2; |
| lf = SDL_SwapFloatBE(src[0]); |
| rf = SDL_SwapFloatBE(src[1]); |
| ce = (lf / 2) + (rf / 2); |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = SDL_SwapFloatBE(lf - ce); |
| dst[3] = SDL_SwapFloatBE(rf - ce); |
| } |
| } else { |
| for (i = cvt->len_cvt / 8; i; --i) { |
| dst -= 4; |
| src -= 2; |
| lf = SDL_SwapFloatLE(src[0]); |
| rf = SDL_SwapFloatLE(src[1]); |
| ce = (lf / 2) + (rf / 2); |
| dst[0] = src[0]; |
| dst[1] = src[1]; |
| dst[2] = SDL_SwapFloatLE(lf - ce); |
| dst[3] = SDL_SwapFloatLE(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; |
| |
| SDL_assert(src_rate != 0); |
| SDL_assert(dst_rate != 0); |
| SDL_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. |
| */ |
| |
| /* Sanity check target pointer */ |
| if (cvt == NULL) { |
| return SDL_InvalidParamError("cvt"); |
| } |
| |
| /* 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))) { |
| return SDL_SetError("Invalid source format"); |
| } |
| if ((SDL_AUDIO_BITSIZE(dst_fmt) > 16) && (!SDL_AUDIO_ISSIGNED(dst_fmt))) { |
| return SDL_SetError("Invalid destination format"); |
| } |
| |
| /* prevent possible divisions by zero, etc. */ |
| if ((src_channels == 0) || (dst_channels == 0)) { |
| return SDL_SetError("Source or destination channels is zero"); |
| } |
| if ((src_rate == 0) || (dst_rate == 0)) { |
| return SDL_SetError("Source or destination rate is zero"); |
| } |
| #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: */ |