Skip to content

Commit

Permalink
audio: Improvements in channel conversion code.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jan 8, 2017
1 parent 3516660 commit 38854e0
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 40 deletions.
76 changes: 41 additions & 35 deletions src/audio/SDL_audiocvt.c
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
12 changes: 7 additions & 5 deletions test/testresample.c
Expand Up @@ -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;
Expand All @@ -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());
Expand All @@ -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();
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down

0 comments on commit 38854e0

Please sign in to comment.