audio: More effort to improve and simplify audio resamplers.
parent
52130bde40
commit
f12ab8f2b3
|
@ -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: */
|
||||
|
|
|
@ -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... */
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue