From 22461383c69f750cd1f66769f88c9893d5466368 Mon Sep 17 00:00:00 2001 From: Daniel Bomar Date: Sat, 15 Oct 2022 15:54:12 -0500 Subject: [PATCH] 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. --- include/SDL_hints.h | 5 +-- src/audio/SDL_audio.c | 5 ++- src/audio/SDL_audio_c.h | 1 + src/audio/SDL_audiocvt.c | 69 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 4d445b35c..600989e58 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -278,10 +278,7 @@ extern "C" { * If this hint isn't specified to a valid setting, or libsamplerate isn't * available, SDL will use the default, internal resampling algorithm. * - * Note that this is currently only applicable to resampling audio that is - * 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). + * As of SDL 2.26, SDL_AudioCVT now respects this hint. * * This hint is currently only checked at audio subsystem initialization. * diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 11700d497..088887822 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -144,6 +144,7 @@ int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data) = NULL; int (*SRC_src_reset)(SRC_STATE *state) = NULL; SRC_STATE* (*SRC_src_delete)(SRC_STATE *state) = 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 LoadLibSampleRate(void) @@ -178,8 +179,9 @@ LoadLibSampleRate(void) 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_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); SRC_lib = NULL; return SDL_FALSE; @@ -190,6 +192,7 @@ LoadLibSampleRate(void) SRC_src_reset = src_reset; SRC_src_delete = src_delete; SRC_src_strerror = src_strerror; + SRC_src_simple = src_simple; #endif SRC_available = SDL_TRUE; diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h index a516c554a..a976dfd09 100644 --- a/src/audio/SDL_audio_c.h +++ b/src/audio/SDL_audio_c.h @@ -45,6 +45,7 @@ extern int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data); extern int (*SRC_src_reset)(SRC_STATE *state); extern SRC_STATE* (*SRC_src_delete)(SRC_STATE *state); extern const char* (*SRC_src_strerror)(int error); +extern int (*SRC_src_simple)(SRC_DATA *data, int converter_type, int channels); #endif /* Functions to get a list of "close" audio formats */ diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 85faa4b0b..e79437e91 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -418,6 +418,48 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt) 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 SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format) { @@ -478,9 +520,36 @@ RESAMPLER_FUNCS(6) RESAMPLER_FUNCS(8) #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 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) { case 1: return SDL_ResampleCVT_c1; case 2: return SDL_ResampleCVT_c2;