From 22461383c69f750cd1f66769f88c9893d5466368 Mon Sep 17 00:00:00 2001
From: Daniel Bomar <[EMAIL REDACTED]>
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 4d445b35cee2..600989e58bb1 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 11700d497d41..08888782242f 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 a516c554a114..a976dfd09e61 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 85faa4b0b75a..e79437e91d97 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;