SDL: audio: Redesigned audio conversion code for SDL3.

From e5a6c24c822e11c9a99f10adad49f904b8170562 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 1 Apr 2023 22:29:30 -0400
Subject: [PATCH] audio: Redesigned audio conversion code for SDL3.

- SDL_AudioCVT is gone, even internally.
- libsamplerate is gone (I suspect our resampler is finally Good Enough).
- Cleanups and improvements to audio conversion interfaces.
- SDL_AudioStream can change its input/output format/rate/channels on the fly!
---
 CMakeLists.txt                                |    3 -
 build-scripts/gen_audio_channel_conversion.c  |   80 +-
 cmake/sdlchecks.cmake                         |   47 -
 docs/README-linux.md                          |    8 +-
 docs/README-migration.md                      |    3 +
 include/SDL3/SDL_audio.h                      |  107 +-
 include/SDL3/SDL_hints.h                      |   14 +-
 include/build_config/SDL_build_config.h.cmake |    4 -
 src/audio/SDL_audio.c                         |   95 -
 src/audio/SDL_audio_c.h                       |   44 +-
 src/audio/SDL_audio_channel_converters.h      |  806 +++-----
 src/audio/SDL_audiocvt.c                      | 1795 +++++++----------
 src/audio/SDL_audiocvt_c.h                    |   69 -
 src/audio/SDL_audiotypecvt.c                  |  465 ++---
 src/audio/SDL_mixer.c                         |    3 +
 src/dynapi/SDL_dynapi.sym                     |    2 +
 src/dynapi/SDL_dynapi_overrides.h             |    2 +
 src/dynapi/SDL_dynapi_procs.h                 |    4 +-
 test/CMakeLists.txt                           |    1 +
 test/testaudiostreamdynamicresample.c         |  123 ++
 20 files changed, 1370 insertions(+), 2305 deletions(-)
 delete mode 100644 src/audio/SDL_audiocvt_c.h
 create mode 100644 test/testaudiostreamdynamicresample.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6cca05ba958e..407f7e46ab15 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -411,8 +411,6 @@ set_option(SDL_PULSEAUDIO          "Use PulseAudio" ${UNIX_SYS})
 dep_option(SDL_PULSEAUDIO_SHARED   "Dynamically load PulseAudio support" ON "SDL_PULSEAUDIO" OFF)
 set_option(SDL_SNDIO               "Support the sndio audio API" ${UNIX_SYS})
 dep_option(SDL_SNDIO_SHARED        "Dynamically load the sndio audio API" ON "SDL_SNDIO" OFF)
-set_option(SDL_LIBSAMPLERATE       "Use libsamplerate for audio rate conversion" ${UNIX_SYS})
-dep_option(SDL_LIBSAMPLERATE_SHARED "Dynamically load libsamplerate" ON "SDL_LIBSAMPLERATE" OFF)
 set_option(SDL_RPATH               "Use an rpath when linking SDL" ${UNIX_SYS})
 set_option(SDL_CLOCK_GETTIME       "Use clock_gettime() instead of gettimeofday()" ${SDL_CLOCK_GETTIME_ENABLED_BY_DEFAULT})
 set_option(SDL_X11                 "Use X11 video driver" ${UNIX_SYS})
@@ -2909,7 +2907,6 @@ if(HAVE_VULKAN AND NOT SDL_LOADSO)
 endif()
 
 # Platform-independent options
-CheckLibSampleRate()
 
 if(SDL_VIDEO)
   if(SDL_OFFSCREEN AND SDL_VIDEO_OPENGL_EGL)
diff --git a/build-scripts/gen_audio_channel_conversion.c b/build-scripts/gen_audio_channel_conversion.c
index 18304d51eb24..1056593b6d7b 100644
--- a/build-scripts/gen_audio_channel_conversion.c
+++ b/build-scripts/gen_audio_channel_conversion.c
@@ -261,27 +261,43 @@ static void write_converter(const int fromchans, const int tochans)
         }
     }
 
-    printf("static void SDLCALL\n"
-           "SDL_Convert%sTo%s(SDL_AudioCVT *cvt, SDL_AudioFormat format)\n"
-           "{\n", remove_dots(fromstr), remove_dots(tostr));
-
-    if (convert_backwards) {  /* must convert backwards when growing the output in-place. */
-        printf("    float *dst = ((float *) (cvt->buf + ((cvt->len_cvt / %d) * %d))) - %d;\n", fromchans, tochans, tochans);
-        printf("    const float *src = ((const float *) (cvt->buf + cvt->len_cvt)) - %d;\n", fromchans);
-    } else {
-        printf("    float *dst = (float *) cvt->buf;\n");
-        printf("    const float *src = dst;\n");
-    }
+    printf("static void SDL_Convert%sTo%s(float *dst, const float *src, int num_frames)\n{\n", remove_dots(fromstr), remove_dots(tostr));
 
     printf("    int i;\n"
            "\n"
-           "    LOG_DEBUG_CONVERT(\"%s\", \"%s\");\n"
-           "    SDL_assert(format == AUDIO_F32SYS);\n"
+           "    LOG_DEBUG_AUDIO_CONVERT(\"%s\", \"%s\");\n"
            "\n", lowercase(fromstr), lowercase(tostr));
 
-    if (convert_backwards) {
+    if (convert_backwards) {  /* must convert backwards when growing the output in-place. */
         printf("    /* convert backwards, since output is growing in-place. */\n");
-        printf("    for (i = cvt->len_cvt / (sizeof (float) * %d); i; i--, src -= %d, dst -= %d) {\n", fromchans, fromchans, tochans);
+        printf("    src += (num_frames-1)");
+        if (fromchans != 1) {
+            printf(" * %d", fromchans);
+        }
+        printf(";\n");
+
+        printf("    dst += (num_frames-1)");
+        if (tochans != 1) {
+            printf(" * %d", tochans);
+        }
+        printf(";\n");
+        printf("    for (i = num_frames");
+        if (fromchans > 1) {
+            printf(" * %d", fromchans);
+        }
+        printf("; i; i--, ");
+        if (fromchans == 1) {
+            printf("src--");
+        } else {
+            printf("src -= %d", fromchans);
+        }
+        printf(", ");
+        if (tochans == 1) {
+            printf("dst--");
+        } else {
+            printf("dst -= %d", tochans);
+        }
+        printf(") {\n");
         fptr = cvtmatrix;
         for (i = 0; i < fromchans; i++) {
             if (input_channel_used[i] > 1) {  /* don't read it from src more than once. */
@@ -326,7 +342,19 @@ static void write_converter(const int fromchans, const int tochans)
 
         printf("    }\n");
     } else {
-        printf("    for (i = cvt->len_cvt / (sizeof (float) * %d); i; i--, src += %d, dst += %d) {\n", fromchans, fromchans, tochans);
+        printf("    for (i = num_frames * %d; i; i--, ", fromchans);
+        if (fromchans == 1) {
+            printf("src++");
+        } else {
+            printf("src += %d", fromchans);
+        }
+        printf(", ");
+        if (tochans == 1) {
+            printf("dst++");
+        } else {
+            printf("dst += %d", tochans);
+        }
+        printf(") {\n");
 
         fptr = cvtmatrix;
         for (i = 0; i < fromchans; i++) {
@@ -372,20 +400,7 @@ static void write_converter(const int fromchans, const int tochans)
         printf("    }\n");
     }
 
-    printf("\n");
-
-    if ((fromchans > 1) && (tochans > 1)) {
-        printf("    cvt->len_cvt = (cvt->len_cvt / %d) * %d;\n", fromchans, tochans);
-    } else if (tochans == 1) {
-        printf("    cvt->len_cvt = cvt->len_cvt / %d;\n", fromchans);
-    } else /* if (fromchans == 1) */ {
-        printf("    cvt->len_cvt = cvt->len_cvt * %d;\n", tochans);
-    }
-
-    printf("    if (cvt->filters[++cvt->filter_index]) {\n"
-           "        cvt->filters[cvt->filter_index] (cvt, format);\n"
-           "    }\n"
-           "}\n\n");
+    printf("\n}\n\n");
 }
 
 int main(void)
@@ -416,6 +431,9 @@ int main(void)
         "\n"
         "/* DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_channel_conversion.c */\n"
         "\n"
+        "\n"
+        "typedef void (*SDL_AudioChannelConverter)(float *dst, const float *src, int num_frames);\n"
+        "\n"
     );
 
     for (ini = 1; ini <= NUM_CHANNELS; ini++) {
@@ -424,7 +442,7 @@ int main(void)
         }
     }
 
-    printf("static const SDL_AudioFilter channel_converters[%d][%d] = {   /* [from][to] */\n", NUM_CHANNELS, NUM_CHANNELS);
+    printf("static const SDL_AudioChannelConverter channel_converters[%d][%d] = {   /* [from][to] */\n", NUM_CHANNELS, NUM_CHANNELS);
     for (ini = 1; ini <= NUM_CHANNELS; ini++) {
         const char *comma = "";
         printf("    {");
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 667221710a09..bab63865874b 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -240,53 +240,6 @@ macro(CheckSNDIO)
   endif()
 endmacro()
 
-# Requires:
-# - SDL_LIBSAMPLERATE
-# Optional:
-# - SDL_LIBSAMPLERATE_SHARED opt
-# - HAVE_SDL_LOADSO opt
-macro(CheckLibSampleRate)
-  if(SDL_LIBSAMPLERATE)
-    find_package(SampleRate QUIET)
-    if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
-      set(HAVE_LIBSAMPLERATE TRUE)
-      if(SDL_LIBSAMPLERATE_SHARED)
-        target_include_directories(sdl-build-options INTERFACE $<TARGET_PROPERTY:SampleRate::samplerate,INTERFACE_INCLUDE_DIRECTORIES>)
-        if(NOT HAVE_SDL_LOADSO)
-          message_warn("You must have SDL_LoadObject() support for dynamic libsamplerate loading")
-        else()
-          get_property(_samplerate_type TARGET SampleRate::samplerate PROPERTY TYPE)
-          if(_samplerate_type STREQUAL "SHARED_LIBRARY")
-            set(HAVE_LIBSAMPLERATE_SHARED TRUE)
-            if(WIN32)
-              set(SDL_LIBSAMPLERATE_DYNAMIC "\"$<TARGET_FILE_NAME:SampleRate::samplerate>\"")
-            else()
-              set(SDL_LIBSAMPLERATE_DYNAMIC "\"$<TARGET_SONAME_FILE_NAME:SampleRate::samplerate>\"")
-            endif()
-          endif()
-        endif()
-      else()
-        target_link_libraries(sdl-build-options INTERFACE SampleRate::samplerate)
-      endif()
-    else()
-      check_include_file(samplerate.h HAVE_LIBSAMPLERATE_H)
-      if(HAVE_LIBSAMPLERATE_H)
-        set(HAVE_LIBSAMPLERATE TRUE)
-        if(SDL_LIBSAMPLERATE_SHARED AND NOT HAVE_SDL_LOADSO)
-          message_warn("You must have SDL_LoadObject() support for dynamic libsamplerate loading")
-        endif()
-        FindLibraryAndSONAME("samplerate")
-        if(SDL_LIBSAMPLERATE_SHARED AND SAMPLERATE_LIB AND HAVE_SDL_LOADSO)
-          set(SDL_LIBSAMPLERATE_DYNAMIC "\"${SAMPLERATE_LIB_SONAME}\"")
-          set(HAVE_LIBSAMPLERATE_SHARED TRUE)
-        else()
-          list(APPEND SDL_EXTRA_LIBS samplerate)
-        endif()
-      endif()
-    endif()
-  endif()
-endmacro()
-
 # Requires:
 # - n/a
 # Optional:
diff --git a/docs/README-linux.md b/docs/README-linux.md
index ea354e95f630..768a712525d9 100644
--- a/docs/README-linux.md
+++ b/docs/README-linux.md
@@ -16,7 +16,7 @@ Ubuntu 18.04, all available features enabled:
 
     sudo apt-get install build-essential git make \
     pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
-    libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev \
+    libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
     libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev \
     libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
     libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
@@ -32,15 +32,11 @@ Fedora 35, all available features enabled:
     systemd-devel mesa-libGL-devel libxkbcommon-devel mesa-libGLES-devel \
     mesa-libEGL-devel vulkan-devel wayland-devel wayland-protocols-devel \
     libdrm-devel mesa-libgbm-devel libusb-devel libdecor-devel \
-    libsamplerate-devel pipewire-jack-audio-connection-kit-devel \
+    pipewire-jack-audio-connection-kit-devel \
 
 NOTES:
 - The sndio audio target is unavailable on Fedora (but probably not what you
   should want to use anyhow).
-- libsamplerate0-dev lets SDL optionally link to libresamplerate at runtime
-  for higher-quality audio resampling. SDL will work without it if the library
-  is missing, so it's safe to build in support even if the end user doesn't
-  have this library installed.
 
 
 Joystick does not work
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 07824f1a0aa8..736869ff7add 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -103,6 +103,9 @@ If you need to convert U16 audio data to a still-supported format at runtime, th
     }

+In SDL2, SDL_AudioStream would convert/resample audio data during input (via SDL_AudioStreamPut). In SDL3, it does this work when requesting audio (via SDL_GetAudioStreamData, which would have been SDL_AudioStreamPut in SDL2. The way you use an AudioStream is roughly the same, just be aware that the workload moved to a different phase.
+In SDL2, SDL_AudioStreamAvailable() returns 0 if passed a NULL stream. In SDL3, the equivalent SDL_GetAudioStreamAvailable() call returns -1 and sets an error string, which matches other audiostream APIs’ behavior.
+

The following functions have been renamed:

  • SDL_AudioStreamAvailable() => SDL_GetAudioStreamAvailable()
    diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h
    index 1596815bab1d…cad7afc2c0cb 100644
    — a/include/SDL3/SDL_audio.h
    +++ b/include/SDL3/SDL_audio.h
    @@ -682,15 +682,15 @@ extern DECLSPEC SDL_AudioSpec *SDLCALL SDL_LoadWAV_RW(SDL_RWops * src,
    SDL_LoadWAV_RW(SDL_RWFromFile(file, “rb”),1, spec,audio_buf,audio_len)

-/* SDL_AudioStream is a new audio conversion interface.

  • The benefits vs SDL_AudioCVT:
    • it can handle resampling data in chunks without generating
      +/* SDL_AudioStream is an audio conversion interface.
    • It can handle resampling data in chunks without generating
      artifacts, when it doesn’t have the complete buffer available.
    • it can handle incoming data in any variable size.
    • It can handle incoming data in any variable size.
    • You push data as you have it, and pull it when you need it
    • It can also function as a basic audio data queue even if you
  •  just have sound that needs to pass from one place to another.
    
    /
    -/
    this is opaque to the outside world. /
    -struct SDL_AudioStream;
    +struct SDL_AudioStream; /
    this is opaque to the outside world. */
    typedef struct SDL_AudioStream SDL_AudioStream;

/**
@@ -704,6 +704,8 @@ typedef struct SDL_AudioStream SDL_AudioStream;

  • \param dst_rate The sampling rate of the desired audio output
  • \returns 0 on success, or -1 on error.
    • \threadsafety It is safe to call this function from any thread.
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_PutAudioStreamData
      @@ -711,18 +713,87 @@ typedef struct SDL_AudioStream SDL_AudioStream;
    • \sa SDL_GetAudioStreamAvailable
    • \sa SDL_FlushAudioStream
    • \sa SDL_ClearAudioStream
    • \sa SDL_ChangeAudioStreamOutput
    • \sa SDL_DestroyAudioStream
      */
      extern DECLSPEC SDL_AudioStream *SDLCALL SDL_CreateAudioStream(SDL_AudioFormat src_format,
  •                                                        Uint8 src_channels,
    
  •                                                        int src_channels,
                                                           int src_rate,
                                                           SDL_AudioFormat dst_format,
    
  •                                                        Uint8 dst_channels,
    
  •                                                        int dst_channels,
                                                           int dst_rate);
    

+/**

    • Query the current format of an audio stream.
    • \param stream the SDL_AudioStream to query.
    • \param src_format Where to store the input audio format; ignored if NULL.
    • \param src_channels Where to store the input channel count; ignored if NULL.
    • \param src_rate Where to store the input sample rate; ignored if NULL.
    • \param dst_format Where to store the output audio format; ignored if NULL.
    • \param dst_channels Where to store the output channel count; ignored if NULL.
    • \param dst_rate Where to store the output sample rate; ignored if NULL.
    • \returns 0 on success, or -1 on error.
    • \threadsafety It is safe to call this function from any thread, as it
    •           holds a stream-specific mutex while running.
      
    • \since This function is available since SDL 3.0.0.
  • */
    +extern DECLSPEC int SDLCALL SDL_GetAudioStreamFormat(SDL_AudioStream *stream,
  •                                                 SDL_AudioFormat *src_format,
    
  •                                                 int *src_channels,
    
  •                                                 int *src_rate,
    
  •                                                 SDL_AudioFormat *dst_format,
    
  •                                                 int *dst_channels,
    
  •                                                 int *dst_rate);
    

+/**

    • Change the input and output formats of an audio stream.
    • Future calls to and SDL_GetAudioStreamAvailable and SDL_GetAudioStreamData
    • will reflect the new format, and future calls to SDL_PutAudioStreamData
    • must provide data in the new input formats.
    • \param src_format The format of the audio input
    • \param src_channels The number of channels of the audio input
    • \param src_rate The sampling rate of the audio input
    • \param dst_format The format of the desired audio output
    • \param dst_channels The number of channels of the desired audio output
    • \param dst_rate The sampling rate of the desired audio output
    • \returns 0 on success, or -1 on error.
    • \threadsafety It is safe to call this function from any thread, as it
    •           holds a stream-specific mutex while running.
      
    • \since This function is available since SDL 3.0.0.
    • \sa SDL_GetAudioStreamFormat
    • \sa SDL_PutAudioStreamData
    • \sa SDL_GetAudioStreamData
    • \sa SDL_GetAudioStreamAvailable
  • */
    +extern DECLSPEC int SDLCALL SDL_SetAudioStreamFormat(SDL_AudioStream *stream,
  •                                                 SDL_AudioFormat src_format,
    
  •                                                 int src_channels,
    
  •                                                 int src_rate,
    
  •                                                 SDL_AudioFormat dst_format,
    
  •                                                 int dst_channels,
    
  •                                                 int dst_rate);
    

/**

  • Add data to be converted/resampled to the stream.
    • This data must match the format/channels/samplerate specified in
    • the latest call to SDL_SetAudioStreamFormat, or the format
    • specified when creating the stream if it hasn’t been changed.
    • Note that this call simply queues unconverted data for later.
    • This is different than SDL2, where data was converted during the
    • Put call and the Get call would just dequeue the
    • previously-converted data.
    • \param stream The stream the audio data is being added to
    • \param buf A pointer to the audio data to add
    • \param len The number of bytes to write to the stream
      @@ -741,7 +812,17 @@ extern DECLSPEC SDL_AudioStream *SDLCALL SDL_CreateAudioStream(SDL_AudioFormat s
      extern DECLSPEC int SDLCALL SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len);

/**

    • Get converted/resampled data from the stream
    • Get converted/resampled data from the stream.
    • The input/output data format/channels/samplerate is specified when
    • creating the stream, and can be changed after creation by calling
    • SDL_SetAudioStreamFormat.
    • Note that any conversion and resampling necessary is done during
    • this call, and SDL_PutAudioStreamData simply queues unconverted
    • data for later. This is different than SDL2, where that work was
    • done while inputting new data to the stream and requesting the
    • output just copied the converted data.
    • \param stream The stream the audio is being requested from
    • \param buf A buffer to fill with audio data
      @@ -753,6 +834,7 @@ extern DECLSPEC int SDLCALL SDL_PutAudioStreamData(SDL_AudioStream *stream, cons
    • \sa SDL_CreateAudioStream
    • \sa SDL_PutAudioStreamData
    • \sa SDL_GetAudioStreamAvailable
    • \sa SDL_SetAudioStreamFormat
    • \sa SDL_FlushAudioStream
    • \sa SDL_ClearAudioStream
    • \sa SDL_DestroyAudioStream
      @@ -766,6 +848,12 @@ extern DECLSPEC int SDLCALL SDL_GetAudioStreamData(SDL_AudioStream *stream, void
    • resample correctly, so this number might be lower than what you expect, or
    • even be zero. Add more data or flush the stream if you need the data now.
    • If the stream has so much data that it would overflow an int, the return
    • value is clamped to a maximum value, but no queued data is lost; if there
    • are gigabytes of data queued, the app might need to read some of it with
    • SDL_GetAudioStreamData before this function’s return value is no longer
    • clamped.
    • \param stream The audio stream to query
    • \returns the number of converted/resampled bytes available.

@@ -1133,6 +1221,7 @@ extern DECLSPEC void SDLCALL SDL_UnlockAudioDevice(SDL_AudioDeviceID dev);
*/
extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID dev);

+/* !!! FIXME: maybe remove this before SDL3’s API is locked down. /
/
*

  • Convert some audio data of one format to another format.

diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 1d2bf9b53a9d…70f95957bed4 100644
— a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -271,21 +271,17 @@ extern “C” {
/**

  • \brief A variable controlling speed/quality tradeoff of audio resampling.
    • to handle audio resampling. There are different resampling modes available
    • that produce different levels of quality, using more CPU.
    • SDL may be able to use different approaches to audio resampling, which
    • produce different levels of quality, using more CPU.
    • If this hint isn’t specified to a valid setting, or libsamplerate isn’t
    • available, SDL will use the default, internal resampling algorithm.
    • As of SDL 2.26, SDL_ConvertAudio() respects this hint when libsamplerate is available.
    • If this hint isn’t specified to a valid setting SDL will use the default.
    • This hint is currently only checked at audio subsystem initialization.
    • This variable can be set to the following values:
    • “0” or “default” - Use SDL’s internal resampling (Default when not set - low quality, fast)
    • “1” or “fast” - Use fast, slightly higher quality resampling, if available
    • “0” or “default” - SDL chooses default (probably “medium”).
    • “1” or “fast” - Use fast, lower-quality resampling, if available
    • “2” or “medium” - Use medium quality resampling, if available
    • “3” or “best” - Use high quality resampling, if available
      */
      diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
      index c91c7a20e59e…29fdae642eec 100644
      — a/include/build_config/SDL_build_config.h.cmake
      +++ b/include/build_config/SDL_build_config.h.cmake
      @@ -221,7 +221,6 @@

#cmakedefine HAVE_LINUX_INPUT_H 1
#cmakedefine HAVE_LIBUDEV_H 1
-#cmakedefine HAVE_LIBSAMPLERATE 1
#cmakedefine HAVE_LIBDECOR_H 1

#cmakedefine HAVE_D3D_H @HAVE_D3D_H@
@@ -543,9 +542,6 @@
/* Whether SDL_DYNAMIC_API needs dlopen */
#cmakedefine DYNAPI_NEEDS_DLOPEN @DYNAPI_NEEDS_DLOPEN@

-/* Enable dynamic libsamplerate support */
-#cmakedefine SDL_LIBSAMPLERATE_DYNAMIC @SDL_LIBSAMPLERATE_DYNAMIC@

/* Enable ime support */
#cmakedefine SDL_USE_IME @SDL_USE_IME@

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 1a306c675291…e38c4cea8263 100644
— a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -103,93 +103,6 @@ static const AudioBootStrap *const bootstrap[] = {
NULL
};

-#ifdef HAVE_LIBSAMPLERATE
-#ifdef SDL_LIBSAMPLERATE_DYNAMIC
-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;
-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)
-{

  • const char *hint = SDL_GetHint(SDL_HINT_AUDIO_RESAMPLING_MODE);
  • SRC_available = SDL_FALSE;
  • SRC_converter = 0;
  • if (!hint || *hint == ‘0’ || SDL_strcasecmp(hint, “default”) == 0) {
  •    return SDL_FALSE; /* don't load anything. */
    
  • } else if (*hint == ‘1’ || SDL_strcasecmp(hint, “fast”) == 0) {
  •    SRC_converter = SRC_SINC_FASTEST;
    
  • } else if (*hint == ‘2’ || SDL_strcasecmp(hint, “medium”) == 0) {
  •    SRC_converter = SRC_SINC_MEDIUM_QUALITY;
    
  • } else if (*hint == ‘3’ || SDL_strcasecmp(hint, “best”) == 0) {
  •    SRC_converter = SRC_SINC_BEST_QUALITY;
    
  • } else if (*hint == ‘4’ || SDL_strcasecmp(hint, “linear”) == 0) {
  •    SRC_converter = SRC_LINEAR;
    
  • } else {
  •    return SDL_FALSE; /* treat it like "default", don't load anything. */
    
  • }

-#ifdef SDL_LIBSAMPLERATE_DYNAMIC

  • SDL_assert(SRC_lib == NULL);
  • SRC_lib = SDL_LoadObject(SDL_LIBSAMPLERATE_DYNAMIC);
  • if (!SRC_lib) {
  •    SDL_ClearError();
    
  •    return SDL_FALSE;
    
  • }
  • /* INDENT-OFF / / clang-format off */
  • SRC_src_new = (SRC_STATE* (*)(int converter_type, int channels, int *error))SDL_LoadFunction(SRC_lib, “src_new”);
  • SRC_src_process = (int (*)(SRC_STATE *state, SRC_DATA *data))SDL_LoadFunction(SRC_lib, “src_process”);
  • 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”);
    -/
    INDENT-ON / / clang-format on */
  • 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;
    
  • }
    -#else
  • SRC_src_new = src_new;
  • SRC_src_process = src_process;
  • 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;
  • return SDL_TRUE;
    -}

-static void UnloadLibSampleRate(void)
-{
-#ifdef SDL_LIBSAMPLERATE_DYNAMIC

  • if (SRC_lib != NULL) {
  •    SDL_UnloadObject(SRC_lib);
    
  • }
  • SRC_lib = NULL;
    -#endif
  • SRC_available = SDL_FALSE;
  • SRC_src_new = NULL;
  • SRC_src_process = NULL;
  • SRC_src_reset = NULL;
  • SRC_src_delete = NULL;
  • SRC_src_strerror = NULL;
    -}
    -#endif

static SDL_AudioDevice *get_audio_device(SDL_AudioDeviceID id)
{
id–;
@@ -978,10 +891,6 @@ int SDL_InitAudio(const char driver_name)
/
Make sure we have a list of devices available at startup. */
current_audio.impl.DetectDevices();

-#ifdef HAVE_LIBSAMPLERATE

  • LoadLibSampleRate();
    -#endif
  • return 0;
    }

@@ -1608,10 +1517,6 @@ void SDL_QuitAudio(void)

 SDL_zero(current_audio);
 SDL_zeroa(open_devices);

-#ifdef HAVE_LIBSAMPLERATE

  • UnloadLibSampleRate();
    -#endif
    }

#define NUM_FORMATS 8
diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h
index d1c35b872ff7…a8a18adb3956 100644
— a/src/audio/SDL_audio_c.h
+++ b/src/audio/SDL_audio_c.h
@@ -24,30 +24,17 @@

#include “SDL_internal.h”

-#ifndef DEBUG_CONVERT
-#define DEBUG_CONVERT 0
-#endif
+#define DEBUG_AUDIOSTREAM 0
+#define DEBUG_AUDIO_CONVERT 0

-#if DEBUG_CONVERT
-#define LOG_DEBUG_CONVERT(from, to) SDL_Log(“SDL_AUDIO_CONVERT: Converting %s to %s.\n”, from, to);
+#if DEBUG_AUDIO_CONVERT
+#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log(“SDL_AUDIO_CONVERT: Converting %s to %s.\n”, from, to);
#else
-#define LOG_DEBUG_CONVERT(from, to)
+#define LOG_DEBUG_AUDIO_CONVERT(from, to)
#endif

/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */

-#ifdef HAVE_LIBSAMPLERATE
-#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);
-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 */
extern SDL_AudioFormat SDL_GetFirstAudioFormat(SDL_AudioFormat format);
extern SDL_AudioFormat SDL_GetNextAudioFormat(void);
@@ -56,21 +43,18 @@ extern SDL_AudioFormat SDL_GetNextAudioFormat(void);
extern Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format);
extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec);

-/* Choose the audio filter functions below /
+/
Must be called at least once before using converters (SDL_CreateAudioStream will call it). */
extern void SDL_ChooseAudioConverters(void);

-struct SDL_AudioCVT;
-typedef void (SDLCALL * SDL_AudioFilter) (struct SDL_AudioCVT * cvt, SDL_AudioFormat format);

/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */
-extern SDL_AudioFilter SDL_Convert_S8_to_F32;
-extern SDL_AudioFilter SDL_Convert_U8_to_F32;
-extern SDL_AudioFilter SDL_Convert_S16_to_F32;
-extern SDL_AudioFilter SDL_Convert_S32_to_F32;
-extern SDL_AudioFilter SDL_Convert_F32_to_S8;
-extern SDL_AudioFilter SDL_Convert_F32_to_U8;
-extern SDL_AudioFilter SDL_Convert_F32_to_S16;
-extern SDL_AudioFilter SDL_Convert_F32_to_S32;
+extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples);
+extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples);
+extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples);
+extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples);
+extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples);
+extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples);
+extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples);
+extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples);

/**

  • Use this function to initialize a particular audio driver.
    diff --git a/src/audio/SDL_audio_channel_converters.h b/src/audio/SDL_audio_channel_converters.h
    index afdafdbdf53c…7fa24f4e7bbb 100644
    — a/src/audio/SDL_audio_channel_converters.h
    +++ b/src/audio/SDL_audio_channel_converters.h
    @@ -21,62 +21,54 @@

/* DO NOT EDIT, THIS FILE WAS GENERATED BY build-scripts/gen_audio_channel_conversion.c */

-static void SDLCALL SDL_ConvertMonoToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+
+typedef void (*SDL_AudioChannelConverter)(float *dst, const float *src, int num_frames);
+
+static void SDL_ConvertMonoToStereo(float *dst, const float *src, int num_frames)
{

  • float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 2))) - 2;

  • const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1;
    int i;

  • LOG_DEBUG_CONVERT(“mono”, “stereo”);

  • SDL_assert(format == AUDIO_F32SYS);

  • LOG_DEBUG_AUDIO_CONVERT(“mono”, “stereo”);

    /* convert backwards, since output is growing in-place. */

  • for (i = cvt->len_cvt / (sizeof(float) * 1); i; i–, src -= 1, dst -= 2) {
  • src += (num_frames-1);
  • dst += (num_frames-1) * 2;
  • for (i = num_frames; i; i–, src–, dst -= 2) {
    const float srcFC = src[0];
    dst[1] /* FR / = srcFC;
    dst[0] /
    FL */ = srcFC;
    }
  • cvt->len_cvt = cvt->len_cvt * 2;
  • if (cvt->filters[++cvt->filter_index]) {
  •    cvt->filters[cvt->filt
    
(Patch may be truncated, please check the link at the top of this post.)