From 38854e03332d2298eefccd21c917de4220669bbf Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 8 Jan 2017 16:18:49 -0500 Subject: [PATCH] audio: Improvements in channel conversion code. --- src/audio/SDL_audiocvt.c | 76 ++++++++++++++++++++++------------------ test/testresample.c | 12 ++++--- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 561c2ca08..a9eecda05 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -31,7 +31,7 @@ /* Effectively mix right and left channels into a single channel */ static void SDLCALL -SDL_ConvertMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) +SDL_ConvertStereoToMono(SDL_AudioCVT * cvt, SDL_AudioFormat format) { float *dst = (float *) cvt->buf; 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 -SDL_ConvertStrip(SDL_AudioCVT * cvt, SDL_AudioFormat format) +SDL_Convert51ToStereo(SDL_AudioCVT * cvt, SDL_AudioFormat format) { float *dst = (float *) cvt->buf; const float *src = dst; int i; - LOG_DEBUG_CONVERT("6 channels", "stereo"); + LOG_DEBUG_CONVERT("5.1", "stereo"); 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) { - dst[0] = src[0]; - dst[1] = src[1]; + const double front_center = (double) src[2]; + 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; @@ -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 -SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format) +SDL_Convert51ToQuad(SDL_AudioCVT * cvt, SDL_AudioFormat format) { float *dst = (float *) cvt->buf; const float *src = dst; int i; - LOG_DEBUG_CONVERT("6 channels", "quad"); + LOG_DEBUG_CONVERT("5.1", "quad"); 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) { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; + /* FIXME: this is a good candidate for SIMD. */ + const double front_center = (double) src[2]; + dst[0] = (float) ((src[0] + front_center) * 0.5); /* FL */ + 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; @@ -99,9 +104,10 @@ SDL_ConvertStrip_2(SDL_AudioCVT * cvt, SDL_AudioFormat format) } } + /* Duplicate a mono channel to both stereo channels */ 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); 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 */ static void SDLCALL -SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format) +SDL_ConvertStereoTo51(SDL_AudioCVT * cvt, SDL_AudioFormat format) { int i; float lf, rf, ce; @@ -140,12 +146,13 @@ SDL_ConvertSurround(SDL_AudioCVT * cvt, SDL_AudioFormat format) src -= 2; lf = src[0]; rf = src[1]; - ce = (lf * 0.5f) + (rf * 0.5f); - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = lf - ce; - dst[3] = rf - ce; - dst[4] = dst[5] = ce; + ce = (lf + rf) * 0.5f; + dst[0] = lf + (lf - ce); /* FL */ + dst[1] = rf + (rf - ce); /* FR */ + dst[2] = ce; /* FC */ + dst[3] = ce; /* !!! FIXME: wrong! This is the subwoofer. */ + dst[4] = lf; /* BL */ + dst[5] = rf; /* BR */ } 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 */ 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); float *dst = (float *) (cvt->buf + cvt->len_cvt * 2); - float lf, rf, ce; + float lf, rf; int i; LOG_DEBUG_CONVERT("stereo", "quad"); @@ -172,11 +179,10 @@ SDL_ConvertSurround_4(SDL_AudioCVT * cvt, SDL_AudioFormat format) src -= 2; lf = src[0]; rf = src[1]; - ce = (lf / 2) + (rf / 2); - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = lf - ce; - dst[3] = rf - ce; + dst[0] = lf; /* FL */ + dst[1] = rf; /* FR */ + dst[2] = lf; /* BL */ + dst[3] = rf; /* BR */ } cvt->len_cvt *= 2; @@ -536,36 +542,36 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, /* Channel conversion */ if (src_channels != dst_channels) { 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; src_channels = 2; cvt->len_ratio *= 2; } if ((src_channels == 2) && (dst_channels == 6)) { - cvt->filters[cvt->filter_index++] = SDL_ConvertSurround; + cvt->filters[cvt->filter_index++] = SDL_ConvertStereoTo51; 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; + cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToQuad; 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->filters[cvt->filter_index++] = SDL_ConvertMonoToStereo; 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; + cvt->filters[cvt->filter_index++] = SDL_Convert51ToStereo; src_channels = 2; cvt->len_ratio /= 3; } 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; cvt->len_ratio /= 2; } @@ -575,7 +581,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, */ while (((src_channels % 2) == 0) && ((src_channels / 2) >= dst_channels)) { - cvt->filters[cvt->filter_index++] = SDL_ConvertMono; + cvt->filters[cvt->filter_index++] = SDL_ConvertStereoToMono; src_channels /= 2; cvt->len_ratio /= 2; } diff --git a/test/testresample.c b/test/testresample.c index 96e46f038..85a8eb71f 100644 --- a/test/testresample.c +++ b/test/testresample.c @@ -20,6 +20,7 @@ main(int argc, char **argv) Uint32 len = 0; Uint8 *data = NULL; int cvtfreq = 0; + int cvtchans = 0; int bitsize = 0; int blockalign = 0; int avgbytes = 0; @@ -28,12 +29,13 @@ main(int argc, char **argv) /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - if (argc != 4) { - SDL_Log("USAGE: %s in.wav out.wav newfreq\n", argv[0]); + if (argc != 5) { + SDL_Log("USAGE: %s in.wav out.wav newfreq newchans\n", argv[0]); return 1; } cvtfreq = SDL_atoi(argv[3]); + cvtchans = SDL_atoi(argv[4]); if (SDL_Init(SDL_INIT_AUDIO) == -1) { 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, - 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_FreeWAV(data); SDL_Quit(); @@ -83,7 +85,7 @@ main(int argc, char **argv) } bitsize = SDL_AUDIO_BITSIZE(spec.format); - blockalign = (bitsize / 8) * spec.channels; + blockalign = (bitsize / 8) * cvtchans; avgbytes = cvtfreq * blockalign; SDL_WriteLE32(io, 0x46464952); /* RIFF */ @@ -92,7 +94,7 @@ main(int argc, char **argv) SDL_WriteLE32(io, 0x20746D66); /* fmt */ SDL_WriteLE32(io, 16); /* chunk size */ 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, avgbytes); /* average bytes per second */ SDL_WriteLE16(io, blockalign); /* block align */