Commit c7f9dcb6fc32a8801fed3b10b3ea7d04571bd649

Ryan C. Gordon 2017-01-24T15:52:22

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.

diff --git a/include/SDL_hints.h b/include/SDL_hints.h
index a50bba3..ef68966 100644
--- a/include/SDL_hints.h
+++ b/include/SDL_hints.h
@@ -776,6 +776,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
  */
 typedef enum
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index a62ef42..ec4c981 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 8a8af13..6358a08 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 4a33a83..73fb0c7 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));
         }