audio: Improvements in channel conversion code.

This commit is contained in:
Ryan C. Gordon 2017-01-08 16:18:49 -05:00
parent 35166609d5
commit 38854e0333
2 changed files with 48 additions and 40 deletions

View File

@ -31,7 +31,7 @@
/* Effectively mix right and left channels into a single channel */ /* Effectively mix right and left channels into a single channel */
static void SDLCALL static void SDLCALL
SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
float *dst = (float *) cvt->buf; float *dst = (float *) cvt->buf;
const float *src = dst; const float *src = dst;
@ -51,20 +51,22 @@ SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format)
} }
/* Discard top 4 channels */ /* Convert from 5.1 to stereo. Average left and right, discard subwoofer. */
static void SDLCALL static void SDLCALL
SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
float *dst = (float *) cvt->buf; float *dst = (float *) cvt->buf;
const float *src = dst; const float *src = dst;
int i; int i;
LOG_DEBUG_CONVERT("6 channels", "stereo"); LOG_DEBUG_CONVERT("5.1", "stereo");
SDL_assert(format == AUDIO_F32SYS); SDL_assert(format == AUDIO_F32SYS);
/* this assumes FL+FR+FC+subwoof+BL+BR layout. */
for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) { for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 2) {
dst[0] = src[0]; const double front_center = (double) src[2];
dst[1] = src[1]; dst[0] = (float) ((src[0] + front_center + src[4]) / 3.0); /* left */
dst[1] = (float) ((src[1] + front_center + src[5]) / 3.0); /* right */
} }
cvt->len_cvt /= 3; cvt->len_cvt /= 3;
@ -74,22 +76,25 @@ SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format)
} }
/* Discard top 2 channels of 6 */ /* Convert from 5.1 to quad */
static void SDLCALL static void SDLCALL
SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
float *dst = (float *) cvt->buf; float *dst = (float *) cvt->buf;
const float *src = dst; const float *src = dst;
int i; int i;
LOG_DEBUG_CONVERT("6 channels", "quad"); LOG_DEBUG_CONVERT("5.1", "quad");
SDL_assert(format == AUDIO_F32SYS); SDL_assert(format == AUDIO_F32SYS);
/* assumes quad is FL+FR+BL+BR layout and 5.1 is FL+FR+FC+subwoof+BL+BR */
for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) { for (i = cvt->len_cvt / (sizeof (float) * 6); i; --i, src += 6, dst += 4) {
dst[0] = src[0]; /* FIXME: this is a good candidate for SIMD. */
dst[1] = src[1]; const double front_center = (double) src[2];
dst[2] = src[2]; dst[0] = (float) ((src[0] + front_center) * 0.5); /* FL */
dst[3] = src[3]; dst[1] = (float) ((src[1] + front_center) * 0.5); /* FR */
dst[2] = (float) ((src[4] + front_center) * 0.5); /* BL */
dst[3] = (float) ((src[5] + front_center) * 0.5); /* BR */
} }
cvt->len_cvt /= 6; cvt->len_cvt /= 6;
@ -99,9 +104,10 @@ SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format)
} }
} }
/* Duplicate a mono channel to both stereo channels */ /* Duplicate a mono channel to both stereo channels */
static void SDLCALL static void SDLCALL
SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_ConvertMonoToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
const float *src = (const float *) (cvt->buf + cvt->len_cvt); const float *src = (const float *) (cvt->buf + cvt->len_cvt);
float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
@ -125,7 +131,7 @@ SDL_ConvertStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format)
/* Duplicate a stereo channel to a pseudo-5.1 stream */ /* Duplicate a stereo channel to a pseudo-5.1 stream */
static void SDLCALL static void SDLCALL
SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
int i; int i;
float lf, rf, ce; float lf, rf, ce;
@ -140,12 +146,13 @@ SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
src -= 2; src -= 2;
lf = src[0]; lf = src[0];
rf = src[1]; rf = src[1];
ce = (lf * 0.5f) + (rf * 0.5f); ce = (lf + rf) * 0.5f;
dst[0] = src[0]; dst[0] = lf + (lf - ce); /* FL */
dst[1] = src[1]; dst[1] = rf + (rf - ce); /* FR */
dst[2] = lf - ce; dst[2] = ce; /* FC */
dst[3] = rf - ce; dst[3] = ce; /* !!! FIXME: wrong! This is the subwoofer. */
dst[4] = dst[5] = ce; dst[4] = lf; /* BL */
dst[5] = rf; /* BR */
} }
cvt->len_cvt *= 3; cvt->len_cvt *= 3;
@ -157,11 +164,11 @@ SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format)
/* Duplicate a stereo channel to a pseudo-4.0 stream */ /* Duplicate a stereo channel to a pseudo-4.0 stream */
static void SDLCALL static void SDLCALL
SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format) SDL_ConvertStereoToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format)
{ {
const float *src = (const float *) (cvt->buf + cvt->len_cvt); const float *src = (const float *) (cvt->buf + cvt->len_cvt);
float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); float *dst = (float *) (cvt->buf + cvt->len_cvt * 2);
float lf, rf, ce; float lf, rf;
int i; int i;
LOG_DEBUG_CONVERT("stereo", "quad"); LOG_DEBUG_CONVERT("stereo", "quad");
@ -172,11 +179,10 @@ SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format)
src -= 2; src -= 2;
lf = src[0]; lf = src[0];
rf = src[1]; rf = src[1];
ce = (lf / 2) + (rf / 2); dst[0] = lf; /* FL */
dst[0] = src[0]; dst[1] = rf; /* FR */
dst[1] = src[1]; dst[2] = lf; /* BL */
dst[2] = lf - ce; dst[3] = rf; /* BR */
dst[3] = rf - ce;
} }
cvt->len_cvt *= 2; cvt->len_cvt *= 2;
@ -536,36 +542,36 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
/* Channel conversion */ /* Channel conversion */
if (src_channels != dst_channels) { if (src_channels != dst_channels) {
if ((src_channels == 1) && (dst_channels > 1)) { if ((src_channels == 1) && (dst_channels > 1)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
cvt->len_mult *= 2; cvt->len_mult *= 2;
src_channels = 2; src_channels = 2;
cvt->len_ratio *= 2; cvt->len_ratio *= 2;
} }
if ((src_channels == 2) && (dst_channels == 6)) { if ((src_channels == 2) && (dst_channels == 6)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertSurround; cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51;
src_channels = 6; src_channels = 6;
cvt->len_mult *= 3; cvt->len_mult *= 3;
cvt->len_ratio *= 3; cvt->len_ratio *= 3;
} }
if ((src_channels == 2) && (dst_channels == 4)) { if ((src_channels == 2) && (dst_channels == 4)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertSurround_4; cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad;
src_channels = 4; src_channels = 4;
cvt->len_mult *= 2; cvt->len_mult *= 2;
cvt->len_ratio *= 2; cvt->len_ratio *= 2;
} }
while ((src_channels * 2) <= dst_channels) { while ((src_channels * 2) <= dst_channels) {
cvt->filters[cvt->filter_index++] = SDL_ConvertStereo; cvt->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo;
cvt->len_mult *= 2; cvt->len_mult *= 2;
src_channels *= 2; src_channels *= 2;
cvt->len_ratio *= 2; cvt->len_ratio *= 2;
} }
if ((src_channels == 6) && (dst_channels <= 2)) { if ((src_channels == 6) && (dst_channels <= 2)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertStrip; cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo;
src_channels = 2; src_channels = 2;
cvt->len_ratio /= 3; cvt->len_ratio /= 3;
} }
if ((src_channels == 6) && (dst_channels == 4)) { if ((src_channels == 6) && (dst_channels == 4)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertStrip_2; cvt->filters[cvt->filter_index++] = SDL_Convert51ToQuad;
src_channels = 4; src_channels = 4;
cvt->len_ratio /= 2; cvt->len_ratio /= 2;
} }
@ -575,7 +581,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
*/ */
while (((src_channels % 2) == 0) && while (((src_channels % 2) == 0) &&
((src_channels / 2) >= dst_channels)) { ((src_channels / 2) >= dst_channels)) {
cvt->filters[cvt->filter_index++] = SDL_ConvertMono; cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono;
src_channels /= 2; src_channels /= 2;
cvt->len_ratio /= 2; cvt->len_ratio /= 2;
} }

View File

@ -20,6 +20,7 @@ main(int argc, char **argv)
Uint32 len = 0; Uint32 len = 0;
Uint8 *data = NULL; Uint8 *data = NULL;
int cvtfreq = 0; int cvtfreq = 0;
int cvtchans = 0;
int bitsize = 0; int bitsize = 0;
int blockalign = 0; int blockalign = 0;
int avgbytes = 0; int avgbytes = 0;
@ -28,12 +29,13 @@ main(int argc, char **argv)
/* Enable standard application logging */ /* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
if (argc != 4) { if (argc != 5) {
SDL_Log("USAGE: %s in.wav out.wav newfreq\n", argv[0]); SDL_Log("USAGE: %s in.wav out.wav newfreq newchans\n", argv[0]);
return 1; return 1;
} }
cvtfreq = SDL_atoi(argv[3]); cvtfreq = SDL_atoi(argv[3]);
cvtchans = SDL_atoi(argv[4]);
if (SDL_Init(SDL_INIT_AUDIO) == -1) { if (SDL_Init(SDL_INIT_AUDIO) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Init() failed: %s\n", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_Init() failed: %s\n", SDL_GetError());
@ -47,7 +49,7 @@ main(int argc, char **argv)
} }
if (SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, if (SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq,
spec.format, spec.channels, cvtfreq) == -1) { spec.format, cvtchans, cvtfreq) == -1) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to build CVT: %s\n", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to build CVT: %s\n", SDL_GetError());
SDL_FreeWAV(data); SDL_FreeWAV(data);
SDL_Quit(); SDL_Quit();
@ -83,7 +85,7 @@ main(int argc, char **argv)
} }
bitsize = SDL_AUDIO_BITSIZE(spec.format); bitsize = SDL_AUDIO_BITSIZE(spec.format);
blockalign = (bitsize / 8) * spec.channels; blockalign = (bitsize / 8) * cvtchans;
avgbytes = cvtfreq * blockalign; avgbytes = cvtfreq * blockalign;
SDL_WriteLE32(io, 0x46464952); /* RIFF */ SDL_WriteLE32(io, 0x46464952); /* RIFF */
@ -92,7 +94,7 @@ main(int argc, char **argv)
SDL_WriteLE32(io, 0x20746D66); /* fmt */ SDL_WriteLE32(io, 0x20746D66); /* fmt */
SDL_WriteLE32(io, 16); /* chunk size */ SDL_WriteLE32(io, 16); /* chunk size */
SDL_WriteLE16(io, 1); /* uncompressed */ SDL_WriteLE16(io, 1); /* uncompressed */
SDL_WriteLE16(io, spec.channels); /* channels */ SDL_WriteLE16(io, cvtchans); /* channels */
SDL_WriteLE32(io, cvtfreq); /* sample rate */ SDL_WriteLE32(io, cvtfreq); /* sample rate */
SDL_WriteLE32(io, avgbytes); /* average bytes per second */ SDL_WriteLE32(io, avgbytes); /* average bytes per second */
SDL_WriteLE16(io, blockalign); /* block align */ SDL_WriteLE16(io, blockalign); /* block align */