mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-01 23:07:45 +08:00
audio: Prebake the resampler's kaiser table instead of doing it at runtime.
This commit is contained in:
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Built with:
|
||||||
|
|
||||||
|
gcc -o genfilter build-scripts/gen_audio_resampler_filter.c -lm && ./genfilter > src/audio/SDL_audio_resampler_filter.h
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
SDL's resampler uses a "bandlimited interpolation" algorithm:
|
||||||
|
https://ccrma.stanford.edu/~jos/resample/
|
||||||
|
|
||||||
|
This code pre-generates the kaiser tables so we don't have to do this at
|
||||||
|
run time, at a cost of about 20 kilobytes of static data in SDL. This code
|
||||||
|
used to be part of SDL itself and generated the tables on the first use,
|
||||||
|
but that was expensive to produce on platforms without floating point
|
||||||
|
hardware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define RESAMPLER_ZERO_CROSSINGS 5
|
||||||
|
#define RESAMPLER_BITS_PER_SAMPLE 16
|
||||||
|
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))
|
||||||
|
#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)
|
||||||
|
|
||||||
|
/* This is a "modified" bessel function, so you can't use POSIX j0() */
|
||||||
|
static double
|
||||||
|
bessel(const double x)
|
||||||
|
{
|
||||||
|
const double xdiv2 = x / 2.0;
|
||||||
|
double i0 = 1.0f;
|
||||||
|
double f = 1.0f;
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
const double diff = pow(xdiv2, i * 2) / pow(f, 2);
|
||||||
|
if (diff < 1.0e-21f) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i0 += diff;
|
||||||
|
i++;
|
||||||
|
f *= (double) i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build kaiser table with cardinal sine applied to it, and array of differences between elements. */
|
||||||
|
static void
|
||||||
|
kaiser_and_sinc(float *table, float *diffs, const int tablelen, const double beta)
|
||||||
|
{
|
||||||
|
const int lenm1 = tablelen - 1;
|
||||||
|
const int lenm1div2 = lenm1 / 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
table[0] = 1.0f;
|
||||||
|
for (i = 1; i < tablelen; i++) {
|
||||||
|
const double kaiser = bessel(beta * sqrt(1.0 - pow(((i - lenm1) / 2.0) / lenm1div2, 2.0))) / bessel(beta);
|
||||||
|
table[tablelen - i] = (float) kaiser;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < tablelen; i++) {
|
||||||
|
const float x = (((float) i) / ((float) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * ((float) M_PI);
|
||||||
|
table[i] *= sinf(x) / x;
|
||||||
|
diffs[i - 1] = table[i] - table[i - 1];
|
||||||
|
}
|
||||||
|
diffs[lenm1] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float ResamplerFilter[RESAMPLER_FILTER_SIZE];
|
||||||
|
static float ResamplerFilterDifference[RESAMPLER_FILTER_SIZE];
|
||||||
|
|
||||||
|
static void
|
||||||
|
PrepareResampleFilter(void)
|
||||||
|
{
|
||||||
|
/* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */
|
||||||
|
const double dB = 80.0;
|
||||||
|
const double beta = 0.1102 * (dB - 8.7);
|
||||||
|
kaiser_and_sinc(ResamplerFilter, ResamplerFilterDifference, RESAMPLER_FILTER_SIZE, beta);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
PrepareResampleFilter();
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"/*\n"
|
||||||
|
" Simple DirectMedia Layer\n"
|
||||||
|
" Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>\n"
|
||||||
|
"\n"
|
||||||
|
" This software is provided 'as-is', without any express or implied\n"
|
||||||
|
" warranty. In no event will the authors be held liable for any damages\n"
|
||||||
|
" arising from the use of this software.\n"
|
||||||
|
"\n"
|
||||||
|
" Permission is granted to anyone to use this software for any purpose,\n"
|
||||||
|
" including commercial applications, and to alter it and redistribute it\n"
|
||||||
|
" freely, subject to the following restrictions:\n"
|
||||||
|
"\n"
|
||||||
|
" 1. The origin of this software must not be misrepresented; you must not\n"
|
||||||
|
" claim that you wrote the original software. If you use this software\n"
|
||||||
|
" in a product, an acknowledgment in the product documentation would be\n"
|
||||||
|
" appreciated but is not required.\n"
|
||||||
|
" 2. Altered source versions must be plainly marked as such, and must not be\n"
|
||||||
|
" misrepresented as being the original software.\n"
|
||||||
|
" 3. This notice may not be removed or altered from any source distribution.\n"
|
||||||
|
"*/\n"
|
||||||
|
"\n"
|
||||||
|
"/* DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_resampler_filter.c */\n"
|
||||||
|
"\n"
|
||||||
|
"#define RESAMPLER_ZERO_CROSSINGS %d\n"
|
||||||
|
"#define RESAMPLER_BITS_PER_SAMPLE %d\n"
|
||||||
|
"#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))\n"
|
||||||
|
"#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)\n"
|
||||||
|
"\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE
|
||||||
|
);
|
||||||
|
|
||||||
|
printf("static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = {\n");
|
||||||
|
printf(" %ff", ResamplerFilter[0]);
|
||||||
|
for (i = 0; i < RESAMPLER_FILTER_SIZE-1; i++) {
|
||||||
|
printf("%s%ff", ((i % 6) == 5) ? ",\n " : ", ", ResamplerFilter[i+1]);
|
||||||
|
}
|
||||||
|
printf("\n};\n\n");
|
||||||
|
|
||||||
|
printf("static const float ResamplerFilterDifference[RESAMPLER_FILTER_SIZE] = {\n");
|
||||||
|
printf(" %ff", ResamplerFilterDifference[0]);
|
||||||
|
for (i = 0; i < RESAMPLER_FILTER_SIZE-1; i++) {
|
||||||
|
printf("%s%ff", ((i % 6) == 5) ? ",\n " : ", ", ResamplerFilterDifference[i+1]);
|
||||||
|
}
|
||||||
|
printf("\n};\n\n");
|
||||||
|
printf("/* vi: set ts=4 sw=4 expandtab: */\n\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vi: set ts=4 sw=4 expandtab: */
|
||||||
|
|
||||||
@@ -1649,8 +1649,6 @@ SDL_AudioQuit(void)
|
|||||||
#ifdef HAVE_LIBSAMPLERATE_H
|
#ifdef HAVE_LIBSAMPLERATE_H
|
||||||
UnloadLibSampleRate();
|
UnloadLibSampleRate();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SDL_FreeResampleFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NUM_FORMATS 10
|
#define NUM_FORMATS 10
|
||||||
|
|||||||
@@ -70,11 +70,6 @@ extern SDL_AudioFilter SDL_Convert_F32_to_S16;
|
|||||||
extern SDL_AudioFilter SDL_Convert_F32_to_U16;
|
extern SDL_AudioFilter SDL_Convert_F32_to_U16;
|
||||||
extern SDL_AudioFilter SDL_Convert_F32_to_S32;
|
extern SDL_AudioFilter SDL_Convert_F32_to_S32;
|
||||||
|
|
||||||
/* You need to call SDL_PrepareResampleFilter() before using the internal resampler.
|
|
||||||
SDL_AudioQuit() calls SDL_FreeResamplerFilter(), you should never call it yourself. */
|
|
||||||
extern int SDL_PrepareResampleFilter(void);
|
|
||||||
extern void SDL_FreeResampleFilter(void);
|
|
||||||
|
|
||||||
#endif /* SDL_audio_c_h_ */
|
#endif /* SDL_audio_c_h_ */
|
||||||
|
|
||||||
/* vi: set ts=4 sw=4 expandtab: */
|
/* vi: set ts=4 sw=4 expandtab: */
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+2
-103
@@ -704,97 +704,7 @@ SDL_Convert51To71(SDL_AudioCVT * cvt, SDL_AudioFormat format)
|
|||||||
/* SDL's resampler uses a "bandlimited interpolation" algorithm:
|
/* SDL's resampler uses a "bandlimited interpolation" algorithm:
|
||||||
https://ccrma.stanford.edu/~jos/resample/ */
|
https://ccrma.stanford.edu/~jos/resample/ */
|
||||||
|
|
||||||
#define RESAMPLER_ZERO_CROSSINGS 5
|
#include "SDL_audio_resampler_filter.h"
|
||||||
#define RESAMPLER_BITS_PER_SAMPLE 16
|
|
||||||
#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))
|
|
||||||
#define RESAMPLER_FILTER_SIZE ((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) + 1)
|
|
||||||
|
|
||||||
/* This is a "modified" bessel function, so you can't use POSIX j0() */
|
|
||||||
static double
|
|
||||||
bessel(const double x)
|
|
||||||
{
|
|
||||||
const double xdiv2 = x / 2.0;
|
|
||||||
double i0 = 1.0f;
|
|
||||||
double f = 1.0f;
|
|
||||||
int i = 1;
|
|
||||||
|
|
||||||
while (SDL_TRUE) {
|
|
||||||
const double diff = SDL_pow(xdiv2, i * 2) / SDL_pow(f, 2);
|
|
||||||
if (diff < 1.0e-21f) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i0 += diff;
|
|
||||||
i++;
|
|
||||||
f *= (double) i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build kaiser table with cardinal sine applied to it, and array of differences between elements. */
|
|
||||||
static void
|
|
||||||
kaiser_and_sinc(float *table, float *diffs, const int tablelen, const double beta)
|
|
||||||
{
|
|
||||||
const int lenm1 = tablelen - 1;
|
|
||||||
const int lenm1div2 = lenm1 / 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
table[0] = 1.0f;
|
|
||||||
for (i = 1; i < tablelen; i++) {
|
|
||||||
const double kaiser = bessel(beta * SDL_sqrt(1.0 - SDL_pow(((i - lenm1) / 2.0) / lenm1div2, 2.0))) / bessel(beta);
|
|
||||||
table[tablelen - i] = (float) kaiser;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 1; i < tablelen; i++) {
|
|
||||||
const float x = (((float) i) / ((float) RESAMPLER_SAMPLES_PER_ZERO_CROSSING)) * ((float) M_PI);
|
|
||||||
table[i] *= SDL_sinf(x) / x;
|
|
||||||
diffs[i - 1] = table[i] - table[i - 1];
|
|
||||||
}
|
|
||||||
diffs[lenm1] = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static SDL_SpinLock ResampleFilterSpinlock = 0;
|
|
||||||
static float *ResamplerFilter = NULL;
|
|
||||||
static float *ResamplerFilterDifference = NULL;
|
|
||||||
|
|
||||||
int
|
|
||||||
SDL_PrepareResampleFilter(void)
|
|
||||||
{
|
|
||||||
SDL_AtomicLock(&ResampleFilterSpinlock);
|
|
||||||
if (!ResamplerFilter) {
|
|
||||||
/* if dB > 50, beta=(0.1102 * (dB - 8.7)), according to Matlab. */
|
|
||||||
const double dB = 80.0;
|
|
||||||
const double beta = 0.1102 * (dB - 8.7);
|
|
||||||
const size_t alloclen = RESAMPLER_FILTER_SIZE * sizeof (float);
|
|
||||||
|
|
||||||
ResamplerFilter = (float *) SDL_malloc(alloclen);
|
|
||||||
if (!ResamplerFilter) {
|
|
||||||
SDL_AtomicUnlock(&ResampleFilterSpinlock);
|
|
||||||
return SDL_OutOfMemory();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResamplerFilterDifference = (float *) SDL_malloc(alloclen);
|
|
||||||
if (!ResamplerFilterDifference) {
|
|
||||||
SDL_free(ResamplerFilter);
|
|
||||||
ResamplerFilter = NULL;
|
|
||||||
SDL_AtomicUnlock(&ResampleFilterSpinlock);
|
|
||||||
return SDL_OutOfMemory();
|
|
||||||
}
|
|
||||||
kaiser_and_sinc(ResamplerFilter, ResamplerFilterDifference, RESAMPLER_FILTER_SIZE, beta);
|
|
||||||
}
|
|
||||||
SDL_AtomicUnlock(&ResampleFilterSpinlock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SDL_FreeResampleFilter(void)
|
|
||||||
{
|
|
||||||
SDL_free(ResamplerFilter);
|
|
||||||
SDL_free(ResamplerFilterDifference);
|
|
||||||
ResamplerFilter = NULL;
|
|
||||||
ResamplerFilterDifference = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ResamplerPadding(const int inrate, const int outrate)
|
ResamplerPadding(const int inrate, const int outrate)
|
||||||
@@ -803,7 +713,7 @@ ResamplerPadding(const int inrate, const int outrate)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (inrate > outrate) {
|
if (inrate > outrate) {
|
||||||
return (int) SDL_ceil(((float) (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) / ((float) outrate)));
|
return (int) SDL_ceilf(((float) (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) / ((float) outrate)));
|
||||||
}
|
}
|
||||||
return RESAMPLER_SAMPLES_PER_ZERO_CROSSING;
|
return RESAMPLER_SAMPLES_PER_ZERO_CROSSING;
|
||||||
}
|
}
|
||||||
@@ -1119,10 +1029,6 @@ SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels,
|
|||||||
return SDL_SetError("No conversion available for these rates");
|
return SDL_SetError("No conversion available for these rates");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_PrepareResampleFilter() < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update (cvt) with filter details... */
|
/* Update (cvt) with filter details... */
|
||||||
if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
|
if (SDL_AddAudioCVTFilter(cvt, filter) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1743,13 +1649,6 @@ SDL_NewAudioStream(const SDL_AudioFormat src_format,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_PrepareResampleFilter() < 0) {
|
|
||||||
SDL_free(retval->resampler_state);
|
|
||||||
retval->resampler_state = NULL;
|
|
||||||
SDL_FreeAudioStream(retval);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval->resampler_func = SDL_ResampleAudioStream;
|
retval->resampler_func = SDL_ResampleAudioStream;
|
||||||
retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
|
retval->reset_resampler_func = SDL_ResetAudioStreamResampler;
|
||||||
retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
|
retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler;
|
||||||
|
|||||||
Reference in New Issue
Block a user