audio: Offer a hint for libsamplerate quality/speed tradeoff.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 24 Jan 2017 15:52:22 -0500
changeset 10849bc671e6906ae
parent 10848 7764aba2f211
child 10850 c9dc0068b0e7
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
src/audio/SDL_audio.c
src/audio/SDL_audio_c.h
src/audio/SDL_audiocvt.c
     1.1 --- a/include/SDL_hints.h	Tue Jan 24 12:20:41 2017 -0500
     1.2 +++ b/include/SDL_hints.h	Tue Jan 24 15:52:22 2017 -0500
     1.3 @@ -776,6 +776,42 @@
     1.4  #define SDL_HINT_OPENGL_ES_DRIVER   "SDL_OPENGL_ES_DRIVER"
     1.5  
     1.6  /**
     1.7 + *  \brief  A variable controlling speed/quality tradeoff of audio resampling.
     1.8 + *
     1.9 + *  If available, SDL can use libsamplerate ( http://www.mega-nerd.com/SRC/ )
    1.10 + *  to handle audio resampling. There are different resampling modes available
    1.11 + *  that produce different levels of quality, possibly using more CPU.
    1.12 + *
    1.13 + *  If this hint isn't specified to a valid setting, or libsamplerate isn't
    1.14 + *  available, SDL will act as if this hint was set to "fast".
    1.15 + *
    1.16 + *  Note that this is currently only applicable to resampling audio that is
    1.17 + *  being written to a device for playback or audio being read from a device
    1.18 + *  for capture. SDL_AudioCVT always uses the "fast" resampler (although this
    1.19 + *  might change for SDL 2.1).
    1.20 + *
    1.21 + *  Most things can probably live with the "fast" resampler, but if quality
    1.22 + *  is important or you can spare some CPU cycles, the other options are
    1.23 + *  worth exploring!
    1.24 + *
    1.25 + *  libsamplerate's interpolators, that these hints map to, are explained here:
    1.26 + *     http://www.mega-nerd.com/SRC/api_misc.html#Converters
    1.27 + *
    1.28 + *  This hint is only checked at audio subsystem init time and changes to it
    1.29 + *  at other times are ignored.
    1.30 + *
    1.31 + *  This variable can be set to the following values:
    1.32 + *
    1.33 + *    "default"  - Use SDL's internal, resampler. (Default when not set. low quality, fast.)
    1.34 + *    "linear" - Use libsamplerate's Linear interpolator (low quality, fast).
    1.35 + *    "zero_order_hold" - Use libsamplerate's Zero Order Hold interpolator (low quality, fast).
    1.36 + *    "sinc_fastest" - Use libsamplerate's fastest (lowest quality) sinc interpolator.
    1.37 + *    "sinc_medium" - Use libsamplerate's medium quality sinc interpolator.
    1.38 + *    "sinc_best" - Use libsamplerate's best quality sinc interpolator.
    1.39 + */
    1.40 +#define SDL_HINT_AUDIO_RESAMPLER_MODE   "SDL_AUDIO_RESAMPLER_MODE"
    1.41 +
    1.42 +/**
    1.43   *  \brief  An enumeration of hint priorities
    1.44   */
    1.45  typedef enum
     2.1 --- a/src/audio/SDL_audio.c	Tue Jan 24 12:20:41 2017 -0500
     2.2 +++ b/src/audio/SDL_audio.c	Tue Jan 24 15:52:22 2017 -0500
     2.3 @@ -113,6 +113,7 @@
     2.4  static void *SRC_lib = NULL;
     2.5  #endif
     2.6  SDL_bool SRC_available = SDL_FALSE;
     2.7 +int SRC_converter = 0;
     2.8  SRC_STATE* (*SRC_src_new)(int converter_type, int channels, int *error) = NULL;
     2.9  int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data) = NULL;
    2.10  int (*SRC_src_reset)(SRC_STATE *state) = NULL;
    2.11 @@ -122,10 +123,25 @@
    2.12  static SDL_bool
    2.13  LoadLibSampleRate(void)
    2.14  {
    2.15 +    const char *hint = SDL_GetHint(SDL_HINT_AUDIO_RESAMPLER_MODE);
    2.16 +
    2.17      SRC_available = SDL_FALSE;
    2.18 +    SRC_converter = 0;
    2.19  
    2.20 -    if (!SDL_GetHintBoolean("SDL_AUDIO_ALLOW_LIBRESAMPLE", SDL_TRUE)) {
    2.21 -        return SDL_FALSE;
    2.22 +    if (!hint || (SDL_strcasecmp(hint, "default") == 0)) {
    2.23 +        return SDL_FALSE;  /* don't load anything. */
    2.24 +    } else if (SDL_strcasecmp(hint, "linear") == 0) {
    2.25 +        SRC_converter = SRC_LINEAR;
    2.26 +    } else if (SDL_strcasecmp(hint, "zero_order_hold") == 0) {
    2.27 +        SRC_converter = SRC_ZERO_ORDER_HOLD;
    2.28 +    } else if (SDL_strcasecmp(hint, "sinc_fastest") == 0) {
    2.29 +        SRC_converter = SRC_SINC_FASTEST;
    2.30 +    } else if (SDL_strcasecmp(hint, "sinc_medium") == 0) {
    2.31 +        SRC_converter = SRC_SINC_MEDIUM_QUALITY;
    2.32 +    } else if (SDL_strcasecmp(hint, "sinc_best") == 0) {
    2.33 +        SRC_converter = SRC_SINC_BEST_QUALITY;
    2.34 +    } else {
    2.35 +        return SDL_FALSE;  /* treat it like "default", don't load anything. */
    2.36      }
    2.37  
    2.38  #ifdef SDL_LIBSAMPLERATE_DYNAMIC
     3.1 --- a/src/audio/SDL_audio_c.h	Tue Jan 24 12:20:41 2017 -0500
     3.2 +++ b/src/audio/SDL_audio_c.h	Tue Jan 24 15:52:22 2017 -0500
     3.3 @@ -39,6 +39,7 @@
     3.4  #ifdef HAVE_LIBSAMPLERATE_H
     3.5  #include "samplerate.h"
     3.6  extern SDL_bool SRC_available;
     3.7 +extern int SRC_converter;
     3.8  extern SRC_STATE* (*SRC_src_new)(int converter_type, int channels, int *error);
     3.9  extern int (*SRC_src_process)(SRC_STATE *state, SRC_DATA *data);
    3.10  extern int (*SRC_src_reset)(SRC_STATE *state);
     4.1 --- a/src/audio/SDL_audiocvt.c	Tue Jan 24 12:20:41 2017 -0500
     4.2 +++ b/src/audio/SDL_audiocvt.c	Tue Jan 24 15:52:22 2017 -0500
     4.3 @@ -947,7 +947,7 @@
     4.4      SRC_STATE *state = NULL;
     4.5  
     4.6      if (SRC_available) {
     4.7 -        state = SRC_src_new(SRC_SINC_FASTEST, stream->pre_resample_channels, &result);
     4.8 +        state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result);
     4.9          if (!state) {
    4.10              SDL_SetError("src_new() failed: %s", SRC_src_strerror(result));
    4.11          }