SDL_audiocvt: Respct the SDL_HINT_AUDIO_RESAMPLING_MODE hint

This implements using libsamplerate for the SDL_AudioCVT API.
This library was already being used for audio streams when this hint is
set.
This commit is contained in:
Daniel Bomar 2022-10-15 15:54:12 -05:00 committed by Sam Lantinga
parent e8fdb861ef
commit 22461383c6
4 changed files with 75 additions and 5 deletions

View File

@ -278,10 +278,7 @@ extern "C" {
* If this hint isn't specified to a valid setting, or libsamplerate isn't * If this hint isn't specified to a valid setting, or libsamplerate isn't
* available, SDL will use the default, internal resampling algorithm. * available, SDL will use the default, internal resampling algorithm.
* *
* Note that this is currently only applicable to resampling audio that is * As of SDL 2.26, SDL_AudioCVT now respects this hint.
* being written to a device for playback or audio being read from a device
* for capture. SDL_AudioCVT always uses the default resampler (although this
* might change for SDL 2.1).
* *
* This hint is currently only checked at audio subsystem initialization. * This hint is currently only checked at audio subsystem initialization.
* *

View File

@ -144,6 +144,7 @@ int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data) = NULL;
int (*SRC_src_reset)(SRC_STATE *state) = NULL; int (*SRC_src_reset)(SRC_STATE *state) = NULL;
SRC_STATE* (*SRC_src_delete)(SRC_STATE *state) = NULL; SRC_STATE* (*SRC_src_delete)(SRC_STATE *state) = NULL;
const char* (*SRC_src_strerror)(int error) = NULL; const char* (*SRC_src_strerror)(int error) = NULL;
int (*SRC_src_simple)(SRC_DATA *data, int converter_type, int channels) = NULL;
static SDL_bool static SDL_bool
LoadLibSampleRate(void) LoadLibSampleRate(void)
@ -178,8 +179,9 @@ LoadLibSampleRate(void)
SRC_src_reset = (int(*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_reset"); SRC_src_reset = (int(*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_reset");
SRC_src_delete = (SRC_STATE* (*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_delete"); SRC_src_delete = (SRC_STATE* (*)(SRC_STATE *state))SDL_LoadFunction(SRC_lib, "src_delete");
SRC_src_strerror = (const char* (*)(int error))SDL_LoadFunction(SRC_lib, "src_strerror"); SRC_src_strerror = (const char* (*)(int error))SDL_LoadFunction(SRC_lib, "src_strerror");
SRC_src_simple = (int(*)(SRC_DATA *data, int converter_type, int channels))SDL_LoadFunction(SRC_lib, "src_simple");
if (!SRC_src_new || !SRC_src_process || !SRC_src_reset || !SRC_src_delete || !SRC_src_strerror) { if (!SRC_src_new || !SRC_src_process || !SRC_src_reset || !SRC_src_delete || !SRC_src_strerror || !SRC_src_simple) {
SDL_UnloadObject(SRC_lib); SDL_UnloadObject(SRC_lib);
SRC_lib = NULL; SRC_lib = NULL;
return SDL_FALSE; return SDL_FALSE;
@ -190,6 +192,7 @@ LoadLibSampleRate(void)
SRC_src_reset = src_reset; SRC_src_reset = src_reset;
SRC_src_delete = src_delete; SRC_src_delete = src_delete;
SRC_src_strerror = src_strerror; SRC_src_strerror = src_strerror;
SRC_src_simple = src_simple;
#endif #endif
SRC_available = SDL_TRUE; SRC_available = SDL_TRUE;

View File

@ -45,6 +45,7 @@ extern int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data);
extern int (*SRC_src_reset)(SRC_STATE *state); extern int (*SRC_src_reset)(SRC_STATE *state);
extern SRC_STATE* (*SRC_src_delete)(SRC_STATE *state); extern SRC_STATE* (*SRC_src_delete)(SRC_STATE *state);
extern const char* (*SRC_src_strerror)(int error); extern const char* (*SRC_src_strerror)(int error);
extern int (*SRC_src_simple)(SRC_DATA *data, int converter_type, int channels);
#endif #endif
/* Functions to get a list of "close" audio formats */ /* Functions to get a list of "close" audio formats */

View File

@ -418,6 +418,48 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt)
return retval; return retval;
} }
#ifdef HAVE_LIBSAMPLERATE_H
static void
SDL_ResampleCVT_SRC(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
{
const float *src = (const float *) cvt->buf;
const int srclen = cvt->len_cvt;
float *dst = (float *) (cvt->buf + srclen);
const int dstlen = (cvt->len * cvt->len_mult) - srclen;
const int framelen = sizeof(float) * chans;
int result = 0;
SRC_DATA data;
SDL_zero(data);
data.data_in = (float *)src; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
data.input_frames = srclen / framelen;
data.data_out = dst;
data.output_frames = dstlen / framelen;
data.src_ratio = cvt->rate_incr;
result = SRC_src_simple(&data, SRC_converter, chans); /* Simple API converts the whole buffer at once. No need for initialization. */
/* !!! FIXME: Handle library failures? */
#ifdef DEBUG_CONVERT
if (result != 0) {
SDL_Log("src_simple() failed: %s", SRC_src_strerror(result));
}
#endif
cvt->len_cvt = data.output_frames_gen * framelen;
SDL_memmove(cvt->buf, dst, cvt->len_cvt);
if (cvt->filters[++cvt->filter_index]) {
cvt->filters[cvt->filter_index](cvt, format);
}
}
#endif /* HAVE_LIBSAMPLERATE_H */
static void static void
SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format) SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format)
{ {
@ -478,9 +520,36 @@ RESAMPLER_FUNCS(6)
RESAMPLER_FUNCS(8) RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS #undef RESAMPLER_FUNCS
#ifdef HAVE_LIBSAMPLERATE_H
#define RESAMPLER_FUNCS(chans) \
static void SDLCALL \
SDL_ResampleCVT_SRC_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \
SDL_ResampleCVT_SRC(cvt, chans, format); \
}
RESAMPLER_FUNCS(1)
RESAMPLER_FUNCS(2)
RESAMPLER_FUNCS(4)
RESAMPLER_FUNCS(6)
RESAMPLER_FUNCS(8)
#undef RESAMPLER_FUNCS
#endif /* HAVE_LIBSAMPLERATE_H */
static SDL_AudioFilter static SDL_AudioFilter
ChooseCVTResampler(const int dst_channels) ChooseCVTResampler(const int dst_channels)
{ {
#ifdef HAVE_LIBSAMPLERATE_H
if (SRC_available) {
switch (dst_channels) {
case 1: return SDL_ResampleCVT_SRC_c1;
case 2: return SDL_ResampleCVT_SRC_c2;
case 4: return SDL_ResampleCVT_SRC_c4;
case 6: return SDL_ResampleCVT_SRC_c6;
case 8: return SDL_ResampleCVT_SRC_c8;
default: break;
}
}
#endif /* HAVE_LIBSAMPLERATE_H */
switch (dst_channels) { switch (dst_channels) {
case 1: return SDL_ResampleCVT_c1; case 1: return SDL_ResampleCVT_c1;
case 2: return SDL_ResampleCVT_c2; case 2: return SDL_ResampleCVT_c2;