Selaa lähdekoodia

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!
Ryan C. Gordon 2 vuotta sitten
vanhempi
commit
e5a6c24c82

+ 0 - 3
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)

+ 49 - 31
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("    {");

+ 0 - 47
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:

+ 2 - 6
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

+ 3 - 0
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()

+ 98 - 9
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.
  *

+ 5 - 9
include/SDL3/SDL_hints.h

@@ -271,21 +271,17 @@ extern "C" {
 /**
  *  \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, 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
  */

+ 0 - 4
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@
 

+ 0 - 95
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

+ 14 - 30
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.

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 164 - 366
src/audio/SDL_audio_channel_converters.h


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 344 - 805
src/audio/SDL_audiocvt.c


+ 0 - 69
src/audio/SDL_audiocvt_c.h

@@ -1,69 +0,0 @@
-/*
-  Simple DirectMedia Layer
-  Copyright (C) 1997-2023 Sam Lantinga <[email protected]>
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-*/
-
-/**
- *  \brief Upper limit of filters in SDL_AudioCVT
- *
- *  The maximum number of SDL_AudioFilter functions in SDL_AudioCVT is
- *  currently limited to 9. The SDL_AudioCVT.filters array has 10 pointers,
- *  one of which is the terminating NULL pointer.
- */
-#define SDL_AUDIOCVT_MAX_FILTERS 9
-
-/**
- *  \struct SDL_AudioCVT
- *  \brief A structure to hold a set of audio conversion filters and buffers.
- *
- *  Note that various parts of the conversion pipeline can take advantage
- *  of SIMD operations (like SSE2, for example). SDL_AudioCVT doesn't require
- *  you to pass it aligned data, but can possibly run much faster if you
- *  set both its (buf) field to a pointer that is aligned to 16 bytes, and its
- *  (len) field to something that's a multiple of 16, if possible.
- */
-#if defined(__GNUC__) && !defined(__CHERI_PURE_CAPABILITY__)
-/* This structure is 84 bytes on 32-bit architectures, make sure GCC doesn't
-   pad it out to 88 bytes to guarantee ABI compatibility between compilers.
-   This is not a concern on CHERI architectures, where pointers must be stored
-   at aligned locations otherwise they will become invalid, and thus structs
-   containing pointers cannot be packed without giving a warning or error.
-   vvv
-   The next time we rev the ABI, make sure to size the ints and add padding.
-*/
-#define SDL_AUDIOCVT_PACKED __attribute__((packed))
-#else
-#define SDL_AUDIOCVT_PACKED
-#endif
-/* */
-typedef struct SDL_AudioCVT
-{
-    int needed;                 /**< Set to 1 if conversion possible */
-    SDL_AudioFormat src_format; /**< Source audio format */
-    SDL_AudioFormat dst_format; /**< Target audio format */
-    double rate_incr;           /**< Rate conversion increment */
-    Uint8 *buf;                 /**< Buffer to hold entire audio data */
-    int len;                    /**< Length of original audio buffer */
-    int len_cvt;                /**< Length of converted audio buffer */
-    int len_mult;               /**< buffer must be len*len_mult big */
-    double len_ratio;           /**< Given len, final size is len*len_ratio */
-    SDL_AudioFilter filters[SDL_AUDIOCVT_MAX_FILTERS + 1]; /**< NULL-terminated list of filter functions */
-    int filter_index;           /**< Current audio conversion function */
-} SDL_AUDIOCVT_PACKED SDL_AudioCVT;
-

+ 122 - 343
src/audio/SDL_audiotypecvt.c

@@ -21,7 +21,6 @@
 #include "SDL_internal.h"
 
 #include "SDL_audio_c.h"
-#include "SDL_audiocvt_c.h"
 
 #ifndef SDL_CPUINFO_DISABLED
 #if defined(__x86_64__) && defined(SDL_SSE2_INTRINSICS)
@@ -33,210 +32,72 @@
 #elif defined(__APPLE__) && defined(__ARM_ARCH) && (__ARM_ARCH >= 7) && defined(SDL_NEON_INTRINSICS)
 #define NEED_SCALAR_CONVERTER_FALLBACKS 0 /* All Apple ARMv7 chips promise NEON support. */
 #endif
-#endif /* !SDL_CPUINFO_DISABLED */
+#endif
 
 /* Set to zero if platform is guaranteed to use a SIMD codepath here. */
 #if !defined(NEED_SCALAR_CONVERTER_FALLBACKS) || defined(SDL_CPUINFO_DISABLED)
 #define NEED_SCALAR_CONVERTER_FALLBACKS 1
 #endif
 
-/* Function pointers set to a CPU-specific implementation. */
-SDL_AudioFilter SDL_Convert_S8_to_F32 = NULL;
-SDL_AudioFilter SDL_Convert_U8_to_F32 = NULL;
-SDL_AudioFilter SDL_Convert_S16_to_F32 = NULL;
-SDL_AudioFilter SDL_Convert_S32_to_F32 = NULL;
-SDL_AudioFilter SDL_Convert_F32_to_S8 = NULL;
-SDL_AudioFilter SDL_Convert_F32_to_U8 = NULL;
-SDL_AudioFilter SDL_Convert_F32_to_S16 = NULL;
-SDL_AudioFilter SDL_Convert_F32_to_S32 = NULL;
-
 #define DIVBY128     0.0078125f
 #define DIVBY32768   0.000030517578125f
 #define DIVBY8388607 0.00000011920930376163766f
 
 #if NEED_SCALAR_CONVERTER_FALLBACKS
-static void SDLCALL SDL_Convert_S8_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const Sint8 *src = ((const Sint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32");
-
-    for (i = cvt->len_cvt; i; --i, --src, --dst) {
-        *dst = ((float)*src) * DIVBY128;
-    }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
-}
-
-static void SDLCALL SDL_Convert_U8_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const Uint8 *src = ((const Uint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32");
-
-    for (i = cvt->len_cvt; i; --i, --src, --dst) {
-        *dst = (((float)*src) * DIVBY128) - 1.0f;
-    }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
-}
-
-static void SDLCALL SDL_Convert_S16_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const Sint16 *src = ((const Sint16 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 2)) - 1;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32");
-
-    for (i = cvt->len_cvt / sizeof(Sint16); i; --i, --src, --dst) {
-        *dst = ((float)*src) * DIVBY32768;
-    }
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
-}
-
-static void SDLCALL SDL_Convert_S32_to_F32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const Sint32 *src = (const Sint32 *)cvt->buf;
-    float *dst = (float *)cvt->buf;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32");
-
-    for (i = cvt->len_cvt / sizeof(Sint32); i; --i, ++src, ++dst) {
-        *dst = ((float)(*src >> 8)) * DIVBY8388607;
-    }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
-}
-
-static void SDLCALL SDL_Convert_F32_to_S8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const float *src = (const float *)cvt->buf;
-    Sint8 *dst = (Sint8 *)cvt->buf;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8");
-
-    for (i = cvt->len_cvt / sizeof(float); i; --i, ++src, ++dst) {
-        const float sample = *src;
-        if (sample >= 1.0f) {
-            *dst = 127;
-        } else if (sample <= -1.0f) {
-            *dst = -128;
-        } else {
-            *dst = (Sint8)(sample * 127.0f);
-        }
-    }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S8);
-    }
-}
-
-static void SDLCALL SDL_Convert_F32_to_U8_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const float *src = (const float *)cvt->buf;
-    Uint8 *dst = (Uint8 *)cvt->buf;
-    int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8");
-
-    for (i = cvt->len_cvt / sizeof(float); i; --i, ++src, ++dst) {
-        const float sample = *src;
-        if (sample >= 1.0f) {
-            *dst = 255;
-        } else if (sample <= -1.0f) {
-            *dst = 0;
-        } else {
-            *dst = (Uint8)((sample + 1.0f) * 127.0f);
-        }
-    }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_U8);
-    }
-}
-
-static void SDLCALL SDL_Convert_F32_to_S16_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const float *src = (const float *)cvt->buf;
-    Sint16 *dst = (Sint16 *)cvt->buf;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16");
-
-    for (i = cvt->len_cvt / sizeof(float); i; --i, ++src, ++dst) {
-        const float sample = *src;
-        if (sample >= 1.0f) {
-            *dst = 32767;
-        } else if (sample <= -1.0f) {
-            *dst = -32768;
-        } else {
-            *dst = (Sint16)(sample * 32767.0f);
-        }
-    }
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS);
-    }
-}
-
-static void SDLCALL SDL_Convert_F32_to_S32_Scalar(SDL_AudioCVT *cvt, SDL_AudioFormat format)
-{
-    const float *src = (const float *)cvt->buf;
-    Sint32 *dst = (Sint32 *)cvt->buf;
-    int i;
-
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32");
-
-    for (i = cvt->len_cvt / sizeof(float); i; --i, ++src, ++dst) {
-        const float sample = *src;
-        if (sample >= 1.0f) {
-            *dst = 2147483647;
-        } else if (sample <= -1.0f) {
-            *dst = (Sint32)-2147483648LL;
-        } else {
-            *dst = ((Sint32)(sample * 8388607.0f)) << 8;
-        }
-    }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS);
-    }
-}
-#endif
+/* these all convert backwards because (currently) float32 is >= to the size of anything it converts to, so it lets us safely convert in-place. */
+#define AUDIOCVT_TOFLOAT_SCALAR(from, fromtype, equation) \
+    static void SDL_Convert_##from##_to_F32_Scalar(float *dst, const fromtype *src, int num_samples) { \
+        int i; \
+        LOG_DEBUG_AUDIO_CONVERT("AUDIO_" #from, "AUDIO_F32"); \
+        for (i = num_samples - 1; i >= 0; --i) { \
+            dst[i] = equation; \
+        } \
+    }
+
+AUDIOCVT_TOFLOAT_SCALAR(S8, Sint8, ((float)src[i]) * DIVBY128)
+AUDIOCVT_TOFLOAT_SCALAR(U8, Uint8, (((float)src[i]) * DIVBY128) - 1.0f)
+AUDIOCVT_TOFLOAT_SCALAR(S16, Sint16, ((float)src[i]) * DIVBY32768)
+AUDIOCVT_TOFLOAT_SCALAR(S32, Sint32, ((float)(src[i] >> 8)) * DIVBY8388607)
+#undef AUDIOCVT_FROMFLOAT_SCALAR
+
+/* these all convert forwards because (currently) float32 is >= to the size of anything it converts from, so it lets us safely convert in-place. */
+#define AUDIOCVT_FROMFLOAT_SCALAR(to, totype, clampmin, clampmax, equation) \
+    static void SDL_Convert_F32_to_##to##_Scalar(totype *dst, const float *src, int num_samples) { \
+        int i; \
+        LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_" #to); \
+        for (i = 0; i < num_samples; i++) { \
+            const float sample = src[i]; \
+            if (sample >= 1.0f) { \
+                dst[i] = (totype) (clampmax); \
+            } else if (sample <= -1.0f) { \
+                dst[i] = (totype) (clampmin); \
+            } else { \
+                dst[i] = (totype) (equation); \
+            } \
+        } \
+    }
+
+AUDIOCVT_FROMFLOAT_SCALAR(S8, Sint8, -128, 127, sample * 127.0f);
+AUDIOCVT_FROMFLOAT_SCALAR(U8, Uint8, 0, 255, (sample + 1.0f) * 127.0f);
+AUDIOCVT_FROMFLOAT_SCALAR(S16, Sint16, -32768, 32767, sample * 32767.0f);
+AUDIOCVT_FROMFLOAT_SCALAR(S32, Sint32, -2147483648LL, 2147483647, ((Sint32)(sample * 8388607.0f)) << 8);
+#undef AUDIOCVT_FROMFLOAT_SCALAR
+
+#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
 
 #ifdef SDL_SSE2_INTRINSICS
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples)
 {
-    const Sint8 *src = ((const Sint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S8", "AUDIO_F32 (using SSE2)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
         *dst = ((float)*src) * DIVBY128;
     }
 
@@ -284,23 +145,19 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(SDL_AudioCV
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples)
 {
-    const Uint8 *src = ((const Uint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_U8", "AUDIO_F32 (using SSE2)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
         *dst = (((float)*src) * DIVBY128) - 1.0f;
     }
 
@@ -350,23 +207,19 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(SDL_AudioCV
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples)
 {
-    const Sint16 *src = ((const Sint16 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 2)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S16", "AUDIO_F32 (using SSE2)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt / sizeof(Sint16); i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
         *dst = ((float)*src) * DIVBY32768;
     }
 
@@ -403,23 +256,16 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(SDL_AudioC
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples)
 {
-    const Sint32 *src = (const Sint32 *)cvt->buf;
-    float *dst = (float *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S32", "AUDIO_F32 (using SSE2)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(Sint32); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         *dst = ((float)(*src >> 8)) * DIVBY8388607;
     }
 
@@ -447,22 +293,16 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(SDL_AudioC
         src++;
         dst++;
     }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint8 *dst = (Sint8 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S8 (using SSE2)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 127;
@@ -509,23 +349,16 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(SDL_AudioCV
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S8);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Uint8 *dst = cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_U8 (using SSE2)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 255;
@@ -572,23 +405,16 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(SDL_AudioCV
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_U8);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint16 *dst = (Sint16 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S16 (using SSE2)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 32767;
@@ -633,23 +459,16 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(SDL_AudioC
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS);
-    }
 }
 
-static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint32 *dst = (Sint32 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32 (using SSE2)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S32 (using SSE2)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 2147483647;
@@ -692,24 +511,21 @@ static void SDLCALL SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(SDL_AudioC
         src++;
         dst++;
     }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS);
-    }
 }
 #endif
 
 #ifdef SDL_NEON_INTRINSICS
-static void SDLCALL SDL_Convert_S8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples)
 {
-    const Sint8 *src = ((const Sint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S8", "AUDIO_F32 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S8", "AUDIO_F32 (using NEON)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
         *dst = ((float)*src) * DIVBY128;
     }
 
@@ -749,23 +565,19 @@ static void SDLCALL SDL_Convert_S8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioForma
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_Convert_U8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_U8_to_F32_NEON(float *dst, const Uint8 *src, int num_samples)
 {
-    const Uint8 *src = ((const Uint8 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 4)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_U8", "AUDIO_F32 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_U8", "AUDIO_F32 (using NEON)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
         *dst = (((float)*src) * DIVBY128) - 1.0f;
     }
 
@@ -806,23 +618,19 @@ static void SDLCALL SDL_Convert_U8_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioForma
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_Convert_S16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_S16_to_F32_NEON(float *dst, const Sint16 *src, int num_samples)
 {
-    const Sint16 *src = ((const Sint16 *)(cvt->buf + cvt->len_cvt)) - 1;
-    float *dst = ((float *)(cvt->buf + cvt->len_cvt * 2)) - 1;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S16", "AUDIO_F32 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S16", "AUDIO_F32 (using NEON)");
+
+    src += num_samples - 1;
+    dst += num_samples - 1;
 
     /* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
-    for (i = cvt->len_cvt / sizeof(Sint16); i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
+    for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
         *dst = ((float)*src) * DIVBY32768;
     }
 
@@ -855,23 +663,16 @@ static void SDLCALL SDL_Convert_S16_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioForm
         src--;
         dst--;
     }
-
-    cvt->len_cvt *= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_Convert_S32_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_S32_to_F32_NEON(float *dst, const Sint32 *src, int num_samples)
 {
-    const Sint32 *src = (const Sint32 *)cvt->buf;
-    float *dst = (float *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_S32", "AUDIO_F32 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_S32", "AUDIO_F32 (using NEON)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(Sint32); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         *dst = ((float)(*src >> 8)) * DIVBY8388607;
     }
 
@@ -899,22 +700,16 @@ static void SDLCALL SDL_Convert_S32_to_F32_NEON(SDL_AudioCVT *cvt, SDL_AudioForm
         src++;
         dst++;
     }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS);
-    }
 }
 
-static void SDLCALL SDL_Convert_F32_to_S8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_F32_to_S8_NEON(Sint8 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint8 *dst = (Sint8 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S8 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S8 (using NEON)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 127;
@@ -963,23 +758,16 @@ static void SDLCALL SDL_Convert_F32_to_S8_NEON(SDL_AudioCVT *cvt, SDL_AudioForma
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S8);
-    }
 }
 
-static void SDLCALL SDL_Convert_F32_to_U8_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_F32_to_U8_NEON(Uint8 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Uint8 *dst = cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_U8 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_U8 (using NEON)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 255;
@@ -1029,23 +817,16 @@ static void SDLCALL SDL_Convert_F32_to_U8_NEON(SDL_AudioCVT *cvt, SDL_AudioForma
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 4;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_U8);
-    }
 }
 
-static void SDLCALL SDL_Convert_F32_to_S16_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_F32_to_S16_NEON(Sint16 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint16 *dst = (Sint16 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S16 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S16 (using NEON)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 32767;
@@ -1090,23 +871,16 @@ static void SDLCALL SDL_Convert_F32_to_S16_NEON(SDL_AudioCVT *cvt, SDL_AudioForm
         src++;
         dst++;
     }
-
-    cvt->len_cvt /= 2;
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S16SYS);
-    }
 }
 
-static void SDLCALL SDL_Convert_F32_to_S32_NEON(SDL_AudioCVT *cvt, SDL_AudioFormat format)
+static void SDL_Convert_F32_to_S32_NEON(Sint32 *dst, const float *src, int num_samples)
 {
-    const float *src = (const float *)cvt->buf;
-    Sint32 *dst = (Sint32 *)cvt->buf;
     int i;
 
-    LOG_DEBUG_CONVERT("AUDIO_F32", "AUDIO_S32 (using NEON)");
+    LOG_DEBUG_AUDIO_CONVERT("AUDIO_F32", "AUDIO_S32 (using NEON)");
 
     /* Get dst aligned to 16 bytes */
-    for (i = cvt->len_cvt / sizeof(float); i && (((size_t)dst) & 15); --i, ++src, ++dst) {
+    for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
         const float sample = *src;
         if (sample >= 1.0f) {
             *dst = 2147483647;
@@ -1149,28 +923,33 @@ static void SDLCALL SDL_Convert_F32_to_S32_NEON(SDL_AudioCVT *cvt, SDL_AudioForm
         src++;
         dst++;
     }
-
-    if (cvt->filters[++cvt->filter_index]) {
-        cvt->filters[cvt->filter_index](cvt, AUDIO_S32SYS);
-    }
 }
 #endif
 
+/* Function pointers set to a CPU-specific implementation. */
+void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples) = NULL;
+void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples) = NULL;
+void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples) = NULL;
+void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples) = NULL;
+void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples) = NULL;
+void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = NULL;
+void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
+void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
+
 void SDL_ChooseAudioConverters(void)
 {
     static SDL_bool converters_chosen = SDL_FALSE;
-
     if (converters_chosen) {
         return;
     }
 
-#define SET_CONVERTER_FUNCS(fntype)                           \
-    SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype;   \
-    SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype;   \
+#define SET_CONVERTER_FUNCS(fntype) \
+    SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
+    SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \
     SDL_Convert_S16_to_F32 = SDL_Convert_S16_to_F32_##fntype; \
     SDL_Convert_S32_to_F32 = SDL_Convert_S32_to_F32_##fntype; \
-    SDL_Convert_F32_to_S8 = SDL_Convert_F32_to_S8_##fntype;   \
-    SDL_Convert_F32_to_U8 = SDL_Convert_F32_to_U8_##fntype;   \
+    SDL_Convert_F32_to_S8 = SDL_Convert_F32_to_S8_##fntype; \
+    SDL_Convert_F32_to_U8 = SDL_Convert_F32_to_U8_##fntype; \
     SDL_Convert_F32_to_S16 = SDL_Convert_F32_to_S16_##fntype; \
     SDL_Convert_F32_to_S32 = SDL_Convert_F32_to_S32_##fntype; \
     converters_chosen = SDL_TRUE

+ 3 - 0
src/audio/SDL_mixer.c

@@ -81,6 +81,9 @@ static const Uint8 mix8[] = {
 #define ADJUST_VOLUME(type, s, v) ((s) = (type)(((s) * (v)) / SDL_MIX_MAXVOLUME))
 #define ADJUST_VOLUME_U8(s, v)    ((s) = (Uint8)(((((s) - 128) * (v)) / SDL_MIX_MAXVOLUME) + 128))
 
+
+/* !!! FIXME: this needs some SIMD magic. */
+
 int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
                         Uint32 len, int volume)
 {

+ 2 - 0
src/dynapi/SDL_dynapi.sym

@@ -842,6 +842,8 @@ SDL3_0.0.0 {
     SDL_CreatePopupWindow;
     SDL_GetWindowParent;
     SDL_CreateWindowWithPosition;
+    SDL_GetAudioStreamFormat;
+    SDL_SetAudioStreamFormat;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -868,3 +868,5 @@
 #define SDL_CreatePopupWindow SDL_CreatePopupWindow_REAL
 #define SDL_GetWindowParent SDL_GetWindowParent_REAL
 #define SDL_CreateWindowWithPosition SDL_CreateWindowWithPosition_REAL
+#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
+#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL

+ 3 - 1
src/dynapi/SDL_dynapi_procs.h

@@ -156,7 +156,7 @@ SDL_DYNAPI_PROC(int,SDL_CondWaitTimeout,(SDL_cond *a, SDL_mutex *b, Sint32 c),(a
 SDL_DYNAPI_PROC(int,SDL_ConvertPixels,(int a, int b, Uint32 c, const void *d, int e, Uint32 f, void *g, int h),(a,b,c,d,e,f,g,h),return)
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurface,(SDL_Surface *a, const SDL_PixelFormat *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormat,(SDL_Surface *a, Uint32 b),(a,b),return)
-SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, Uint8 b, int c, SDL_AudioFormat d, Uint8 e, int f),(a,b,c,d,e,f),return)
+SDL_DYNAPI_PROC(SDL_AudioStream*,SDL_CreateAudioStream,(SDL_AudioFormat a, int b, int c, SDL_AudioFormat d, int e, int f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateColorCursor,(SDL_Surface *a, int b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(SDL_cond*,SDL_CreateCond,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Cursor*,SDL_CreateCursor,(const Uint8 *a, const Uint8 *b, int c, int d, int e, int f),(a,b,c,d,e,f),return)
@@ -913,3 +913,5 @@ SDL_DYNAPI_PROC(SDL_SystemTheme,SDL_GetSystemTheme,(void),(),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_GetWindowParent,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(SDL_Window*,SDL_CreateWindowWithPosition,(const char *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
+SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat *b, int *c, int *d, SDL_AudioFormat *e, int *f, int *g),(a,b,c,d,e,f,g),return)
+SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioFormat b, int c, int d, SDL_AudioFormat e, int f, int g),(a,b,c,d,e,f,g),return)

+ 1 - 0
test/CMakeLists.txt

@@ -151,6 +151,7 @@ add_sdl_test_executable(loopwavequeue NEEDS_RESOURCES TESTUTILS SOURCES loopwave
 add_sdl_test_executable(testsurround SOURCES testsurround.c)
 add_sdl_test_executable(testresample NEEDS_RESOURCES SOURCES testresample.c)
 add_sdl_test_executable(testaudioinfo SOURCES testaudioinfo.c)
+add_sdl_test_executable(testaudiostreamdynamicresample SOURCES testaudiostreamdynamicresample.c)
 
 file(GLOB TESTAUTOMATION_SOURCE_FILES testautomation*.c)
 add_sdl_test_executable(testautomation NEEDS_RESOURCES SOURCES ${TESTAUTOMATION_SOURCE_FILES})

+ 123 - 0
test/testaudiostreamdynamicresample.c

@@ -0,0 +1,123 @@
+/*
+  Copyright (C) 1997-2023 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+
+/* !!! FIXME: this code is not up to standards for SDL3 test apps. Someone should improve this. */
+
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+#include <SDL3/SDL_test.h>
+
+static void SDLCALL audio_callback(void *userdata, Uint8 * stream, int len)
+{
+    SDL_AudioStream *audiostream = (SDL_AudioStream *) userdata;
+    SDL_memset(stream, 0, len);
+    SDL_GetAudioStreamData(audiostream, stream, len);
+}
+
+int main(int argc, char *argv[])
+{
+    SDL_Window *window;
+    SDL_Renderer *renderer;
+    SDL_bool done = SDL_FALSE;
+    const SDL_FRect slider_area = { (640 - 500) / 2, (480 - 100) / 2, 500, 100 };
+    SDL_FRect slider_fill_area = slider_area;
+    int multiplier = 100;
+    SDL_AudioSpec spec;
+    Uint8 *audio_buf = NULL;
+    Uint32 audio_len = 0;
+    SDL_AudioStream *stream;
+    SDL_AudioDeviceID device;
+
+    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
+    window = SDL_CreateWindow("Drag the slider: Normal speed", 640, 480, 0);
+    renderer = SDL_CreateRenderer(window, NULL, 0);
+
+    SDL_LoadWAV("sample.wav", &spec, &audio_buf, &audio_len);
+    stream = SDL_CreateAudioStream(spec.format, spec.channels, spec.freq, spec.format, spec.channels, spec.freq);
+    SDL_PutAudioStreamData(stream, audio_buf, audio_len);
+    spec.callback = audio_callback;
+    spec.userdata = stream;
+    device = SDL_OpenAudioDevice(NULL, SDL_FALSE, &spec, NULL, 0);
+    SDL_PlayAudioDevice(device);
+
+    slider_fill_area.w /= 2;
+
+    while (!done) {
+        SDL_Event e;
+        int newmultiplier = multiplier;
+        while (SDL_PollEvent(&e)) {
+            if (e.type == SDL_EVENT_QUIT) {
+                done = 1;
+            } else if (e.type == SDL_EVENT_KEY_DOWN) {
+                if (e.key.keysym.sym == SDLK_ESCAPE) {
+                    done = 1;
+                }
+            } else if (e.type == SDL_EVENT_MOUSE_MOTION) {
+                if (e.motion.state & SDL_BUTTON_LMASK) {
+                    const SDL_FPoint p = { e.motion.x, e.motion.y };
+                    if (SDL_PointInRectFloat(&p, &slider_area)) {
+                        const float w = SDL_roundf(p.x - slider_area.x);
+                        slider_fill_area.w = w;
+                        newmultiplier = ((int) ((w / slider_area.w) * 800.0f)) - 400;
+                    }
+                }
+            }
+        }
+
+        if (multiplier != newmultiplier) {
+            char title[64];
+            int newfreq = spec.freq;
+
+            multiplier = newmultiplier;
+            if (multiplier == 0) {
+                SDL_snprintf(title, sizeof (title), "Drag the slider: Normal speed");
+            } else if (multiplier < 0) {
+                SDL_snprintf(title, sizeof (title), "Drag the slider: %.2fx slow", (-multiplier / 100.0f) + 1.0f);
+            } else {
+                SDL_snprintf(title, sizeof (title), "Drag the slider: %.2fx fast", (multiplier / 100.0f) + 1.0f);
+            }
+            SDL_SetWindowTitle(window, title);
+
+            // this math sucks, but whatever.
+            if (multiplier < 0) {
+                newfreq = spec.freq + (int) ((spec.freq * (multiplier / 400.0f)) * 0.75f);
+            } else if (multiplier > 0) {
+                newfreq = spec.freq + (int) (spec.freq * (multiplier / 100.0f));
+            }
+            //SDL_Log("newfreq=%d   multiplier=%d\n", newfreq, multiplier);
+            SDL_LockAudioDevice(device);
+            SDL_SetAudioStreamFormat(stream, spec.format, spec.channels, newfreq, spec.format, spec.channels, spec.freq);
+            SDL_UnlockAudioDevice(device);
+        }
+
+        // keep it looping.
+        if (SDL_GetAudioStreamAvailable(stream) < (1024 * 100)) {
+            SDL_PutAudioStreamData(stream, audio_buf, audio_len);
+        }
+
+        SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
+        SDL_RenderClear(renderer);
+        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+        SDL_RenderFillRect(renderer, &slider_area);
+        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+        SDL_RenderFillRect(renderer, &slider_fill_area);
+        SDL_RenderPresent(renderer);
+    }
+
+    SDL_DestroyRenderer(renderer);
+    SDL_DestroyWindow(window);
+    SDL_CloseAudioDevice(device);
+    SDL_free(audio_buf);
+    SDL_Quit();
+    return 0;
+}
+

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä