1122 lines
36 KiB
C
1122 lines
36 KiB
C
/*
|
|
Simple DirectMedia Layer
|
|
Copyright (C) 1997-2014 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: */
|