Rebuild full ResamplerFilter (left wing + right wing) at runtime
parent
0c15ce0060
commit
6a73f74b6b
|
@ -50,7 +50,6 @@ gcc -o genfilter build-scripts/gen_audio_resampler_filter.c -lm && ./genfilter >
|
||||||
#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
|
#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
|
||||||
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
|
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
|
||||||
#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
|
#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
|
||||||
#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)
|
|
||||||
|
|
||||||
/* This is a "modified" bessel function, so you can't use POSIX j0() */
|
/* This is a "modified" bessel function, so you can't use POSIX j0() */
|
||||||
static double
|
static double
|
||||||
|
@ -136,18 +135,13 @@ int main(void)
|
||||||
"#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)\n"
|
"#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)\n"
|
||||||
"#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)\n"
|
"#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)\n"
|
||||||
"#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)\n"
|
"#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)\n"
|
||||||
"#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)\n"
|
|
||||||
"\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE
|
"\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE
|
||||||
);
|
);
|
||||||
|
|
||||||
printf("static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {");
|
printf("static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {");
|
||||||
for (i = 0; i < RESAMPLER_TABLE_SIZE; i++) {
|
for (i = 0; i < RESAMPLER_FILTER_SIZE; i++) {
|
||||||
double v = 0.0;
|
j = (i % RESAMPLER_ZERO_CROSSINGS) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING + (i / RESAMPLER_ZERO_CROSSINGS);
|
||||||
if (i < RESAMPLER_FILTER_SIZE) {
|
printf("%s%12.9ff,", (i % RESAMPLER_ZERO_CROSSINGS) ? "" : "\n ", ResamplerFilter[j]);
|
||||||
j = (i % RESAMPLER_ZERO_CROSSINGS) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING + (i / RESAMPLER_ZERO_CROSSINGS);
|
|
||||||
v = ResamplerFilter[j];
|
|
||||||
}
|
|
||||||
printf("%s%12.9ff,", (i % RESAMPLER_ZERO_CROSSINGS) ? "" : "\n ", v);
|
|
||||||
}
|
}
|
||||||
printf("\n};\n\n");
|
printf("\n};\n\n");
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,8 @@
|
||||||
#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
|
#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)
|
||||||
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
|
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)
|
||||||
#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
|
#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)
|
||||||
#define RESAMPLER_TABLE_SIZE (RESAMPLER_FILTER_SIZE + RESAMPLER_ZERO_CROSSINGS)
|
|
||||||
|
|
||||||
static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {
|
static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {
|
||||||
1.000000000f, 0.000000000f,-0.000000000f, 0.000000000f,-0.000000000f,
|
1.000000000f, 0.000000000f,-0.000000000f, 0.000000000f,-0.000000000f,
|
||||||
0.999993165f,-0.001679888f, 0.000529080f,-0.000151513f, 0.000027455f,
|
0.999993165f,-0.001679888f, 0.000529080f,-0.000151513f, 0.000027455f,
|
||||||
0.999972661f,-0.003351212f, 0.001055794f,-0.000302183f, 0.000054683f,
|
0.999972661f,-0.003351212f, 0.001055794f,-0.000302183f, 0.000054683f,
|
||||||
|
@ -541,6 +540,5 @@ static const float ResamplerFilter[RESAMPLER_TABLE_SIZE] = {
|
||||||
0.005090874f,-0.001601309f, 0.000459559f,-0.000083727f, 0.000003250f,
|
0.005090874f,-0.001601309f, 0.000459559f,-0.000083727f, 0.000003250f,
|
||||||
0.003385399f,-0.001065208f, 0.000305539f,-0.000055591f, 0.000002140f,
|
0.003385399f,-0.001065208f, 0.000305539f,-0.000055591f, 0.000002140f,
|
||||||
0.001688435f,-0.000531434f, 0.000152351f,-0.000027682f, 0.000001057f,
|
0.001688435f,-0.000531434f, 0.000152351f,-0.000027682f, 0.000001057f,
|
||||||
0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f, 0.000000000f,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -84,11 +84,20 @@ static int GetHistoryBufferSampleFrames(const int required_resampler_frames)
|
||||||
|
|
||||||
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
|
#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
|
||||||
|
|
||||||
|
#define RESAMPLER_FULL_FILTER_SIZE RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1)
|
||||||
|
|
||||||
// TODO: Add SIMD-accelerated versions
|
// TODO: Add SIMD-accelerated versions
|
||||||
static void ResampleFrame(const float* src, float* dst, const float* filter, const int chans)
|
static void ResampleFrame(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
|
||||||
{
|
{
|
||||||
int i, chan;
|
int i, chan;
|
||||||
|
|
||||||
|
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
||||||
|
|
||||||
|
// Interpolate between the nearest two filters
|
||||||
|
for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
|
||||||
|
filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
|
||||||
|
}
|
||||||
|
|
||||||
if (chans == 2) {
|
if (chans == 2) {
|
||||||
float v0 = 0.0f;
|
float v0 = 0.0f;
|
||||||
float v1 = 0.0f;
|
float v1 = 0.0f;
|
||||||
|
@ -136,12 +145,40 @@ static void ResampleFrame(const float* src, float* dst, const float* filter, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
|
||||||
|
|
||||||
|
void SDL_SetupAudioResampler()
|
||||||
|
{
|
||||||
|
// Build a table combining the left and right wings, for faster access
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
|
||||||
|
for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
|
||||||
|
int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
|
||||||
|
int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
|
||||||
|
|
||||||
|
float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
|
||||||
|
FullResamplerFilter[lwing] = value;
|
||||||
|
FullResamplerFilter[rwing] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
|
||||||
|
int rwing = i + RESAMPLER_ZERO_CROSSINGS;
|
||||||
|
int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
|
||||||
|
|
||||||
|
FullResamplerFilter[lwing] = 0.0f;
|
||||||
|
FullResamplerFilter[rwing] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes,
|
static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes,
|
||||||
const Sint64 resample_rate, Sint64* resample_offset)
|
const Sint64 resample_rate, Sint64* resample_offset)
|
||||||
{
|
{
|
||||||
SDL_assert(resample_rate > 0);
|
SDL_assert(resample_rate > 0);
|
||||||
float *dst = outbuf;
|
float *dst = outbuf;
|
||||||
int i, j;
|
int i;
|
||||||
|
|
||||||
Sint64 srcpos = *resample_offset;
|
Sint64 srcpos = *resample_offset;
|
||||||
|
|
||||||
|
@ -152,26 +189,12 @@ static void ResampleAudio(const int chans, const float *inbuf, const int inframe
|
||||||
|
|
||||||
SDL_assert(srcindex >= -1 && srcindex < inframes);
|
SDL_assert(srcindex >= -1 && srcindex < inframes);
|
||||||
|
|
||||||
const int filterindex = (int)(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_ZERO_CROSSINGS;
|
const float* filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
|
||||||
|
const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
|
||||||
const float interpolation1 = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
|
|
||||||
const float interpolation2 = 1.0f - interpolation1;
|
|
||||||
|
|
||||||
float filter[RESAMPLER_SAMPLES_PER_FRAME];
|
|
||||||
|
|
||||||
for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
|
|
||||||
const int filt_ind1 = filterindex + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
|
|
||||||
const int filt_ind2 = (RESAMPLER_FILTER_SIZE - 1) - filt_ind1;
|
|
||||||
|
|
||||||
const float scale1 = (ResamplerFilter[filt_ind1] * interpolation2) + (ResamplerFilter[filt_ind1 + RESAMPLER_ZERO_CROSSINGS] * interpolation1);
|
|
||||||
const float scale2 = (ResamplerFilter[filt_ind2] * interpolation1) + (ResamplerFilter[filt_ind2 + RESAMPLER_ZERO_CROSSINGS] * interpolation2);
|
|
||||||
|
|
||||||
filter[j] = scale1;
|
|
||||||
filter[j + RESAMPLER_ZERO_CROSSINGS] = scale2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float* src = &inbuf[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
|
const float* src = &inbuf[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
|
||||||
ResampleFrame(src, dst, filter, chans);
|
ResampleFrame(src, dst, filter, interp, chans);
|
||||||
|
|
||||||
dst += chans;
|
dst += chans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -962,6 +962,8 @@ void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = N
|
||||||
void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
|
void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
|
||||||
void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
|
void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
|
||||||
|
|
||||||
|
extern void SDL_SetupAudioResampler(void);
|
||||||
|
|
||||||
void SDL_ChooseAudioConverters(void)
|
void SDL_ChooseAudioConverters(void)
|
||||||
{
|
{
|
||||||
static SDL_bool converters_chosen = SDL_FALSE;
|
static SDL_bool converters_chosen = SDL_FALSE;
|
||||||
|
@ -969,6 +971,9 @@ void SDL_ChooseAudioConverters(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Hacks on top of hacks.
|
||||||
|
SDL_SetupAudioResampler();
|
||||||
|
|
||||||
#define SET_CONVERTER_FUNCS(fntype) \
|
#define SET_CONVERTER_FUNCS(fntype) \
|
||||||
SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
|
SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
|
||||||
SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \
|
SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \
|
||||||
|
|
Loading…
Reference in New Issue