From c7f9dcb6fc32a8801fed3b10b3ea7d04571bd649 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 24 Jan 2017 15:52:22 -0500 Subject: [PATCH] audio: Offer a hint for libsamplerate quality/speed tradeoff. This defaults to the internal SDL resampler, since that's the likely default without a system-wide install of libsamplerate, but those that need more can tweak this. --- include/SDL_hints.h | 36 ++++++++++++++++++++++++++++++++++++ src/audio/SDL_audio.c | 22 +++++++++++++++++++--- src/audio/SDL_audio_c.h | 1 + src/audio/SDL_audiocvt.c | 2 +- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index a50bba315..ef6896601 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -775,6 +775,42 @@ extern "C" { */ #define SDL_HINT_OPENGL_ES_DRIVER "SDL_OPENGL_ES_DRIVER" +/** + * \brief A variable controlling speed/quality tradeoff of audio resampling. + * + * If available, SDL can use libsamplerate ( http://www.mega-nerd.com/SRC/ ) + * to handle audio resampling. There are different resampling modes available + * that produce different levels of quality, possibly using more CPU. + * + * If this hint isn't specified to a valid setting, or libsamplerate isn't + * available, SDL will act as if this hint was set to "fast". + * + * 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 "fast" resampler (although this + * might change for SDL 2.1). + * + * Most things can probably live with the "fast" resampler, but if quality + * is important or you can spare some CPU cycles, the other options are + * worth exploring! + * + * libsamplerate's interpolators, that these hints map to, are explained here: + * http://www.mega-nerd.com/SRC/api_misc.html#Converters + * + * This hint is only checked at audio subsystem init time and changes to it + * at other times are ignored. + * + * This variable can be set to the following values: + * + * "default" - Use SDL's internal, resampler. (Default when not set. low quality, fast.) + * "linear" - Use libsamplerate's Linear interpolator (low quality, fast). + * "zero_order_hold" - Use libsamplerate's Zero Order Hold interpolator (low quality, fast). + * "sinc_fastest" - Use libsamplerate's fastest (lowest quality) sinc interpolator. + * "sinc_medium" - Use libsamplerate's medium quality sinc interpolator. + * "sinc_best" - Use libsamplerate's best quality sinc interpolator. + */ +#define SDL_HINT_AUDIO_RESAMPLER_MODE "SDL_AUDIO_RESAMPLER_MODE" + /** * \brief An enumeration of hint priorities */ diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index a62ef4234..ec4c9814c 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -113,6 +113,7 @@ static const AudioBootStrap *const bootstrap[] = { static void *SRC_lib = NULL; #endif SDL_bool SRC_available = SDL_FALSE; +int SRC_converter = 0; SRC_STATE* (*SRC_src_new)(int converter_type, int channels, int *error) = NULL; int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data) = NULL; int (*SRC_src_reset)(SRC_STATE *state) = NULL; @@ -122,10 +123,25 @@ const char* (*SRC_src_strerror)(int error) = NULL; static SDL_bool LoadLibSampleRate(void) { - SRC_available = SDL_FALSE; + const char *hint = SDL_GetHint(SDL_HINT_AUDIO_RESAMPLER_MODE); - if (!SDL_GetHintBoolean("SDL_AUDIO_ALLOW_LIBRESAMPLE", SDL_TRUE)) { - return SDL_FALSE; + SRC_available = SDL_FALSE; + SRC_converter = 0; + + if (!hint || (SDL_strcasecmp(hint, "default") == 0)) { + return SDL_FALSE; /* don't load anything. */ + } else if (SDL_strcasecmp(hint, "linear") == 0) { + SRC_converter = SRC_LINEAR; + } else if (SDL_strcasecmp(hint, "zero_order_hold") == 0) { + SRC_converter = SRC_ZERO_ORDER_HOLD; + } else if (SDL_strcasecmp(hint, "sinc_fastest") == 0) { + SRC_converter = SRC_SINC_FASTEST; + } else if (SDL_strcasecmp(hint, "sinc_medium") == 0) { + SRC_converter = SRC_SINC_MEDIUM_QUALITY; + } else if (SDL_strcasecmp(hint, "sinc_best") == 0) { + SRC_converter = SRC_SINC_BEST_QUALITY; + } else { + return SDL_FALSE; /* treat it like "default", don't load anything. */ } #ifdef SDL_LIBSAMPLERATE_DYNAMIC diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h index 8a8af137a..6358a0865 100644 --- a/src/audio/SDL_audio_c.h +++ b/src/audio/SDL_audio_c.h @@ -39,6 +39,7 @@ #ifdef HAVE_LIBSAMPLERATE_H #include "samplerate.h" extern SDL_bool SRC_available; +extern int SRC_converter; extern SRC_STATE* (*SRC_src_new)(int converter_type, int channels, int *error); extern int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data); extern int (*SRC_src_reset)(SRC_STATE *state); diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 4a33a839d..73fb0c712 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -947,7 +947,7 @@ SetupLibSampleRateResampling(SDL_AudioStream *stream) SRC_STATE *state = NULL; if (SRC_available) { - state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result); + state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result); if (!state) { SDL_SetError("src_new() failed: %s", SRC_src_strerror(result)); }