audio: More effort to improve and simplify audio resamplers.

Ryan C. Gordon 2017-01-05 19:12:20 -05:00
parent 52130bde40
commit f12ab8f2b3
3 changed files with 128 additions and 162 deletions

View File

@ -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: */

View File

@ -331,14 +331,43 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
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_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_Multiple(cvt, 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_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(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); \
}
RESAMPLER_FUNCS(1)
RESAMPLER_FUNCS(2)
RESAMPLER_FUNCS(4)
RESAMPLER_FUNCS(6)
RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS
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);
@ -352,110 +381,73 @@ SDL_FindFrequencyMultiple(const int src_rate, const int 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;
return hi / lo;
}
#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_assert(format == AUDIO_F32SYS); \
SDL_Upsample_x2(cvt, chans); \
} \
static void SDLCALL \
SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(cvt, 2, chans); \
} \
static void SDLCALL \
SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Upsample_x4(cvt, chans); \
} \
static void SDLCALL \
SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_assert(format == AUDIO_F32SYS); \
SDL_Downsample_Multiple(cvt, 4, chans); \
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; \
}
RESAMPLER_FUNCS(1)
RESAMPLER_FUNCS(2)
RESAMPLER_FUNCS(4)
RESAMPLER_FUNCS(6)
RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS
if (upsample) {
if (multiple) {
PICK_CHANNEL_FILTER(Upsample, Multiple);
} else {
PICK_CHANNEL_FILTER(Upsample, Arbitrary);
}
} else {
if (multiple) {
PICK_CHANNEL_FILTER(Downsample, Multiple);
} else {
PICK_CHANNEL_FILTER(Downsample, Arbitrary);
}
}
#undef PICK_CHANNEL_FILTER
return filter;
}
static int
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels,
int src_rate, int dst_rate)
SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
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;
SDL_AudioFilter filter;
#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 == 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);
}
}
#undef PICK_CHANNEL_FILTER
if (filter == NULL) {
return SDL_SetError("No conversion available for these rates");
}
/* 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. */
if (src_rate == dst_rate) {
return 0; /* no conversion necessary. */
}
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");
}
/* 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. */
}
@ -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. */
@ -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... */
}

View File

@ -220,14 +220,14 @@ void
SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
{
const int srcsize = cvt->len_cvt - (64 * channels);
const int dstsize = (int) (((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr) * (channels*4);
const int dstsize = (int) ((((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr)) * (channels*4);
register int eps = 0;
float *dst = ((float *) (cvt->buf + dstsize)) - channels;
const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
const float *target = ((const float *) cvt->buf);
const size_t cpy = sizeof (float) * channels;
float last_sample[8];
float sample[8];
float last_sample[8];
int i;
#if DEBUG_CONVERT
@ -236,7 +236,9 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
SDL_assert(channels <= 8);
SDL_memcpy(sample, src, cpy);
for (i = 0; i < channels; i++) {
sample[i] = (float) ((((double) src[i]) + ((double) src[i - channels])) * 0.5);
}
SDL_memcpy(last_sample, src, cpy);
while (dst > target) {
@ -244,11 +246,15 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
dst -= channels;
eps += srcsize;
if ((eps << 1) >= dstsize) {
src -= channels;
for (i = 0; i < channels; i++) {
sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
if (src > target) {
src -= channels;
for (i = 0; i < channels; i++) {
sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
}
} else {
}
SDL_memcpy(last_sample, sample, cpy);
SDL_memcpy(last_sample, src, cpy);
eps -= dstsize;
}
}
@ -291,7 +297,7 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
for (i = 0; i < channels; i++) {
sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5);
}
SDL_memcpy(last_sample, sample, cpy);
SDL_memcpy(last_sample, src, cpy);
eps -= srcsize;
}
}
@ -303,32 +309,43 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels)
}
void
SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels)
SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels)
{
const int dstsize = cvt->len_cvt * 2;
float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 2);
const int multiple = (int) cvt->rate_incr;
const int dstsize = cvt->len_cvt * multiple;
float *buf = (float *) cvt->buf;
float *dst = ((float *) (cvt->buf + dstsize)) - channels;
const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
const float *target = ((const float *) cvt->buf);
const float *target = buf + channels;
const size_t cpy = sizeof (float) * channels;
float last_sample[8];
int i;
#if DEBUG_CONVERT
fprintf(stderr, "Upsample (x2), %d channels.\n", channels);
fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels);
#endif
SDL_assert(channels <= 8);
SDL_memcpy(last_sample, src, cpy);
while (dst > target) {
SDL_assert(src >= buf);
for (i = 0; i < channels; i++) {
dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5);
}
dst -= channels;
SDL_memcpy(dst, src, cpy);
SDL_memcpy(last_sample, src, cpy);
for (i = 1; i < multiple; i++) {
SDL_memcpy(dst, dst + channels, cpy);
dst -= channels;
}
src -= channels;
dst -= channels;
if (src > buf) {
SDL_memcpy(last_sample, src - channels, cpy);
}
}
cvt->len_cvt = dstsize;
@ -338,51 +355,9 @@ SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels)
}
void
SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels)
{
const int dstsize = cvt->len_cvt * 4;
float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 4);
const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels;
const float *target = ((const float *) cvt->buf);
const size_t cpy = sizeof (float) * channels;
float last_sample[8];
int i;
#if DEBUG_CONVERT
fprintf(stderr, "Upsample (x4), %d channels.\n", channels);
#endif
SDL_assert(channels <= 8);
SDL_memcpy(last_sample, src, cpy);
while (dst > target) {
for (i = 0; i < channels; i++) {
dst[i] = (float) ((((double) src[i]) + (3.0 * ((double) last_sample[i]))) * 0.25);
}
dst -= channels;
for (i = 0; i < channels; i++) {
dst[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.25);
}
dst -= channels;
for (i = 0; i < channels; i++) {
dst[i] = (float) (((3.0 * ((double) src[i])) + ((double) last_sample[i])) * 0.25);
}
dst -= channels;
SDL_memcpy(dst, src, cpy);
dst -= channels;
SDL_memcpy(last_sample, src, cpy);
src -= channels;
}
cvt->len_cvt = dstsize;
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
}
}
void
SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels)
SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels)
{
const int multiple = (int) (1.0 / cvt->rate_incr);
const int dstsize = cvt->len_cvt / multiple;
float *dst = (float *) cvt->buf;
const float *src = (float *) cvt->buf;