Skip to content

Commit

Permalink
audio: More effort to improve and simplify audio resamplers.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jan 6, 2017
1 parent 52130bd commit f12ab8f
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 157 deletions.
5 changes: 2 additions & 3 deletions src/audio/SDL_audio_c.h
Expand Up @@ -50,9 +50,8 @@ void SDLCALL SDL_Convert_F32_to_S16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDLCALL SDL_Convert_F32_to_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDLCALL SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format);
void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
void SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels);
void SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels);
void SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels);
void SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels);
void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels);
void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels);

/* vi: set ts=4 sw=4 expandtab: */
184 changes: 88 additions & 96 deletions src/audio/SDL_audiocvt.c
Expand Up @@ -331,67 +331,31 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
return retval;
}

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. */
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;

return retval;
}

/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store
!!! FIXME: channel info or integer sample rates, so we have to have
!!! FIXME: function entry points for each supported channel count and
!!! FIXME: multiple vs arbitrary. When we rev the ABI, remove this. */
#define RESAMPLER_FUNCS(chans) \
static void SDLCALL \
SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_Arbitrary(cvt, chans); \
}\
static void SDLCALL \
SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Arbitrary(cvt, chans); \
} \
static void SDLCALL \
SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_x2(cvt, chans); \
SDL_Upsample_Multiple(cvt, chans); \
} \
static void SDLCALL \
SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(cvt, 2, chans); \
} \
SDL_Upsample_Arbitrary(cvt, chans); \
}\
static void SDLCALL \
SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_x4(cvt, chans); \
SDL_Downsample_Multiple(cvt, chans); \
} \
static void SDLCALL \
SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(cvt, 4, chans); \
SDL_Downsample_Arbitrary(cvt, chans); \
}
RESAMPLER_FUNCS(1)
RESAMPLER_FUNCS(2)
Expand All @@ -401,61 +365,89 @@ RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS

static int
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
int src_rate, int dst_rate)
SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate)
{
if (src_rate != dst_rate) {
const int upsample = (src_rate < dst_rate) ? 1 : 0;
const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
SDL_AudioFilter filter = NULL;
int lo, hi;

#define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
default: break; \
}
SDL_assert(src_rate != 0);
SDL_assert(dst_rate != 0);
SDL_assert(src_rate != dst_rate);

if (upsample) {
if (multiple == 0) {
PICK_CHANNEL_FILTER(Upsample, Arbitrary);
} else if (multiple == 2) {
PICK_CHANNEL_FILTER(Upsample, x2);
} else if (multiple == 4) {
PICK_CHANNEL_FILTER(Upsample, x4);
}
} else {
if (multiple == 0) {
PICK_CHANNEL_FILTER(Downsample, Arbitrary);
} else if (multiple == 2) {
PICK_CHANNEL_FILTER(Downsample, x2);
} else if (multiple == 4) {
PICK_CHANNEL_FILTER(Downsample, x4);
}
}
if (src_rate < dst_rate) {
lo = src_rate;
hi = dst_rate;
} else {
lo = dst_rate;
hi = src_rate;
}

#undef PICK_CHANNEL_FILTER
if ((hi % lo) != 0)
return 0; /* not a multiple. */

if (filter == NULL) {
return SDL_SetError("No conversion available for these rates");
}
return hi / lo;
}

/* 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;
static SDL_AudioFilter
ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate)
{
const int upsample = (src_rate < dst_rate) ? 1 : 0;
const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate);
SDL_AudioFilter filter = NULL;

#define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \
case 1: filter = SDL_##upordown##_##resampler##_c1; break; \
case 2: filter = SDL_##upordown##_##resampler##_c2; break; \
case 4: filter = SDL_##upordown##_##resampler##_c4; break; \
case 6: filter = SDL_##upordown##_##resampler##_c6; break; \
case 8: filter = SDL_##upordown##_##resampler##_c8; break; \
default: break; \
}

if (upsample) {
if (multiple) {
PICK_CHANNEL_FILTER(Upsample, Multiple);
} else {
PICK_CHANNEL_FILTER(Upsample, Arbitrary);
}
} else {
if (multiple) {
PICK_CHANNEL_FILTER(Downsample, Multiple);
} else {
cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate);
PICK_CHANNEL_FILTER(Downsample, Arbitrary);
}
}

#undef PICK_CHANNEL_FILTER

return filter;
}

static int
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
const int src_rate, const int dst_rate)
{
SDL_AudioFilter filter;

if (src_rate == dst_rate) {
return 0; /* no conversion necessary. */
}

filter = ChooseResampler(dst_channels, src_rate, dst_rate);
if (filter == NULL) {
return SDL_SetError("No conversion available for these rates");
}

return 1; /* added a converter. */
/* 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 0; /* no conversion necessary. */
return 1; /* added a converter. */
}


Expand Down Expand Up @@ -514,7 +506,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
The expectation is we can process data faster in float32
(possibly with SIMD), and making several passes over the same
buffer in is likely to be CPU cache-friendly, avoiding the
buffer is likely to be CPU cache-friendly, avoiding the
biggest performance hit in modern times. Previously we had
(script-generated) custom converters for every data type and
it was a bloat on SDL compile times and final library size. */
Expand Down Expand Up @@ -585,11 +577,11 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt,
}

/* Do rate conversion, if necessary. Updates (cvt). */
if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) ==
-1) {
if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) {
return -1; /* shouldn't happen, but just in case... */
}

/* Move to final data type. */
if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) {
return -1; /* shouldn't happen, but just in case... */
}
Expand Down

0 comments on commit f12ab8f

Please sign in to comment.