From e5a6c24c822e11c9a99f10adad49f904b8170562 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" 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 | 1935 +++++++---------- src/audio/SDL_audiocvt_c.h | 69 - src/audio/SDL_audiotypecvt.c | 453 +--- 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, 1434 insertions(+), 2369 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 6cca05ba9..407f7e46a 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 18304d51e..1056593b6 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 667221710..bab638658 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 $) - 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 "\"$\"") - else() - set(SDL_LIBSAMPLERATE_DYNAMIC "\"$\"") - 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 ea354e95f..768a71252 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 07824f1a0..736869ff7 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 1596815ba..cad7afc2c 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 1d2bf9b53..70f95957b 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. * - * 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 */ diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index c91c7a20e..29fdae642 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 1a306c675..e38c4cea8 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 d1c35b872..a8a18adb3 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 afdafdbdf..7fa24f4e7 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->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoTo21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoTo21(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 3))) - 3; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "2.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 3) { + src += (num_frames-1); + dst += (num_frames-1) * 3; + for (i = num_frames; i; i--, src--, dst -= 3) { const float srcFC = src[0]; dst[2] /* LFE */ = 0.0f; dst[1] /* FR */ = srcFC; dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoToQuad(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 4))) - 4; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "quad"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 4) { + src += (num_frames-1); + dst += (num_frames-1) * 4; + for (i = num_frames; i; i--, src--, dst -= 4) { const float srcFC = src[0]; dst[3] /* BR */ = 0.0f; dst[2] /* BL */ = 0.0f; @@ -84,23 +76,18 @@ static void SDLCALL SDL_ConvertMonoToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoTo41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoTo41(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 5))) - 5; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "4.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 5) { + src += (num_frames-1); + dst += (num_frames-1) * 5; + for (i = num_frames; i; i--, src--, dst -= 5) { const float srcFC = src[0]; dst[4] /* BR */ = 0.0f; dst[3] /* BL */ = 0.0f; @@ -109,23 +96,18 @@ static void SDLCALL SDL_ConvertMonoTo41(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoTo51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoTo51(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 6))) - 6; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "5.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 6) { + src += (num_frames-1); + dst += (num_frames-1) * 6; + for (i = num_frames; i; i--, src--, dst -= 6) { const float srcFC = src[0]; dst[5] /* BR */ = 0.0f; dst[4] /* BL */ = 0.0f; @@ -135,23 +117,18 @@ static void SDLCALL SDL_ConvertMonoTo51(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoTo61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoTo61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 7) { + src += (num_frames-1); + dst += (num_frames-1) * 7; + for (i = num_frames; i; i--, src--, dst -= 7) { const float srcFC = src[0]; dst[6] /* SR */ = 0.0f; dst[5] /* SL */ = 0.0f; @@ -162,23 +139,18 @@ static void SDLCALL SDL_ConvertMonoTo61(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertMonoTo71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertMonoTo71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 1) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 1; int i; - LOG_DEBUG_CONVERT("mono", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("mono", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 1); i; i--, src -= 1, dst -= 8) { + src += (num_frames-1); + dst += (num_frames-1) * 8; + for (i = num_frames; i; i--, src--, dst -= 8) { const float srcFC = src[0]; dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; @@ -190,87 +162,65 @@ static void SDLCALL SDL_ConvertMonoTo71(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = srcFC; } - cvt->len_cvt = cvt->len_cvt * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("stereo", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src += 2, dst += 1) { + for (i = num_frames * 2; i; i--, src += 2, dst++) { dst[0] /* FC */ = (src[0] * 0.500000000f) + (src[1] * 0.500000000f); } - cvt->len_cvt = cvt->len_cvt / 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoTo21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoTo21(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 3))) - 3; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "2.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 3) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 3; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 3) { dst[2] /* LFE */ = 0.0f; dst[1] /* FR */ = src[1]; dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoToQuad(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 4))) - 4; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "quad"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 4) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 4; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 4) { dst[3] /* BR */ = 0.0f; dst[2] /* BL */ = 0.0f; dst[1] /* FR */ = src[1]; dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoTo41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoTo41(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 5))) - 5; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "4.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 5) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 5; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 5) { dst[4] /* BR */ = 0.0f; dst[3] /* BL */ = 0.0f; dst[2] /* LFE */ = 0.0f; @@ -278,23 +228,18 @@ static void SDLCALL SDL_ConvertStereoTo41(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoTo51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoTo51(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 6))) - 6; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "5.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 6) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 6; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 6) { dst[5] /* BR */ = 0.0f; dst[4] /* BL */ = 0.0f; dst[3] /* LFE */ = 0.0f; @@ -303,23 +248,18 @@ static void SDLCALL SDL_ConvertStereoTo51(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoTo61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoTo61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 7) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 7; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 7) { dst[6] /* SR */ = 0.0f; dst[5] /* SL */ = 0.0f; dst[4] /* BC */ = 0.0f; @@ -329,23 +269,18 @@ static void SDLCALL SDL_ConvertStereoTo61(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertStereoTo71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertStereoTo71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 2) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 2; int i; - LOG_DEBUG_CONVERT("stereo", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("stereo", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 2); i; i--, src -= 2, dst -= 8) { + src += (num_frames-1) * 2; + dst += (num_frames-1) * 8; + for (i = num_frames * 2; i; i--, src -= 2, dst -= 8) { dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; dst[5] /* BR */ = 0.0f; @@ -356,63 +291,44 @@ static void SDLCALL SDL_ConvertStereoTo71(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 2) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21ToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21ToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("2.1", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src += 3, dst += 1) { + for (i = num_frames * 3; i; i--, src += 3, dst++) { dst[0] /* FC */ = (src[0] * 0.333333343f) + (src[1] * 0.333333343f) + (src[2] * 0.333333343f); } - cvt->len_cvt = cvt->len_cvt / 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21ToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("2.1", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src += 3, dst += 2) { + for (i = num_frames * 3; i; i--, src += 3, dst += 2) { const float srcLFE = src[2]; dst[0] /* FL */ = (src[0] * 0.800000012f) + (srcLFE * 0.200000003f); dst[1] /* FR */ = (src[1] * 0.800000012f) + (srcLFE * 0.200000003f); } - cvt->len_cvt = (cvt->len_cvt / 3) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21ToQuad(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 3) * 4))) - 4; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 3; int i; - LOG_DEBUG_CONVERT("2.1", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "quad"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src -= 3, dst -= 4) { + src += (num_frames-1) * 3; + dst += (num_frames-1) * 4; + for (i = num_frames * 3; i; i--, src -= 3, dst -= 4) { const float srcLFE = src[2]; dst[3] /* BR */ = (srcLFE * 0.111111112f); dst[2] /* BL */ = (srcLFE * 0.111111112f); @@ -420,23 +336,18 @@ static void SDLCALL SDL_Convert21ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = (srcLFE * 0.111111112f) + (src[0] * 0.888888896f); } - cvt->len_cvt = (cvt->len_cvt / 3) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21To41(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 3) * 5))) - 5; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 3; int i; - LOG_DEBUG_CONVERT("2.1", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "4.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src -= 3, dst -= 5) { + src += (num_frames-1) * 3; + dst += (num_frames-1) * 5; + for (i = num_frames * 3; i; i--, src -= 3, dst -= 5) { dst[4] /* BR */ = 0.0f; dst[3] /* BL */ = 0.0f; dst[2] /* LFE */ = src[2]; @@ -444,23 +355,18 @@ static void SDLCALL SDL_Convert21To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 3) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21To51(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 3) * 6))) - 6; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 3; int i; - LOG_DEBUG_CONVERT("2.1", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "5.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src -= 3, dst -= 6) { + src += (num_frames-1) * 3; + dst += (num_frames-1) * 6; + for (i = num_frames * 3; i; i--, src -= 3, dst -= 6) { dst[5] /* BR */ = 0.0f; dst[4] /* BL */ = 0.0f; dst[3] /* LFE */ = src[2]; @@ -469,23 +375,18 @@ static void SDLCALL SDL_Convert21To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 3) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21To61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 3) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 3; int i; - LOG_DEBUG_CONVERT("2.1", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src -= 3, dst -= 7) { + src += (num_frames-1) * 3; + dst += (num_frames-1) * 7; + for (i = num_frames * 3; i; i--, src -= 3, dst -= 7) { dst[6] /* SR */ = 0.0f; dst[5] /* SL */ = 0.0f; dst[4] /* BC */ = 0.0f; @@ -495,23 +396,18 @@ static void SDLCALL SDL_Convert21To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 3) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert21To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert21To71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 3) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 3; int i; - LOG_DEBUG_CONVERT("2.1", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("2.1", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 3); i; i--, src -= 3, dst -= 8) { + src += (num_frames-1) * 3; + dst += (num_frames-1) * 8; + for (i = num_frames * 3; i; i--, src -= 3, dst -= 8) { dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; dst[5] /* BR */ = 0.0f; @@ -522,63 +418,42 @@ static void SDLCALL SDL_Convert21To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 3) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("quad", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src += 4, dst += 1) { + for (i = num_frames * 4; i; i--, src += 4, dst++) { dst[0] /* FC */ = (src[0] * 0.250000000f) + (src[1] * 0.250000000f) + (src[2] * 0.250000000f) + (src[3] * 0.250000000f); } - cvt->len_cvt = cvt->len_cvt / 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("quad", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src += 4, dst += 2) { + for (i = num_frames * 4; i; i--, src += 4, dst += 2) { const float srcBL = src[2]; const float srcBR = src[3]; dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); dst[1] /* FR */ = (src[1] * 0.421000004f) + (srcBL * 0.219999999f) + (srcBR * 0.358999997f); } - cvt->len_cvt = (cvt->len_cvt / 4) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadTo21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadTo21(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("quad", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "2.1"); - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src += 4, dst += 3) { + for (i = num_frames * 4; i; i--, src += 4, dst += 3) { const float srcBL = src[2]; const float srcBR = src[3]; dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); @@ -586,23 +461,18 @@ static void SDLCALL SDL_ConvertQuadTo21(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[2] /* LFE */ = 0.0f; } - cvt->len_cvt = (cvt->len_cvt / 4) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadTo41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadTo41(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 4) * 5))) - 5; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 4; int i; - LOG_DEBUG_CONVERT("quad", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "4.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src -= 4, dst -= 5) { + src += (num_frames-1) * 4; + dst += (num_frames-1) * 5; + for (i = num_frames * 4; i; i--, src -= 4, dst -= 5) { dst[4] /* BR */ = src[3]; dst[3] /* BL */ = src[2]; dst[2] /* LFE */ = 0.0f; @@ -610,23 +480,18 @@ static void SDLCALL SDL_ConvertQuadTo41(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 4) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadTo51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadTo51(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 4) * 6))) - 6; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 4; int i; - LOG_DEBUG_CONVERT("quad", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "5.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src -= 4, dst -= 6) { + src += (num_frames-1) * 4; + dst += (num_frames-1) * 6; + for (i = num_frames * 4; i; i--, src -= 4, dst -= 6) { dst[5] /* BR */ = src[3]; dst[4] /* BL */ = src[2]; dst[3] /* LFE */ = 0.0f; @@ -635,23 +500,18 @@ static void SDLCALL SDL_ConvertQuadTo51(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 4) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadTo61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadTo61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 4) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 4; int i; - LOG_DEBUG_CONVERT("quad", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src -= 4, dst -= 7) { + src += (num_frames-1) * 4; + dst += (num_frames-1) * 7; + for (i = num_frames * 4; i; i--, src -= 4, dst -= 7) { const float srcBL = src[2]; const float srcBR = src[3]; dst[6] /* SR */ = (srcBR * 0.796000004f); @@ -663,23 +523,18 @@ static void SDLCALL SDL_ConvertQuadTo61(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = (src[0] * 0.939999998f); } - cvt->len_cvt = (cvt->len_cvt / 4) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_ConvertQuadTo71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_ConvertQuadTo71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 4) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 4; int i; - LOG_DEBUG_CONVERT("quad", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("quad", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 4); i; i--, src -= 4, dst -= 8) { + src += (num_frames-1) * 4; + dst += (num_frames-1) * 8; + for (i = num_frames * 4; i; i--, src -= 4, dst -= 8) { dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; dst[5] /* BR */ = src[3]; @@ -690,41 +545,27 @@ static void SDLCALL SDL_ConvertQuadTo71(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 4) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41ToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41ToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("4.1", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src += 5, dst += 1) { + for (i = num_frames * 5; i; i--, src += 5, dst++) { dst[0] /* FC */ = (src[0] * 0.200000003f) + (src[1] * 0.200000003f) + (src[2] * 0.200000003f) + (src[3] * 0.200000003f) + (src[4] * 0.200000003f); } - cvt->len_cvt = cvt->len_cvt / 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41ToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("4.1", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src += 5, dst += 2) { + for (i = num_frames * 5; i; i--, src += 5, dst += 2) { const float srcLFE = src[2]; const float srcBL = src[3]; const float srcBR = src[4]; @@ -732,22 +573,15 @@ static void SDLCALL SDL_Convert41ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[1] /* FR */ = (src[1] * 0.374222219f) + (srcLFE * 0.111111112f) + (srcBL * 0.195555553f) + (srcBR * 0.319111109f); } - cvt->len_cvt = (cvt->len_cvt / 5) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41To21(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("4.1", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "2.1"); - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src += 5, dst += 3) { + for (i = num_frames * 5; i; i--, src += 5, dst += 3) { const float srcBL = src[3]; const float srcBR = src[4]; dst[0] /* FL */ = (src[0] * 0.421000004f) + (srcBL * 0.358999997f) + (srcBR * 0.219999999f); @@ -755,22 +589,15 @@ static void SDLCALL SDL_Convert41To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[2] /* LFE */ = src[2]; } - cvt->len_cvt = (cvt->len_cvt / 5) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41ToQuad(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("4.1", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "quad"); - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src += 5, dst += 4) { + for (i = num_frames * 5; i; i--, src += 5, dst += 4) { const float srcLFE = src[2]; dst[0] /* FL */ = (src[0] * 0.941176474f) + (srcLFE * 0.058823530f); dst[1] /* FR */ = (src[1] * 0.941176474f) + (srcLFE * 0.058823530f); @@ -778,23 +605,18 @@ static void SDLCALL SDL_Convert41ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[3] /* BR */ = (srcLFE * 0.058823530f) + (src[4] * 0.941176474f); } - cvt->len_cvt = (cvt->len_cvt / 5) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41To51(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 5) * 6))) - 6; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 5; int i; - LOG_DEBUG_CONVERT("4.1", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "5.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src -= 5, dst -= 6) { + src += (num_frames-1) * 5; + dst += (num_frames-1) * 6; + for (i = num_frames * 5; i; i--, src -= 5, dst -= 6) { dst[5] /* BR */ = src[4]; dst[4] /* BL */ = src[3]; dst[3] /* LFE */ = src[2]; @@ -803,23 +625,18 @@ static void SDLCALL SDL_Convert41To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 5) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41To61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 5) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 5; int i; - LOG_DEBUG_CONVERT("4.1", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src -= 5, dst -= 7) { + src += (num_frames-1) * 5; + dst += (num_frames-1) * 7; + for (i = num_frames * 5; i; i--, src -= 5, dst -= 7) { const float srcBL = src[3]; const float srcBR = src[4]; dst[6] /* SR */ = (srcBR * 0.796000004f); @@ -831,23 +648,18 @@ static void SDLCALL SDL_Convert41To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = (src[0] * 0.939999998f); } - cvt->len_cvt = (cvt->len_cvt / 5) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert41To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert41To71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 5) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 5; int i; - LOG_DEBUG_CONVERT("4.1", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("4.1", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 5); i; i--, src -= 5, dst -= 8) { + src += (num_frames-1) * 5; + dst += (num_frames-1) * 8; + for (i = num_frames * 5; i; i--, src -= 5, dst -= 8) { dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; dst[5] /* BR */ = src[4]; @@ -858,41 +670,27 @@ static void SDLCALL SDL_Convert41To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 5) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51ToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51ToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("5.1", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src += 6, dst += 1) { + for (i = num_frames * 6; i; i--, src += 6, dst++) { dst[0] /* FC */ = (src[0] * 0.166666672f) + (src[1] * 0.166666672f) + (src[2] * 0.166666672f) + (src[3] * 0.166666672f) + (src[4] * 0.166666672f) + (src[5] * 0.166666672f); } - cvt->len_cvt = cvt->len_cvt / 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51ToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("5.1", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src += 6, dst += 2) { + for (i = num_frames * 6; i; i--, src += 6, dst += 2) { const float srcFC = src[2]; const float srcLFE = src[3]; const float srcBL = src[4]; @@ -901,22 +699,15 @@ static void SDLCALL SDL_Convert51ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[1] /* FR */ = (src[1] * 0.294545442f) + (srcFC * 0.208181813f) + (srcLFE * 0.090909094f) + (srcBL * 0.154545456f) + (srcBR * 0.251818180f); } - cvt->len_cvt = (cvt->len_cvt / 6) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51To21(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("5.1", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "2.1"); - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src += 6, dst += 3) { + for (i = num_frames * 6; i; i--, src += 6, dst += 3) { const float srcFC = src[2]; const float srcBL = src[4]; const float srcBR = src[5]; @@ -925,22 +716,15 @@ static void SDLCALL SDL_Convert51To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[2] /* LFE */ = src[3]; } - cvt->len_cvt = (cvt->len_cvt / 6) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51ToQuad(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("5.1", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "quad"); - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src += 6, dst += 4) { + for (i = num_frames * 6; i; i--, src += 6, dst += 4) { const float srcFC = src[2]; const float srcLFE = src[3]; dst[0] /* FL */ = (src[0] * 0.558095276f) + (srcFC * 0.394285709f) + (srcLFE * 0.047619049f); @@ -949,22 +733,15 @@ static void SDLCALL SDL_Convert51ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[3] /* BR */ = (srcLFE * 0.047619049f) + (src[5] * 0.558095276f); } - cvt->len_cvt = (cvt->len_cvt / 6) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51To41(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("5.1", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "4.1"); - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src += 6, dst += 5) { + for (i = num_frames * 6; i; i--, src += 6, dst += 5) { const float srcFC = src[2]; dst[0] /* FL */ = (src[0] * 0.586000025f) + (srcFC * 0.414000005f); dst[1] /* FR */ = (src[1] * 0.586000025f) + (srcFC * 0.414000005f); @@ -973,23 +750,18 @@ static void SDLCALL SDL_Convert51To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[4] /* BR */ = (src[5] * 0.586000025f); } - cvt->len_cvt = (cvt->len_cvt / 6) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51To61(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 6) * 7))) - 7; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 6; int i; - LOG_DEBUG_CONVERT("5.1", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "6.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src -= 6, dst -= 7) { + src += (num_frames-1) * 6; + dst += (num_frames-1) * 7; + for (i = num_frames * 6; i; i--, src -= 6, dst -= 7) { const float srcBL = src[4]; const float srcBR = src[5]; dst[6] /* SR */ = (srcBR * 0.796000004f); @@ -1001,23 +773,18 @@ static void SDLCALL SDL_Convert51To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = (src[0] * 0.939999998f); } - cvt->len_cvt = (cvt->len_cvt / 6) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert51To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert51To71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 6) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 6; int i; - LOG_DEBUG_CONVERT("5.1", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("5.1", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 6); i; i--, src -= 6, dst -= 8) { + src += (num_frames-1) * 6; + dst += (num_frames-1) * 8; + for (i = num_frames * 6; i; i--, src -= 6, dst -= 8) { dst[7] /* SR */ = 0.0f; dst[6] /* SL */ = 0.0f; dst[5] /* BR */ = src[5]; @@ -1028,41 +795,27 @@ static void SDLCALL SDL_Convert51To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 6) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61ToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61ToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 1) { + for (i = num_frames * 7; i; i--, src += 7, dst++) { dst[0] /* FC */ = (src[0] * 0.143142849f) + (src[1] * 0.143142849f) + (src[2] * 0.143142849f) + (src[3] * 0.142857149f) + (src[4] * 0.143142849f) + (src[5] * 0.143142849f) + (src[6] * 0.143142849f); } - cvt->len_cvt = cvt->len_cvt / 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61ToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 2) { + for (i = num_frames * 7; i; i--, src += 7, dst += 2) { const float srcFC = src[2]; const float srcLFE = src[3]; const float srcBC = src[4]; @@ -1072,22 +825,15 @@ static void SDLCALL SDL_Convert61ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[1] /* FR */ = (src[1] * 0.247384623f) + (srcFC * 0.174461529f) + (srcLFE * 0.076923080f) + (srcBC * 0.174461529f) + (srcSL * 0.100615382f) + (srcSR * 0.226153851f); } - cvt->len_cvt = (cvt->len_cvt / 7) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61To21(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "2.1"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 3) { + for (i = num_frames * 7; i; i--, src += 7, dst += 3) { const float srcFC = src[2]; const float srcBC = src[4]; const float srcSL = src[5]; @@ -1097,22 +843,15 @@ static void SDLCALL SDL_Convert61To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[2] /* LFE */ = src[3]; } - cvt->len_cvt = (cvt->len_cvt / 7) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61ToQuad(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "quad"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 4) { + for (i = num_frames * 7; i; i--, src += 7, dst += 4) { const float srcFC = src[2]; const float srcLFE = src[3]; const float srcBC = src[4]; @@ -1124,22 +863,15 @@ static void SDLCALL SDL_Convert61ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[3] /* BR */ = (srcLFE * 0.040000003f) + (srcBC * 0.327360004f) + (srcSR * 0.431039989f); } - cvt->len_cvt = (cvt->len_cvt / 7) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61To41(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "4.1"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 5) { + for (i = num_frames * 7; i; i--, src += 7, dst += 5) { const float srcFC = src[2]; const float srcBC = src[4]; const float srcSL = src[5]; @@ -1151,22 +883,15 @@ static void SDLCALL SDL_Convert61To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[4] /* BR */ = (srcBC * 0.340999991f) + (srcSR * 0.449000001f); } - cvt->len_cvt = (cvt->len_cvt / 7) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61To51(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("6.1", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "5.1"); - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src += 7, dst += 6) { + for (i = num_frames * 7; i; i--, src += 7, dst += 6) { const float srcBC = src[4]; const float srcSL = src[5]; const float srcSR = src[6]; @@ -1178,23 +903,18 @@ static void SDLCALL SDL_Convert61To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[5] /* BR */ = (srcBC * 0.432000011f) + (srcSR * 0.568000019f); } - cvt->len_cvt = (cvt->len_cvt / 7) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert61To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert61To71(float *dst, const float *src, int num_frames) { - float *dst = ((float *)(cvt->buf + ((cvt->len_cvt / 7) * 8))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 7; int i; - LOG_DEBUG_CONVERT("6.1", "7.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("6.1", "7.1"); /* convert backwards, since output is growing in-place. */ - for (i = cvt->len_cvt / (sizeof(float) * 7); i; i--, src -= 7, dst -= 8) { + src += (num_frames-1) * 7; + dst += (num_frames-1) * 8; + for (i = num_frames * 7; i; i--, src -= 7, dst -= 8) { const float srcBC = src[4]; dst[7] /* SR */ = src[6]; dst[6] /* SL */ = src[5]; @@ -1206,41 +926,27 @@ static void SDLCALL SDL_Convert61To71(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[0] /* FL */ = src[0]; } - cvt->len_cvt = (cvt->len_cvt / 7) * 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71ToMono(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71ToMono(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "mono"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "mono"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 1) { + for (i = num_frames * 8; i; i--, src += 8, dst++) { dst[0] /* FC */ = (src[0] * 0.125125006f) + (src[1] * 0.125125006f) + (src[2] * 0.125125006f) + (src[3] * 0.125000000f) + (src[4] * 0.125125006f) + (src[5] * 0.125125006f) + (src[6] * 0.125125006f) + (src[7] * 0.125125006f); } - cvt->len_cvt = cvt->len_cvt / 8; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71ToStereo(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "stereo"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "stereo"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 2) { + for (i = num_frames * 8; i; i--, src += 8, dst += 2) { const float srcFC = src[2]; const float srcLFE = src[3]; const float srcBL = src[4]; @@ -1251,22 +957,15 @@ static void SDLCALL SDL_Convert71ToStereo(SDL_AudioCVT *cvt, SDL_AudioFormat for dst[1] /* FR */ = (src[1] * 0.211866662f) + (srcFC * 0.150266662f) + (srcLFE * 0.066666670f) + (srcBL * 0.111066669f) + (srcBR * 0.181066677f) + (srcSL * 0.085866667f) + (srcSR * 0.194133341f); } - cvt->len_cvt = (cvt->len_cvt / 8) * 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71To21(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "2.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "2.1"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 3) { + for (i = num_frames * 8; i; i--, src += 8, dst += 3) { const float srcFC = src[2]; const float srcBL = src[4]; const float srcBR = src[5]; @@ -1277,22 +976,15 @@ static void SDLCALL SDL_Convert71To21(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[2] /* LFE */ = src[3]; } - cvt->len_cvt = (cvt->len_cvt / 8) * 3; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71ToQuad(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "quad"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "quad"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 4) { + for (i = num_frames * 8; i; i--, src += 8, dst += 4) { const float srcFC = src[2]; const float srcLFE = src[3]; const float srcSL = src[6]; @@ -1303,22 +995,15 @@ static void SDLCALL SDL_Convert71ToQuad(SDL_AudioCVT *cvt, SDL_AudioFormat forma dst[3] /* BR */ = (srcLFE * 0.034482758f) + (src[5] * 0.466344833f) + (srcSR * 0.433517247f); } - cvt->len_cvt = (cvt->len_cvt / 8) * 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71To41(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "4.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "4.1"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 5) { + for (i = num_frames * 8; i; i--, src += 8, dst += 5) { const float srcFC = src[2]; const float srcSL = src[6]; const float srcSR = src[7]; @@ -1329,22 +1014,15 @@ static void SDLCALL SDL_Convert71To41(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[4] /* BR */ = (src[5] * 0.483000010f) + (srcSR * 0.449000001f); } - cvt->len_cvt = (cvt->len_cvt / 8) * 5; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71To51(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "5.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "5.1"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 6) { + for (i = num_frames * 8; i; i--, src += 8, dst += 6) { const float srcSL = src[6]; const float srcSR = src[7]; dst[0] /* FL */ = (src[0] * 0.518000007f) + (srcSL * 0.188999996f); @@ -1355,22 +1033,15 @@ static void SDLCALL SDL_Convert71To51(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[5] /* BR */ = (src[5] * 0.518000007f) + (srcSR * 0.481999993f); } - cvt->len_cvt = (cvt->len_cvt / 8) * 6; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static void SDLCALL SDL_Convert71To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) +static void SDL_Convert71To61(float *dst, const float *src, int num_frames) { - float *dst = (float *)cvt->buf; - const float *src = dst; int i; - LOG_DEBUG_CONVERT("7.1", "6.1"); - SDL_assert(format == AUDIO_F32SYS); + LOG_DEBUG_AUDIO_CONVERT("7.1", "6.1"); - for (i = cvt->len_cvt / (sizeof(float) * 8); i; i--, src += 8, dst += 7) { + for (i = num_frames * 8; i; i--, src += 8, dst += 7) { const float srcBL = src[4]; const float srcBR = src[5]; dst[0] /* FL */ = (src[0] * 0.541000009f); @@ -1382,19 +1053,16 @@ static void SDLCALL SDL_Convert71To61(SDL_AudioCVT *cvt, SDL_AudioFormat format) dst[6] /* SR */ = (srcBR * 0.458999991f) + (src[7] * 0.541000009f); } - cvt->len_cvt = (cvt->len_cvt / 8) * 7; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } } -static const SDL_AudioFilter channel_converters[8][8] = { /* [from][to] */ - { NULL, SDL_ConvertMonoToStereo, SDL_ConvertMonoTo21, SDL_ConvertMonoToQuad, SDL_ConvertMonoTo41, SDL_ConvertMonoTo51, SDL_ConvertMonoTo61, SDL_ConvertMonoTo71 }, - { SDL_ConvertStereoToMono, NULL, SDL_ConvertStereoTo21, SDL_ConvertStereoToQuad, SDL_ConvertStereoTo41, SDL_ConvertStereoTo51, SDL_ConvertStereoTo61, SDL_ConvertStereoTo71 }, - { SDL_Convert21ToMono, SDL_Convert21ToStereo, NULL, SDL_Convert21ToQuad, SDL_Convert21To41, SDL_Convert21To51, SDL_Convert21To61, SDL_Convert21To71 }, - { SDL_ConvertQuadToMono, SDL_ConvertQuadToStereo, SDL_ConvertQuadTo21, NULL, SDL_ConvertQuadTo41, SDL_ConvertQuadTo51, SDL_ConvertQuadTo61, SDL_ConvertQuadTo71 }, - { SDL_Convert41ToMono, SDL_Convert41ToStereo, SDL_Convert41To21, SDL_Convert41ToQuad, NULL, SDL_Convert41To51, SDL_Convert41To61, SDL_Convert41To71 }, - { SDL_Convert51ToMono, SDL_Convert51ToStereo, SDL_Convert51To21, SDL_Convert51ToQuad, SDL_Convert51To41, NULL, SDL_Convert51To61, SDL_Convert51To71 }, - { SDL_Convert61ToMono, SDL_Convert61ToStereo, SDL_Convert61To21, SDL_Convert61ToQuad, SDL_Convert61To41, SDL_Convert61To51, NULL, SDL_Convert61To71 }, - { SDL_Convert71ToMono, SDL_Convert71ToStereo, SDL_Convert71To21, SDL_Convert71ToQuad, SDL_Convert71To41, SDL_Convert71To51, SDL_Convert71To61, NULL } +static const SDL_AudioChannelConverter channel_converters[8][8] = { /* [from][to] */ + { NULL, SDL_ConvertMonoToStereo, SDL_ConvertMonoTo21, SDL_ConvertMonoToQuad, SDL_ConvertMonoTo41, SDL_ConvertMonoTo51, SDL_ConvertMonoTo61, SDL_ConvertMonoTo71 }, + { SDL_ConvertStereoToMono, NULL, SDL_ConvertStereoTo21, SDL_ConvertStereoToQuad, SDL_ConvertStereoTo41, SDL_ConvertStereoTo51, SDL_ConvertStereoTo61, SDL_ConvertStereoTo71 }, + { SDL_Convert21ToMono, SDL_Convert21ToStereo, NULL, SDL_Convert21ToQuad, SDL_Convert21To41, SDL_Convert21To51, SDL_Convert21To61, SDL_Convert21To71 }, + { SDL_ConvertQuadToMono, SDL_ConvertQuadToStereo, SDL_ConvertQuadTo21, NULL, SDL_ConvertQuadTo41, SDL_ConvertQuadTo51, SDL_ConvertQuadTo61, SDL_ConvertQuadTo71 }, + { SDL_Convert41ToMono, SDL_Convert41ToStereo, SDL_Convert41To21, SDL_Convert41ToQuad, NULL, SDL_Convert41To51, SDL_Convert41To61, SDL_Convert41To71 }, + { SDL_Convert51ToMono, SDL_Convert51ToStereo, SDL_Convert51To21, SDL_Convert51ToQuad, SDL_Convert51To41, NULL, SDL_Convert51To61, SDL_Convert51To71 }, + { SDL_Convert61ToMono, SDL_Convert61ToStereo, SDL_Convert61To21, SDL_Convert61ToQuad, SDL_Convert61To41, SDL_Convert61To51, NULL, SDL_Convert61To71 }, + { SDL_Convert71ToMono, SDL_Convert71ToStereo, SDL_Convert71To21, SDL_Convert71ToQuad, SDL_Convert71To41, SDL_Convert71To51, SDL_Convert71To61, NULL } }; + diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 413f5e797..ec6307811 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -23,235 +23,52 @@ /* Functions for audio drivers to perform runtime conversion of audio format */ #include "SDL_audio_c.h" -#include "SDL_audiocvt_c.h" #include "../SDL_dataqueue.h" -#define DEBUG_AUDIOSTREAM 0 - -/** - * Initialize an SDL_AudioCVT structure for conversion. - * - * Before an SDL_AudioCVT structure can be used to convert audio data it must - * be initialized with source and destination information. - * - * This function will zero out every field of the SDL_AudioCVT, so it must be - * called before the application fills in the final buffer information. - * - * Once this function has returned successfully, and reported that a - * conversion is necessary, the application fills in the rest of the fields in - * SDL_AudioCVT, now that it knows how large a buffer it needs to allocate, - * and then can call SDL_ConvertAudio() to complete the conversion. - * - * \param cvt an SDL_AudioCVT structure filled in with audio conversion - * information - * \param src_format the source format of the audio data; for more info see - * SDL_AudioFormat - * \param src_channels the number of channels in the source - * \param src_rate the frequency (sample-frames-per-second) of the source - * \param dst_format the destination format of the audio data; for more info - * see SDL_AudioFormat - * \param dst_channels the number of channels in the destination - * \param dst_rate the frequency (sample-frames-per-second) of the destination - * \returns 1 if the audio filter is prepared, 0 if no conversion is needed, - * or a negative error code on failure; call SDL_GetError() for more - * information. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_ConvertAudio - */ -static int SDL_BuildAudioCVT(SDL_AudioCVT * cvt, - SDL_AudioFormat src_format, - Uint8 src_channels, - int src_rate, - SDL_AudioFormat dst_format, - Uint8 dst_channels, - int dst_rate); - - -/** - * Convert audio data to a desired audio format. - * - * This function does the actual audio data conversion, after the application - * has called SDL_BuildAudioCVT() to prepare the conversion information and - * then filled in the buffer details. - * - * Once the application has initialized the `cvt` structure using - * SDL_BuildAudioCVT(), allocated an audio buffer and filled it with audio - * data in the source format, this function will convert the buffer, in-place, - * to the desired format. - * - * The data conversion may go through several passes; any given pass may - * possibly temporarily increase the size of the data. For example, SDL might - * expand 16-bit data to 32 bits before resampling to a lower frequency, - * shrinking the data size after having grown it briefly. Since the supplied - * buffer will be both the source and destination, converting as necessary - * in-place, the application must allocate a buffer that will fully contain - * the data during its largest conversion pass. After SDL_BuildAudioCVT() - * returns, the application should set the `cvt->len` field to the size, in - * bytes, of the source data, and allocate a buffer that is `cvt->len * - * cvt->len_mult` bytes long for the `buf` field. - * - * The source data should be copied into this buffer before the call to - * SDL_ConvertAudio(). Upon successful return, this buffer will contain the - * converted audio, and `cvt->len_cvt` will be the size of the converted data, - * in bytes. Any bytes in the buffer past `cvt->len_cvt` are undefined once - * this function returns. - * - * \param cvt an SDL_AudioCVT structure that was previously set up by - * SDL_BuildAudioCVT(). - * \returns 0 if the conversion was completed successfully or a negative error - * code on failure; call SDL_GetError() for more information. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_BuildAudioCVT - */ -static int SDL_ConvertAudio(SDL_AudioCVT * cvt); - - -/* - * CHANNEL LAYOUTS AS SDL EXPECTS THEM: - * - * (Even if the platform expects something else later, that - * SDL will swizzle between the app and the platform). - * - * Abbreviations: - * - FRONT=single mono speaker - * - FL=front left speaker - * - FR=front right speaker - * - FC=front center speaker - * - BL=back left speaker - * - BR=back right speaker - * - SR=surround right speaker - * - SL=surround left speaker - * - BC=back center speaker - * - LFE=low-frequency speaker - * - * These are listed in the order they are laid out in - * memory, so "FL+FR" means "the front left speaker is - * layed out in memory first, then the front right, then - * it repeats for the next audio frame". - * - * 1 channel (mono) layout: FRONT - * 2 channels (stereo) layout: FL+FR - * 3 channels (2.1) layout: FL+FR+LFE - * 4 channels (quad) layout: FL+FR+BL+BR - * 5 channels (4.1) layout: FL+FR+LFE+BL+BR - * 6 channels (5.1) layout: FL+FR+FC+LFE+BL+BR - * 7 channels (6.1) layout: FL+FR+FC+LFE+BC+SL+SR - * 8 channels (7.1) layout: FL+FR+FC+LFE+BL+BR+SL+SR - */ - -#ifdef SDL_SSE3_INTRINSICS -/* Convert from stereo to mono. Average left and right. */ -static void SDLCALL SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(SDL_AudioCVT *cvt, SDL_AudioFormat format) -{ - const __m128 divby2 = _mm_set1_ps(0.5f); - float *dst = (float *)cvt->buf; - const float *src = dst; - int i = cvt->len_cvt / 8; - - LOG_DEBUG_CONVERT("stereo", "mono (using SSE3)"); - SDL_assert(format == AUDIO_F32SYS); - - /* Do SSE blocks as long as we have 16 bytes available. - Just use unaligned load/stores, if the memory at runtime is - aligned it'll be just as fast on modern processors */ - while (i >= 4) { /* 4 * float32 */ - _mm_storeu_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_loadu_ps(src), _mm_loadu_ps(src + 4)), divby2)); - i -= 4; - src += 8; - dst += 4; - } - - /* Finish off any leftovers with scalar operations. */ - while (i) { - *dst = (src[0] + src[1]) * 0.5f; - dst++; - i--; - src += 2; - } - - cvt->len_cvt /= 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } -} -#endif - -#ifdef SDL_SSE_INTRINSICS -/* Convert from mono to stereo. Duplicate to stereo left and right. */ -static void SDLCALL SDL_TARGETING("sse") SDL_ConvertMonoToStereo_SSE(SDL_AudioCVT *cvt, SDL_AudioFormat format) -{ - float *dst = ((float *)(cvt->buf + (cvt->len_cvt * 2))) - 8; - const float *src = ((const float *)(cvt->buf + cvt->len_cvt)) - 4; - int i = cvt->len_cvt / sizeof(float); - - LOG_DEBUG_CONVERT("mono", "stereo (using SSE)"); - SDL_assert(format == AUDIO_F32SYS); - - /* Do SSE blocks as long as we have 16 bytes available. - Just use unaligned load/stores, if the memory at runtime is - aligned it'll be just as fast on modern processors */ - /* convert backwards, since output is growing in-place. */ - while (i >= 4) { /* 4 * float32 */ - const __m128 input = _mm_loadu_ps(src); /* A B C D */ - _mm_storeu_ps(dst, _mm_unpacklo_ps(input, input)); /* A A B B */ - _mm_storeu_ps(dst + 4, _mm_unpackhi_ps(input, input)); /* C C D D */ - i -= 4; - src -= 4; - dst -= 8; - } - - /* Finish off any leftovers with scalar operations. */ - src += 3; - dst += 6; /* adjust for smaller buffers. */ - while (i) { /* convert backwards, since output is growing in-place. */ - const float srcFC = src[0]; - dst[1] /* FR */ = srcFC; - dst[0] /* FL */ = srcFC; - i--; - src--; - dst -= 2; - } - - cvt->len_cvt *= 2; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } -} -#endif - -/* Include the autogenerated channel converters... */ -#include "SDL_audio_channel_converters.h" /* SDL's resampler uses a "bandlimited interpolation" algorithm: https://ccrma.stanford.edu/~jos/resample/ */ #include "SDL_audio_resampler_filter.h" -static Sint32 GetResamplerPadding(const Sint32 inrate, const Sint32 outrate) +static int GetResamplerPaddingFrames(const int iinrate, const int ioutrate) { /* This function uses integer arithmetics to avoid precision loss caused * by large floating point numbers. Sint32 is needed for the large number * multiplication. The integers are assumed to be non-negative so that * division rounds by truncation. */ + const Sint32 inrate = (Sint32) iinrate; + const Sint32 outrate = (Sint32) ioutrate; + SDL_assert(inrate >= 0); + SDL_assert(outrate >= 0); if (inrate == outrate) { return 0; - } - if (inrate > outrate) { - return (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate + outrate - 1) / outrate; + } else if (inrate > outrate) { + return (int) (((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) + (outrate - 1)) / outrate); } return RESAMPLER_SAMPLES_PER_ZERO_CROSSING; } -/* lpadding and rpadding are expected to be buffers of (ResamplePadding(inrate, outrate) * chans * sizeof(float)) bytes. */ -static int SDL_ResampleAudio(const int chans, const int inrate, const int outrate, - const float *lpadding, const float *rpadding, - const float *inbuf, const int inbuflen, - float *outbuf, const int outbuflen) +static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames) +{ + /* we want to keep enough input history to successfully resample data between arbitrary + frequencies without causing artifacts at the start of a chunk, but also to retain + history if the output frequency changes midstream. So we always demand at least 5000 + sample frames here--which is enough to cover a sudden resample from 192000Hz to 22050Hz + without problems--if the app is gradually changing the sample rate for a pitch effect + to any destination, this won't be a problem, and if they just need a dramatic + downsample that doesn't change, we give them the buffer they need and that won't be a + problem either. Upsamples don't need this at any frequency. The rest seems like an + acceptable loss. */ + return (int) SDL_max(required_resampler_frames, 5000); +} + +/* lpadding and rpadding are expected to be buffers of (GetResamplePadding(inrate, outrate) * chans * sizeof (float)) bytes. */ +static void ResampleAudio(const int chans, const int inrate, const int outrate, + const float *lpadding, const float *rpadding, + const float *inbuf, const int inframes, + float *outbuf, const int outframes) { /* This function uses integer arithmetics to avoid precision loss caused * by large floating point numbers. For some operations, Sint32 or Sint64 @@ -259,13 +76,7 @@ static int SDL_ResampleAudio(const int chans, const int inrate, const int outrat * assumed to be non-negative so that division rounds by truncation and * modulo is always non-negative. Note that the operator order is important * for these integer divisions. */ - const int paddinglen = GetResamplerPadding(inrate, outrate); - const int framelen = chans * (int)sizeof(float); - const int inframes = inbuflen / framelen; - /* outbuflen isn't total to write, it's total available. */ - const int wantedoutframes = (int)((Sint64)inframes * outrate / inrate); - const int maxoutframes = outbuflen / framelen; - const int outframes = SDL_min(wantedoutframes, maxoutframes); + const int paddinglen = GetResamplerPaddingFrames(inrate, outrate); float *dst = outbuf; int i, j, chan; @@ -307,48 +118,130 @@ static int SDL_ResampleAudio(const int chans, const int inrate, const int outrat *(dst++) = outsample; } } - - return outframes * chans * sizeof(float); } -static int SDL_ConvertAudio(SDL_AudioCVT *cvt) +/* + * CHANNEL LAYOUTS AS SDL EXPECTS THEM: + * + * (Even if the platform expects something else later, that + * SDL will swizzle between the app and the platform). + * + * Abbreviations: + * - FRONT=single mono speaker + * - FL=front left speaker + * - FR=front right speaker + * - FC=front center speaker + * - BL=back left speaker + * - BR=back right speaker + * - SR=surround right speaker + * - SL=surround left speaker + * - BC=back center speaker + * - LFE=low-frequency speaker + * + * These are listed in the order they are laid out in + * memory, so "FL+FR" means "the front left speaker is + * layed out in memory first, then the front right, then + * it repeats for the next audio frame". + * + * 1 channel (mono) layout: FRONT + * 2 channels (stereo) layout: FL+FR + * 3 channels (2.1) layout: FL+FR+LFE + * 4 channels (quad) layout: FL+FR+BL+BR + * 5 channels (4.1) layout: FL+FR+LFE+BL+BR + * 6 channels (5.1) layout: FL+FR+FC+LFE+BL+BR + * 7 channels (6.1) layout: FL+FR+FC+LFE+BC+SL+SR + * 8 channels (7.1) layout: FL+FR+FC+LFE+BL+BR+SL+SR + */ + +#ifdef SDL_SSE3_INTRINSICS +/* Convert from stereo to mono. Average left and right. */ +static void SDL_TARGETING("sse3") SDL_ConvertStereoToMono_SSE3(float *dst, const float *src, int num_frames) { - /* !!! FIXME: (cvt) should be const; stack-copy it here. */ - /* !!! FIXME: (actually, we can't...len_cvt needs to be updated. Grr.) */ + const __m128 divby2 = _mm_set1_ps(0.5f); + int i = num_frames; - /* Make sure there's data to convert */ - if (cvt->buf == NULL) { - return SDL_SetError("No buffer allocated for conversion"); + LOG_DEBUG_AUDIO_CONVERT("stereo", "mono (using SSE3)"); + + /* Do SSE blocks as long as we have 16 bytes available. + Just use unaligned load/stores, if the memory at runtime is + aligned it'll be just as fast on modern processors */ + while (i >= 4) { /* 4 * float32 */ + _mm_storeu_ps(dst, _mm_mul_ps(_mm_hadd_ps(_mm_loadu_ps(src), _mm_loadu_ps(src + 4)), divby2)); + i -= 4; + src += 8; + dst += 4; } - /* Return okay if no conversion is necessary */ - cvt->len_cvt = cvt->len; - if (cvt->filters[0] == NULL) { - return 0; + /* Finish off any leftovers with scalar operations. */ + while (i) { + *dst = (src[0] + src[1]) * 0.5f; + dst++; + i--; + src += 2; } - - /* Set up the conversion and go! */ - cvt->filter_index = 0; - cvt->filters[0](cvt, cvt->src_format); - return 0; } - -static void SDLCALL SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat format) -{ -#if DEBUG_CONVERT - SDL_Log("SDL_AUDIO_CONVERT: Converting byte order\n"); #endif - switch (SDL_AUDIO_BITSIZE(format)) { -#define CASESWAP(b) \ - case b: \ - { \ - Uint##b *ptr = (Uint##b *)cvt->buf; \ - int i; \ - for (i = cvt->len_cvt / sizeof(*ptr); i; --i, ++ptr) { \ - *ptr = SDL_Swap##b(*ptr); \ - } \ - break; \ +#ifdef SDL_SSE_INTRINSICS +/* Convert from mono to stereo. Duplicate to stereo left and right. */ +static void SDL_TARGETING("sse") SDL_ConvertMonoToStereo_SSE(float *dst, const float *src, int num_frames) +{ + int i = num_frames; + + /* convert backwards, since output is growing in-place. */ + src += (num_frames-1) * 1; + dst += (num_frames-1) * 2; + + LOG_DEBUG_AUDIO_CONVERT("mono", "stereo (using SSE)"); + + /* Do SSE blocks as long as we have 16 bytes available. + Just use unaligned load/stores, if the memory at runtime is + aligned it'll be just as fast on modern processors */ + /* convert backwards, since output is growing in-place. */ + while (i >= 4) { /* 4 * float32 */ + const __m128 input = _mm_loadu_ps(src); /* A B C D */ + _mm_storeu_ps(dst, _mm_unpacklo_ps(input, input)); /* A A B B */ + _mm_storeu_ps(dst + 4, _mm_unpackhi_ps(input, input)); /* C C D D */ + i -= 4; + src -= 4; + dst -= 8; + } + + /* Finish off any leftovers with scalar operations. */ + src += 3; + dst += 6; /* adjust for smaller buffers. */ + while (i) { /* convert backwards, since output is growing in-place. */ + const float srcFC = src[0]; + dst[1] /* FR */ = srcFC; + dst[0] /* FL */ = srcFC; + i--; + src--; + dst -= 2; + } +} +#endif + +/* Include the autogenerated channel converters... */ +#include "SDL_audio_channel_converters.h" + + +static void AudioConvertByteswap(void *dst, const void *src, int num_samples, int bitsize) +{ + int i; + +#if DEBUG_AUDIO_CONVERT + SDL_Log("SDL_AUDIO_CONVERT: Converting %d-bit byte order", bitsize); +#endif + + switch (bitsize) { +#define CASESWAP(b) \ + case b: { \ + const Uint##b *tsrc = (const Uint##b *)src; \ + Uint##b *tdst = (Uint##b *)dst; \ + for (i = num_samples; i; i++) { \ + tdst[i] = SDL_Swap##b(tsrc[i]); \ + } \ + break; \ } CASESWAP(16); @@ -361,338 +254,34 @@ static void SDLCALL SDL_Convert_Byteswap(SDL_AudioCVT *cvt, SDL_AudioFormat form SDL_assert(!"unhandled byteswap datatype!"); break; } +} - if (cvt->filters[++cvt->filter_index]) { - /* flip endian flag for data. */ - if (format & SDL_AUDIO_MASK_ENDIAN) { - format &= ~SDL_AUDIO_MASK_ENDIAN; - } else { - format |= SDL_AUDIO_MASK_ENDIAN; - } - cvt->filters[cvt->filter_index](cvt, format); +static void AudioConvertToFloat(float *dst, const void *src, int num_samples, SDL_AudioFormat src_fmt) +{ + SDL_assert( (SDL_AUDIO_BITSIZE(src_fmt) <= 8) || ((SDL_AUDIO_ISBIGENDIAN(src_fmt) == 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) ); /* This only deals with native byte order. */ + + switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) { + case AUDIO_S8: SDL_Convert_S8_to_F32(dst, (const Sint8 *) src, num_samples); break; + case AUDIO_U8: SDL_Convert_U8_to_F32(dst, (const Uint8 *) src, num_samples); break; + case AUDIO_S16: SDL_Convert_S16_to_F32(dst, (const Sint16 *) src, num_samples); break; + case AUDIO_S32: SDL_Convert_S32_to_F32(dst, (const Sint32 *) src, num_samples); break; + case AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break; /* oh well, just pass it through. */ + default: SDL_assert(!"Unexpected audio format!"); break; } } -static int SDL_AddAudioCVTFilter(SDL_AudioCVT *cvt, SDL_AudioFilter filter) +static void AudioConvertFromFloat(void *dst, const float *src, int num_samples, SDL_AudioFormat dst_fmt) { - if (cvt->filter_index >= SDL_AUDIOCVT_MAX_FILTERS) { - return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS); + SDL_assert( (SDL_AUDIO_BITSIZE(dst_fmt) <= 8) || ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) == 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN)) ); /* This only deals with native byte order. */ + + switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) { + case AUDIO_S8: SDL_Convert_F32_to_S8((Sint8 *) dst, src, num_samples); break; + case AUDIO_U8: SDL_Convert_F32_to_U8((Uint8 *) dst, src, num_samples); break; + case AUDIO_S16: SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); break; + case AUDIO_S32: SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); break; + case AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break; /* oh well, just pass it through. */ + default: SDL_assert(!"Unexpected audio format!"); break; } - SDL_assert(filter != NULL); - cvt->filters[cvt->filter_index++] = filter; - cvt->filters[cvt->filter_index] = NULL; /* Moving terminator */ - return 0; -} - -static int SDL_BuildAudioTypeCVTToFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat src_fmt) -{ - int retval = 0; /* 0 == no conversion necessary. */ - - if ((SDL_AUDIO_ISBIGENDIAN(src_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && SDL_AUDIO_BITSIZE(src_fmt) > 8) { - if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { - return -1; - } - retval = 1; /* added a converter. */ - } - - if (!SDL_AUDIO_ISFLOAT(src_fmt)) { - const Uint16 src_bitsize = SDL_AUDIO_BITSIZE(src_fmt); - const Uint16 dst_bitsize = 32; - SDL_AudioFilter filter = NULL; - - switch (src_fmt & ~SDL_AUDIO_MASK_ENDIAN) { - case AUDIO_S8: - filter = SDL_Convert_S8_to_F32; - break; - case AUDIO_U8: - filter = SDL_Convert_U8_to_F32; - break; - case AUDIO_S16: - filter = SDL_Convert_S16_to_F32; - break; - case AUDIO_S32: - filter = SDL_Convert_S32_to_F32; - break; - default: - SDL_assert(!"Unexpected audio format!"); - break; - } - - if (!filter) { - return SDL_SetError("No conversion from source format to float available"); - } - - if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { - return -1; - } - if (src_bitsize < dst_bitsize) { - const int mult = (dst_bitsize / src_bitsize); - cvt->len_mult *= mult; - cvt->len_ratio *= mult; - } else if (src_bitsize > dst_bitsize) { - const int div = (src_bitsize / dst_bitsize); - cvt->len_ratio /= div; - } - - retval = 1; /* added a converter. */ - } - - return retval; -} - -static int SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt) -{ - int retval = 0; /* 0 == no conversion necessary. */ - - if (!SDL_AUDIO_ISFLOAT(dst_fmt)) { - const Uint16 dst_bitsize = SDL_AUDIO_BITSIZE(dst_fmt); - const Uint16 src_bitsize = 32; - SDL_AudioFilter filter = NULL; - switch (dst_fmt & ~SDL_AUDIO_MASK_ENDIAN) { - case AUDIO_S8: - filter = SDL_Convert_F32_to_S8; - break; - case AUDIO_U8: - filter = SDL_Convert_F32_to_U8; - break; - case AUDIO_S16: - filter = SDL_Convert_F32_to_S16; - break; - case AUDIO_S32: - filter = SDL_Convert_F32_to_S32; - break; - default: - SDL_assert(!"Unexpected audio format!"); - break; - } - - if (!filter) { - return SDL_SetError("No conversion from float to format 0x%.4x available", dst_fmt); - } - - if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { - return -1; - } - if (src_bitsize < dst_bitsize) { - const int mult = (dst_bitsize / src_bitsize); - cvt->len_mult *= mult; - cvt->len_ratio *= mult; - } else if (src_bitsize > dst_bitsize) { - const int div = (src_bitsize / dst_bitsize); - cvt->len_ratio /= div; - } - retval = 1; /* added a converter. */ - } - - if ((SDL_AUDIO_ISBIGENDIAN(dst_fmt) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && SDL_AUDIO_BITSIZE(dst_fmt) > 8) { - if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { - return -1; - } - retval = 1; /* added a converter. */ - } - - return retval; -} - -#ifdef HAVE_LIBSAMPLERATE - -static void SDL_ResampleCVT_SRC(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format) -{ - const float *src = (const float *)cvt->buf; - const int srclen = cvt->len_cvt; - float *dst = (float *)(cvt->buf + srclen); - const int dstlen = (cvt->len * cvt->len_mult) - srclen; - const int framelen = sizeof(float) * chans; - int result = 0; - SRC_DATA data; - - SDL_zero(data); - - data.data_in = (float *)src; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */ - data.input_frames = srclen / framelen; - - data.data_out = dst; - data.output_frames = dstlen / framelen; - - data.src_ratio = cvt->rate_incr; - - result = SRC_src_simple(&data, SRC_converter, chans); /* Simple API converts the whole buffer at once. No need for initialization. */ -/* !!! FIXME: Handle library failures? */ -#if DEBUG_CONVERT - if (result != 0) { - SDL_Log("src_simple() failed: %s", SRC_src_strerror(result)); - } -#else - (void)result; -#endif - - cvt->len_cvt = data.output_frames_gen * framelen; - - SDL_memmove(cvt->buf, dst, cvt->len_cvt); - - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } -} - -#endif /* HAVE_LIBSAMPLERATE */ - -static int SDL_ResampleCVT(SDL_AudioCVT *cvt, const int chans, const SDL_AudioFormat format) -{ - /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator). - !!! FIXME in 2.1: We need to store data for this resampler, because the cvt structure doesn't store the original sample rates, - !!! FIXME in 2.1: so we steal the ninth and tenth slot. :( */ - const int inrate = (int)(size_t)cvt->filters[SDL_AUDIOCVT_MAX_FILTERS - 1]; - const int outrate = (int)(size_t)cvt->filters[SDL_AUDIOCVT_MAX_FILTERS]; - const float *src = (const float *)cvt->buf; - const int srclen = cvt->len_cvt; - /*float *dst = (float *) cvt->buf; - const int dstlen = (cvt->len * cvt->len_mult);*/ - /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ - float *dst = (float *)(cvt->buf + srclen); - const int dstlen = (cvt->len * cvt->len_mult) - srclen; - const int requestedpadding = GetResamplerPadding(inrate, outrate); - int paddingsamples; - float *padding; - - if (requestedpadding < SDL_MAX_SINT32 / chans) { - paddingsamples = requestedpadding * chans; - } else { - paddingsamples = 0; - } - SDL_assert(format == AUDIO_F32SYS); - - /* we keep no streaming state here, so pad with silence on both ends. */ - padding = (float *)SDL_calloc(paddingsamples ? paddingsamples : 1, sizeof(float)); - if (padding == NULL) { - return SDL_OutOfMemory(); - } - - cvt->len_cvt = SDL_ResampleAudio(chans, inrate, outrate, padding, padding, src, srclen, dst, dstlen); - - SDL_free(padding); - - SDL_memmove(cvt->buf, dst, cvt->len_cvt); /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ - - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, format); - } - return 0; -} - -/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't - !!! FIXME: store channel info, so we have to have function entry - !!! FIXME: points for each supported channel count and multiple - !!! FIXME: vs arbitrary. When we rev the ABI, clean this up. */ -#define RESAMPLER_FUNCS(chans) \ - static void SDLCALL \ - SDL_ResampleCVT_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) \ - { \ - SDL_ResampleCVT(cvt, chans, format); \ - } -RESAMPLER_FUNCS(1) -RESAMPLER_FUNCS(2) -RESAMPLER_FUNCS(4) -RESAMPLER_FUNCS(6) -RESAMPLER_FUNCS(8) -#undef RESAMPLER_FUNCS - -#ifdef HAVE_LIBSAMPLERATE -#define RESAMPLER_FUNCS(chans) \ - static void SDLCALL \ - SDL_ResampleCVT_SRC_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) \ - { \ - SDL_ResampleCVT_SRC(cvt, chans, format); \ - } -RESAMPLER_FUNCS(1) -RESAMPLER_FUNCS(2) -RESAMPLER_FUNCS(4) -RESAMPLER_FUNCS(6) -RESAMPLER_FUNCS(8) -#undef RESAMPLER_FUNCS -#endif /* HAVE_LIBSAMPLERATE */ - -static SDL_AudioFilter ChooseCVTResampler(const int dst_channels) -{ -#ifdef HAVE_LIBSAMPLERATE - if (SRC_available) { - switch (dst_channels) { - case 1: - return SDL_ResampleCVT_SRC_c1; - case 2: - return SDL_ResampleCVT_SRC_c2; - case 4: - return SDL_ResampleCVT_SRC_c4; - case 6: - return SDL_ResampleCVT_SRC_c6; - case 8: - return SDL_ResampleCVT_SRC_c8; - default: - break; - } - } -#endif /* HAVE_LIBSAMPLERATE */ - - switch (dst_channels) { - case 1: - return SDL_ResampleCVT_c1; - case 2: - return SDL_ResampleCVT_c2; - case 4: - return SDL_ResampleCVT_c4; - case 6: - return SDL_ResampleCVT_c6; - case 8: - return SDL_ResampleCVT_c8; - default: - break; - } - - return NULL; -} - -static int SDL_BuildAudioResampleCVT(SDL_AudioCVT *cvt, const int dst_channels, - const int src_rate, const int dst_rate) -{ - SDL_AudioFilter filter; - - if (src_rate == dst_rate) { - return 0; /* no conversion necessary. */ - } - - filter = ChooseCVTResampler(dst_channels); - if (filter == NULL) { - return SDL_SetError("No conversion available for these rates"); - } - - /* Update (cvt) with filter details... */ - if (SDL_AddAudioCVTFilter(cvt, filter) < 0) { - return -1; - } - - /* !!! FIXME in 2.1: there are ten slots in the filter list, and the theoretical maximum we use is six (seven with NULL terminator). - !!! FIXME in 2.1: We need to store data for this resampler, because the cvt structure doesn't store the original sample rates, - !!! FIXME in 2.1: so we steal the ninth and tenth slot. :( */ - if (cvt->filter_index >= (SDL_AUDIOCVT_MAX_FILTERS - 2)) { - return SDL_SetError("Too many filters needed for conversion, exceeded maximum of %d", SDL_AUDIOCVT_MAX_FILTERS - 2); - } - cvt->filters[SDL_AUDIOCVT_MAX_FILTERS - 1] = (SDL_AudioFilter)(uintptr_t)src_rate; - cvt->filters[SDL_AUDIOCVT_MAX_FILTERS] = (SDL_AudioFilter)(uintptr_t)dst_rate; - - if (src_rate < dst_rate) { - const double mult = ((double)dst_rate) / ((double)src_rate); - cvt->len_mult *= (int)SDL_ceil(mult); - cvt->len_ratio *= mult; - } else { - cvt->len_ratio /= ((double)src_rate) / ((double)dst_rate); - } - - /* !!! FIXME: remove this if we can get the resampler to work in-place again. */ - /* the buffer is big enough to hold the destination now, but - we need it large enough to hold a separate scratch buffer. */ - cvt->len_mult *= 2; - - return 1; /* added a converter. */ } static SDL_bool SDL_IsSupportedAudioFormat(const SDL_AudioFormat fmt) @@ -720,72 +309,38 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels) return ((channels >= 1) && (channels <= 8)) ? SDL_TRUE : SDL_FALSE; } -/* Creates a set of audio filters to convert from one format to another. - Returns 0 if no conversion is needed, 1 if the audio filter is set up, - or -1 if an error like invalid parameter, unsupported format, etc. occurred. -*/ -static int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, - SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, - SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate) +/* this does type and channel conversions _but not resampling_ (resampling + happens in SDL_AudioStream). This expects data to be aligned/padded for + SIMD access. This does not check parameter validity, + (beyond asserts), it expects you did that already! */ +/* all of this has to function as if src==dst (conversion in-place), but as a convenience + if you're just going to copy the final output elsewhere, you can specify a different + output pointer. */ +static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, + void *dst, SDL_AudioFormat dst_format, int dst_channels) { - SDL_AudioFilter channel_converter = NULL; + const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format); + const int src_bitsize = (int) SDL_AUDIO_BITSIZE(src_format); + SDL_assert(src != NULL); + SDL_assert(dst != NULL); + SDL_assert(SDL_IsSupportedAudioFormat(src_format)); + SDL_assert(SDL_IsSupportedAudioFormat(dst_format)); + SDL_assert(SDL_IsSupportedChannelCount(src_channels)); + SDL_assert(SDL_IsSupportedChannelCount(dst_channels)); - /* Sanity check target pointer */ - if (cvt == NULL) { - return SDL_InvalidParamError("cvt"); - } - - /* Make sure we zero out the audio conversion before error checking */ - SDL_zerop(cvt); - - if (!SDL_IsSupportedAudioFormat(src_format)) { - return SDL_SetError("Invalid source format"); - } - if (!SDL_IsSupportedAudioFormat(dst_format)) { - return SDL_SetError("Invalid destination format"); - } - if (!SDL_IsSupportedChannelCount(src_channels)) { - return SDL_SetError("Invalid source channels"); - } - if (!SDL_IsSupportedChannelCount(dst_channels)) { - return SDL_SetError("Invalid destination channels"); - } - if (src_rate <= 0) { - return SDL_SetError("Source rate is equal to or less than zero"); - } - if (dst_rate <= 0) { - return SDL_SetError("Destination rate is equal to or less than zero"); - } - if (src_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { - return SDL_SetError("Source rate is too high"); - } - if (dst_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { - return SDL_SetError("Destination rate is too high"); - } - -#if DEBUG_CONVERT - SDL_Log("SDL_AUDIO_CONVERT: Build format %04x->%04x, channels %u->%u, rate %d->%d\n", - src_format, dst_format, src_channels, dst_channels, src_rate, dst_rate); +#if DEBUG_AUDIO_CONVERT + SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels); #endif - /* Start off with no conversion necessary */ - cvt->src_format = src_format; - cvt->dst_format = dst_format; - cvt->needed = 0; - cvt->filter_index = 0; - SDL_zeroa(cvt->filters); - cvt->len_mult = 1; - cvt->len_ratio = 1.0; - cvt->rate_incr = ((double)dst_rate) / ((double)src_rate); - - /* Make sure we've chosen audio conversion functions (SIMD, scalar, etc.) */ - SDL_ChooseAudioConverters(); + if (!num_frames) { + return; /* no data to convert, quit. */ + } /* Type conversion goes like this now: - byteswap to CPU native format first if necessary. - convert to native Float32 if necessary. - - resample and change channel count if necessary. + - change channel count if necessary. - convert to final data format. - byteswap back to foreign format if necessary. @@ -797,292 +352,276 @@ static int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, it was a bloat on SDL compile times and final library size. */ /* see if we can skip float conversion entirely. */ - if (src_rate == dst_rate && src_channels == dst_channels) { + if (src_channels == dst_channels) { if (src_format == dst_format) { - return 0; + /* nothing to do, we're already in the right format, just copy it over if necessary. */ + if (src != dst) { + SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8)); + } + return; } /* just a byteswap needed? */ if ((src_format & ~SDL_AUDIO_MASK_ENDIAN) == (dst_format & ~SDL_AUDIO_MASK_ENDIAN)) { - if (SDL_AUDIO_BITSIZE(dst_format) == 8) { - return 0; + if (src_bitsize == 8) { + if (src != dst) { + SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8)); + } + return; /* nothing to do, it's a 1-byte format. */ } - if (SDL_AddAudioCVTFilter(cvt, SDL_Convert_Byteswap) < 0) { - return -1; - } - cvt->needed = 1; - return 1; + AudioConvertByteswap(dst, src, num_frames * src_channels, src_bitsize); + return; /* all done. */ } } - /* Convert data types, if necessary. Updates (cvt). */ - if (SDL_BuildAudioTypeCVTToFloat(cvt, src_format) < 0) { - return -1; /* shouldn't happen, but just in case... */ + /* make sure we're in native byte order. */ + if ((SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8)) { + AudioConvertByteswap(dst, src, num_frames * src_channels, src_bitsize); + src = dst; /* we've written to dst, future work will convert in-place. */ + } + + /* get us to float format. */ + if (!SDL_AUDIO_ISFLOAT(src_format)) { + AudioConvertToFloat((float *) dst, src, num_frames * src_channels, src_format); + src = dst; /* we've written to dst, future work will convert in-place. */ } /* Channel conversion */ - /* SDL_IsSupportedChannelCount should have caught these asserts, or we added a new format and forgot to update the table. */ - SDL_assert(src_channels <= SDL_arraysize(channel_converters)); - SDL_assert(dst_channels <= SDL_arraysize(channel_converters[0])); + if (src_channels != dst_channels) { + SDL_AudioChannelConverter channel_converter; + SDL_AudioChannelConverter override = NULL; + + /* SDL_IsSupportedChannelCount should have caught these asserts, or we added a new format and forgot to update the table. */ + SDL_assert(src_channels <= SDL_arraysize(channel_converters)); + SDL_assert(dst_channels <= SDL_arraysize(channel_converters[0])); + + channel_converter = channel_converters[src_channels - 1][dst_channels - 1]; + SDL_assert(channel_converter != NULL); - channel_converter = channel_converters[src_channels - 1][dst_channels - 1]; - if ((channel_converter == NULL) != (src_channels == dst_channels)) { - /* All combinations of supported channel counts should have been handled by now, but let's be defensive */ - return SDL_SetError("Invalid channel combination"); - } else if (channel_converter != NULL) { /* swap in some SIMD versions for a few of these. */ if (channel_converter == SDL_ConvertStereoToMono) { - SDL_AudioFilter filter = NULL; -#ifdef SDL_SSE3_INTRINSICS - if (!filter && SDL_HasSSE3()) { - filter = SDL_ConvertStereoToMono_SSE3; - } -#endif - if (filter) { - channel_converter = filter; - } + #ifdef SDL_SSE3_INTRINSICS + if (!override && SDL_HasSSE3()) { override = SDL_ConvertStereoToMono_SSE3; } + #endif } else if (channel_converter == SDL_ConvertMonoToStereo) { - SDL_AudioFilter filter = NULL; -#ifdef SDL_SSE_INTRINSICS - if (!filter && SDL_HasSSE()) { - filter = SDL_ConvertMonoToStereo_SSE; - } -#endif - if (filter) { - channel_converter = filter; - } + #ifdef SDL_SSE_INTRINSICS + if (!override && SDL_HasSSE()) { override = SDL_ConvertMonoToStereo_SSE; } + #endif } - if (SDL_AddAudioCVTFilter(cvt, channel_converter) < 0) { - return -1; + if (override) { + channel_converter = override; } - - if (src_channels < dst_channels) { - cvt->len_mult = ((cvt->len_mult * dst_channels) + (src_channels - 1)) / src_channels; - } - - cvt->len_ratio = (cvt->len_ratio * dst_channels) / src_channels; - src_channels = dst_channels; + channel_converter((float *) dst, (float *) src, num_frames); + src = dst; /* we've written to dst, future work will convert in-place. */ } - /* Do rate conversion, if necessary. Updates (cvt). */ - if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) < 0) { - return -1; /* shouldn't happen, but just in case... */ - } + /* Resampling is not done in here. SDL_AudioStream handles that. */ /* Move to final data type. */ - if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_format) < 0) { - return -1; /* shouldn't happen, but just in case... */ + if (!SDL_AUDIO_ISFLOAT(dst_format)) { + AudioConvertFromFloat(dst, (float *) src, num_frames * dst_channels, dst_format); + src = dst; /* we've written to dst, future work will convert in-place. */ } - cvt->needed = (cvt->filter_index != 0); - return cvt->needed; -} + /* make sure we're in final byte order. */ + if ((SDL_AUDIO_ISBIGENDIAN(dst_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (dst_bitsize > 8)) { + AudioConvertByteswap(dst, src, num_frames * dst_channels, dst_bitsize); + src = dst; /* we've written to dst, future work will convert in-place. */ + } -typedef int (*SDL_ResampleAudioStreamFunc)(SDL_AudioStream *stream, const void *inbuf, const int inbuflen, void *outbuf, const int outbuflen); -typedef void (*SDL_ResetAudioStreamResamplerFunc)(SDL_AudioStream *stream); -typedef void (*SDL_CleanupAudioStreamResamplerFunc)(SDL_AudioStream *stream); + SDL_assert(src == dst); /* if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd! */ +} struct SDL_AudioStream { - SDL_AudioCVT cvt_before_resampling; - SDL_AudioCVT cvt_after_resampling; SDL_DataQueue *queue; - SDL_bool first_run; - Uint8 *staging_buffer; - int staging_buffer_size; - int staging_buffer_filled; - Uint8 *work_buffer_base; /* maybe unaligned pointer from SDL_realloc(). */ - int work_buffer_len; + SDL_mutex *lock; /* this is just a copy of `queue`'s mutex. We share a lock. */ + + Uint8 *work_buffer; /* used for scratch space during data conversion/resampling. */ + Uint8 *history_buffer; /* history for left padding and future sample rate changes. */ + Uint8 *future_buffer; /* stuff that left the queue for the right padding and will be next read's data. */ + float *left_padding; /* left padding for resampling. */ + float *right_padding; /* right padding for resampling. */ + + SDL_bool flushed; + + size_t work_buffer_allocation; + size_t history_buffer_allocation; + size_t future_buffer_allocation; + size_t resampler_padding_allocation; + + int resampler_padding_frames; + int history_buffer_frames; + int future_buffer_filled_frames; + int src_sample_frame_size; SDL_AudioFormat src_format; - Uint8 src_channels; + int src_channels; int src_rate; + int dst_sample_frame_size; SDL_AudioFormat dst_format; - Uint8 dst_channels; + int dst_channels; int dst_rate; - double rate_incr; - Uint8 pre_resample_channels; + + int pre_resample_channels; int packetlen; - int resampler_padding_samples; - float *resampler_padding; - void *resampler_state; - SDL_ResampleAudioStreamFunc resampler_func; - SDL_ResetAudioStreamResamplerFunc reset_resampler_func; - SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func; }; -static Uint8 *EnsureStreamBufferSize(SDL_AudioStream *stream, int newlen) +static int GetMemsetSilenceValue(const SDL_AudioFormat fmt) { - Uint8 *ptr; - size_t offset; + return (fmt == AUDIO_U8) ? 0x80 : 0x00; +} - if (stream->work_buffer_len >= newlen) { - ptr = stream->work_buffer_base; - } else { - ptr = (Uint8 *)SDL_realloc(stream->work_buffer_base, (size_t)newlen + 32); - if (ptr == NULL) { - SDL_OutOfMemory(); - return NULL; +/* this assumes you're holding the stream's lock (or are still creating the stream). */ +static int SetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioFormat src_format, int src_channels, int src_rate, SDL_AudioFormat dst_format, int dst_channels, int dst_rate) +{ + /* If increasing channels, do it after resampling, since we'd just + do more work to resample duplicate channels. If we're decreasing, do + it first so we resample the interpolated data instead of interpolating + the resampled data (!!! FIXME: decide if that works in practice, though!). + This is decided in pre_resample_channels. */ + const int src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; + const int dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels; + const int prev_history_buffer_frames = stream->history_buffer_frames; + const int pre_resample_channels = SDL_min(src_channels, dst_channels); + const int resampler_padding_frames = GetResamplerPaddingFrames(src_rate, dst_rate); + const size_t resampler_padding_allocation = ((size_t) resampler_padding_frames) * pre_resample_channels * sizeof (float); + const size_t future_buffer_allocation = resampler_padding_frames * src_sample_frame_size; + const int history_buffer_frames = GetHistoryBufferSampleFrames(resampler_padding_frames); + const size_t history_buffer_allocation = history_buffer_frames * src_sample_frame_size; + Uint8 *history_buffer = stream->history_buffer; + Uint8 *future_buffer = stream->future_buffer; + float *padding; + + /* do all the things that can fail upfront, so we can just return an error without changing the stream if anything goes wrong. */ + + /* set up for (possibly new) conversions */ + + /* grow the padding buffers if necessary; these buffer sizes change if sample rate or source channel count is adjusted. */ + /* (we can replace these buffers in `stream` now even if we abandon this function when a later allocation fails, because it's safe for these buffers to be overallocated and their contents don't matter.) */ + if (stream->resampler_padding_allocation < resampler_padding_allocation) { + /* left_padding and right_padding are just scratch buffers, so we don't need to preserve existing contents. */ + padding = (float *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), resampler_padding_allocation); + if (!padding) { + return SDL_OutOfMemory(); } - /* Make sure we're aligned to 16 bytes for SIMD code. */ - stream->work_buffer_base = ptr; - stream->work_buffer_len = newlen; + SDL_aligned_free(stream->left_padding); + stream->left_padding = padding; + + padding = (float *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), resampler_padding_allocation); + if (!padding) { + return SDL_OutOfMemory(); + } + SDL_aligned_free(stream->right_padding); + stream->right_padding = padding; + + stream->resampler_padding_allocation = resampler_padding_allocation; } - offset = ((size_t)ptr) & 15; - return offset ? ptr + (16 - offset) : ptr; -} - -#ifdef HAVE_LIBSAMPLERATE -static int SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen) -{ - const float *inbuf = (const float *)_inbuf; - float *outbuf = (float *)_outbuf; - const int framelen = sizeof(float) * stream->pre_resample_channels; - SRC_STATE *state = (SRC_STATE *)stream->resampler_state; - SRC_DATA data; - int result; - - SDL_assert(inbuf != ((const float *)outbuf)); /* SDL_PutAudioStreamData() shouldn't allow in-place resamples. */ - - data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */ - data.input_frames = inbuflen / framelen; - data.input_frames_used = 0; - - data.data_out = outbuf; - data.output_frames = outbuflen / framelen; - - data.end_of_input = 0; - data.src_ratio = stream->rate_incr; - - result = SRC_src_process(state, &data); - if (result != 0) { - SDL_SetError("src_process() failed: %s", SRC_src_strerror(result)); - return 0; - } - - /* If this fails, we need to store them off somewhere */ - SDL_assert(data.input_frames_used == data.input_frames); - - return data.output_frames_gen * (sizeof(float) * stream->pre_resample_channels); -} - -static void SDL_ResetAudioStreamResampler_SRC(SDL_AudioStream *stream) -{ - SRC_src_reset((SRC_STATE *)stream->resampler_state); -} - -static void SDL_CleanupAudioStreamResampler_SRC(SDL_AudioStream *stream) -{ - SRC_STATE *state = (SRC_STATE *)stream->resampler_state; - if (state) { - SRC_src_delete(state); - } - - stream->resampler_state = NULL; - stream->resampler_func = NULL; - stream->reset_resampler_func = NULL; - stream->cleanup_resampler_func = NULL; -} - -static SDL_bool SetupLibSampleRateResampling(SDL_AudioStream *stream) -{ - int result = 0; - SRC_STATE *state = NULL; - - if (SRC_available) { - state = SRC_src_new(SRC_converter, stream->pre_resample_channels, &result); - if (state == NULL) { - SDL_SetError("src_new() failed: %s", SRC_src_strerror(result)); + /* grow the history buffer if necessary; often times this won't be, as it already buffers more than immediately necessary in case of a dramatic downsample. */ + if (stream->history_buffer_allocation < history_buffer_allocation) { + history_buffer = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), history_buffer_allocation); + if (!history_buffer) { + return SDL_OutOfMemory(); } } - if (state == NULL) { - SDL_CleanupAudioStreamResampler_SRC(stream); - return SDL_FALSE; + /* grow the future buffer if necessary; the buffer size changes if sample rate is adjusted. */ + if (stream->future_buffer_allocation < future_buffer_allocation) { + future_buffer = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), future_buffer_allocation); + if (!future_buffer) { + if (history_buffer != stream->history_buffer) { + SDL_aligned_free(history_buffer); + } + return SDL_OutOfMemory(); + } } - stream->resampler_state = state; - stream->resampler_func = SDL_ResampleAudioStream_SRC; - stream->reset_resampler_func = SDL_ResetAudioStreamResampler_SRC; - stream->cleanup_resampler_func = SDL_CleanupAudioStreamResampler_SRC; + /* okay, we've done all the things that can fail, now we can change stream state. */ - return SDL_TRUE; -} -#endif /* HAVE_LIBSAMPLERATE */ - -static int SDL_ResampleAudioStream(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen) -{ - const Uint8 *inbufend = ((const Uint8 *)_inbuf) + inbuflen; - const float *inbuf = (const float *)_inbuf; - float *outbuf = (float *)_outbuf; - const int chans = (int)stream->pre_resample_channels; - const int inrate = stream->src_rate; - const int outrate = stream->dst_rate; - const int paddingsamples = stream->resampler_padding_samples; - const int paddingbytes = paddingsamples * sizeof(float); - float *lpadding = (float *)stream->resampler_state; - const float *rpadding = (const float *)inbufend; /* we set this up so there are valid padding samples at the end of the input buffer. */ - const int cpy = SDL_min(inbuflen, paddingbytes); - int retval; - - SDL_assert(inbuf != ((const float *)outbuf)); /* SDL_PutAudioStreamData() shouldn't allow in-place resamples. */ - - retval = SDL_ResampleAudio(chans, inrate, outrate, lpadding, rpadding, inbuf, inbuflen, outbuf, outbuflen); - - /* update our left padding with end of current input, for next run. */ - if (cpy < paddingbytes) { /* slide end of the padding buffer to the start if we aren't replacing the whole thing. */ - SDL_memmove(lpadding, lpadding + (cpy / sizeof(float)), paddingbytes - cpy); + /* copy to new buffers and/or convert data; ConvertAudio will do a simple memcpy if format matches, and nothing at all if the buffer hasn't changed */ + if (stream->future_buffer) { + ConvertAudio(stream->future_buffer_filled_frames, stream->future_buffer, stream->src_format, stream->src_channels, future_buffer, src_format, src_channels); + } else if (future_buffer != NULL) { + SDL_memset(future_buffer, GetMemsetSilenceValue(src_format), future_buffer_allocation); } - SDL_memcpy((lpadding + paddingsamples) - (cpy / sizeof(float)), inbufend - cpy, cpy); - return retval; -} -static void SDL_ResetAudioStreamResampler(SDL_AudioStream *stream) -{ - /* set all the padding to silence. */ - const int len = stream->resampler_padding_samples; - SDL_memset(stream->resampler_state, '\0', len * sizeof(float)); -} + if (stream->history_buffer) { + if (history_buffer_frames <= prev_history_buffer_frames) { + ConvertAudio(history_buffer_frames, stream->history_buffer, stream->src_format, stream->src_channels, history_buffer, src_format, src_channels); + } else { + ConvertAudio(prev_history_buffer_frames, stream->history_buffer, stream->src_format, stream->src_channels, history_buffer + ((history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size), src_format, src_channels); + SDL_memset(history_buffer, GetMemsetSilenceValue(src_format), (history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size); /* silence oldest history samples. */ + } + } else if (history_buffer != NULL) { + SDL_memset(history_buffer, GetMemsetSilenceValue(src_format), history_buffer_allocation); + } -static void SDL_CleanupAudioStreamResampler(SDL_AudioStream *stream) -{ - SDL_free(stream->resampler_state); + if (future_buffer != stream->future_buffer) { + SDL_aligned_free(stream->future_buffer); + stream->future_buffer = future_buffer; + stream->future_buffer_allocation = future_buffer_allocation; + } + + if (history_buffer != stream->history_buffer) { + SDL_aligned_free(stream->history_buffer); + stream->history_buffer = history_buffer; + stream->history_buffer_allocation = history_buffer_allocation; + } + + stream->resampler_padding_frames = resampler_padding_frames; + stream->history_buffer_frames = history_buffer_frames; + stream->src_sample_frame_size = src_sample_frame_size; + stream->src_format = src_format; + stream->src_channels = src_channels; + stream->src_rate = src_rate; + stream->dst_sample_frame_size = dst_sample_frame_size; + stream->dst_format = dst_format; + stream->dst_channels = dst_channels; + stream->dst_rate = dst_rate; + stream->pre_resample_channels = pre_resample_channels; + + return 0; } SDL_AudioStream * 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) { int packetlen = 4096; /* !!! FIXME: good enough for now. */ - Uint8 pre_resample_channels; SDL_AudioStream *retval; - if (src_channels == 0) { + if (!SDL_IsSupportedChannelCount(src_channels)) { SDL_InvalidParamError("src_channels"); return NULL; - } - - if (dst_channels == 0) { + } else if (!SDL_IsSupportedChannelCount(dst_channels)) { SDL_InvalidParamError("dst_channels"); return NULL; - } - - if (src_rate <= 0) { + } else if (src_rate <= 0) { SDL_InvalidParamError("src_rate"); return NULL; - } - - if (dst_rate <= 0) { + } else if (dst_rate <= 0) { SDL_InvalidParamError("dst_rate"); return NULL; + } else if (src_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + SDL_SetError("Source rate is too high"); + return NULL; + } else if (dst_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + SDL_SetError("Destination rate is too high"); + return NULL; + } else if (!SDL_IsSupportedAudioFormat(src_format)) { + SDL_InvalidParamError("src_format"); + return NULL; + } else if (!SDL_IsSupportedAudioFormat(dst_format)) { + SDL_InvalidParamError("dst_format"); + return NULL; } retval = (SDL_AudioStream *)SDL_calloc(1, sizeof(SDL_AudioStream)); @@ -1091,395 +630,473 @@ SDL_CreateAudioStream(SDL_AudioFormat src_format, return NULL; } - /* If increasing channels, do it after resampling, since we'd just - do more work to resample duplicate channels. If we're decreasing, do - it first so we resample the interpolated data instead of interpolating - the resampled data (!!! FIXME: decide if that works in practice, though!). */ - pre_resample_channels = SDL_min(src_channels, dst_channels); - - retval->first_run = SDL_TRUE; - retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; - retval->src_format = src_format; - retval->src_channels = src_channels; - retval->src_rate = src_rate; - retval->dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels; - retval->dst_format = dst_format; - retval->dst_channels = dst_channels; - retval->dst_rate = dst_rate; - retval->pre_resample_channels = pre_resample_channels; - retval->packetlen = packetlen; - retval->rate_incr = ((double)dst_rate) / ((double)src_rate); - retval->resampler_padding_samples = GetResamplerPadding(retval->src_rate, retval->dst_rate) * pre_resample_channels; - retval->resampler_padding = (float *)SDL_calloc(retval->resampler_padding_samples ? retval->resampler_padding_samples : 1, sizeof(float)); - - if (retval->resampler_padding == NULL) { - SDL_DestroyAudioStream(retval); - SDL_OutOfMemory(); - return NULL; - } - - retval->staging_buffer_size = ((retval->resampler_padding_samples / retval->pre_resample_channels) * retval->src_sample_frame_size); - if (retval->staging_buffer_size > 0) { - retval->staging_buffer = (Uint8 *)SDL_malloc(retval->staging_buffer_size); - if (retval->staging_buffer == NULL) { - SDL_DestroyAudioStream(retval); - SDL_OutOfMemory(); - return NULL; - } - } - - /* Not resampling? It's an easy conversion (and maybe not even that!) */ - if (src_rate == dst_rate) { - retval->cvt_before_resampling.needed = SDL_FALSE; - if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, src_format, src_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) { - SDL_DestroyAudioStream(retval); - return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ - } - } else { - /* Don't resample at first. Just get us to Float32 format. */ - /* !!! FIXME: convert to int32 on devices without hardware float. */ - if (SDL_BuildAudioCVT(&retval->cvt_before_resampling, src_format, src_channels, src_rate, AUDIO_F32SYS, pre_resample_channels, src_rate) < 0) { - SDL_DestroyAudioStream(retval); - return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ - } - -#ifdef HAVE_LIBSAMPLERATE - SetupLibSampleRateResampling(retval); -#endif - - if (!retval->resampler_func) { - retval->resampler_state = SDL_calloc(retval->resampler_padding_samples, sizeof(float)); - if (!retval->resampler_state) { - SDL_DestroyAudioStream(retval); - SDL_OutOfMemory(); - return NULL; - } - - retval->resampler_func = SDL_ResampleAudioStream; - retval->reset_resampler_func = SDL_ResetAudioStreamResampler; - retval->cleanup_resampler_func = SDL_CleanupAudioStreamResampler; - } - - /* Convert us to the final format after resampling. */ - if (SDL_BuildAudioCVT(&retval->cvt_after_resampling, AUDIO_F32SYS, pre_resample_channels, dst_rate, dst_format, dst_channels, dst_rate) < 0) { - SDL_DestroyAudioStream(retval); - return NULL; /* SDL_BuildAudioCVT should have called SDL_SetError. */ - } - } - retval->queue = SDL_CreateDataQueue(packetlen, (size_t)packetlen * 2); if (!retval->queue) { SDL_DestroyAudioStream(retval); return NULL; /* SDL_CreateDataQueue should have called SDL_SetError. */ } + retval->lock = SDL_GetDataQueueMutex(retval->queue); + SDL_assert(retval->lock != NULL); + + /* Make sure we've chosen audio conversion functions (SIMD, scalar, etc.) */ + SDL_ChooseAudioConverters(); /* !!! FIXME: let's do this during SDL_Init? */ + + retval->src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; + retval->src_format = src_format; + retval->src_channels = src_channels; + retval->src_rate = src_rate; + retval->packetlen = packetlen; + + if (SetAudioStreamFormat(retval, src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate) == -1) { + SDL_DestroyAudioStream(retval); + return NULL; + } + return retval; } -static int SDL_PutAudioStreamInternal(SDL_AudioStream *stream, const void *buf, int len, int *maxputbytes) +int 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) { - int buflen = len; - int workbuflen; - Uint8 *workbuf; - Uint8 *resamplebuf = NULL; - int resamplebuflen = 0; - int neededpaddingbytes; - int paddingbytes; + if (!stream) { + return SDL_InvalidParamError("stream"); + } + SDL_LockMutex(stream->lock); + #define GETAUDIOSTREAMFIELD(x) if (x) { *x = stream->x; } + GETAUDIOSTREAMFIELD(src_format); + GETAUDIOSTREAMFIELD(src_channels); + GETAUDIOSTREAMFIELD(src_rate); + GETAUDIOSTREAMFIELD(dst_format); + GETAUDIOSTREAMFIELD(dst_channels); + GETAUDIOSTREAMFIELD(dst_rate); + #undef GETAUDIOSTREAMFIELD + SDL_UnlockMutex(stream->lock); + return 0; +} - /* !!! FIXME: several converters can take advantage of SIMD, but only - !!! FIXME: if the data is aligned to 16 bytes. EnsureStreamBufferSize() - !!! FIXME: guarantees the buffer will align, but the - !!! FIXME: converters will iterate over the data backwards if - !!! FIXME: the output grows, and this means we won't align if buflen - !!! FIXME: isn't a multiple of 16. In these cases, we should chop off - !!! FIXME: a few samples at the end and convert them separately. */ +int 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) +{ + int retval; - /* no padding prepended on first run. */ - neededpaddingbytes = stream->resampler_padding_samples * sizeof(float); - paddingbytes = stream->first_run ? 0 : neededpaddingbytes; - stream->first_run = SDL_FALSE; - - /* Make sure the work buffer can hold all the data we need at once... */ - workbuflen = buflen; - if (stream->cvt_before_resampling.needed) { - workbuflen *= stream->cvt_before_resampling.len_mult; + if (!stream) { + return SDL_InvalidParamError("stream"); + } else if (!SDL_IsSupportedAudioFormat(src_format)) { + return SDL_InvalidParamError("src_format"); + } else if (!SDL_IsSupportedChannelCount(src_channels)) { + return SDL_InvalidParamError("src_channels"); + } else if (src_rate <= 0) { + return SDL_InvalidParamError("src_rate"); + } else if (src_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + return SDL_SetError("Source rate is too high"); + } else if (!SDL_IsSupportedAudioFormat(dst_format)) { + return SDL_InvalidParamError("dst_format"); + } else if (!SDL_IsSupportedChannelCount(dst_channels)) { + return SDL_InvalidParamError("dst_channels"); + } else if (dst_rate <= 0) { + return SDL_InvalidParamError("dst_rate"); + } else if (dst_rate >= SDL_MAX_SINT32 / RESAMPLER_SAMPLES_PER_ZERO_CROSSING) { + return SDL_SetError("Destination rate is too high"); } - if (stream->dst_rate != stream->src_rate) { - /* resamples can't happen in place, so make space for second buf. */ - const int framesize = stream->pre_resample_channels * sizeof(float); - const int frames = workbuflen / framesize; - resamplebuflen = ((int)SDL_ceil(frames * stream->rate_incr)) * framesize; -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: will resample %d bytes to %d (ratio=%.6f)\n", workbuflen, resamplebuflen, stream->rate_incr); -#endif - workbuflen += resamplebuflen; - } + SDL_LockMutex(stream->lock); + retval = SetAudioStreamFormat(stream, src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); + SDL_UnlockMutex(stream->lock); - if (stream->cvt_after_resampling.needed) { - /* !!! FIXME: buffer might be big enough already? */ - workbuflen *= stream->cvt_after_resampling.len_mult; - } - - workbuflen += neededpaddingbytes; - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: Putting %d bytes of preconverted audio, need %d byte work buffer\n", buflen, workbuflen); -#endif - - workbuf = EnsureStreamBufferSize(stream, workbuflen); - if (workbuf == NULL) { - return -1; /* probably out of memory. */ - } - - resamplebuf = workbuf; /* default if not resampling. */ - - SDL_memcpy(workbuf + paddingbytes, buf, buflen); - - if (stream->cvt_before_resampling.needed) { - stream->cvt_before_resampling.buf = workbuf + paddingbytes; - stream->cvt_before_resampling.len = buflen; - if (SDL_ConvertAudio(&stream->cvt_before_resampling) == -1) { - return -1; /* uhoh! */ - } - buflen = stream->cvt_before_resampling.len_cvt; - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: After initial conversion we have %d bytes\n", buflen); -#endif - } - - if (stream->dst_rate != stream->src_rate) { - /* save off some samples at the end; they are used for padding now so - the resampler is coherent and then used at the start of the next - put operation. Prepend last put operation's padding, too. */ - - /* prepend prior put's padding. :P */ - if (paddingbytes) { - SDL_memcpy(workbuf, stream->resampler_padding, paddingbytes); - buflen += paddingbytes; - } - - /* save off the data at the end for the next run. */ - SDL_memcpy(stream->resampler_padding, workbuf + (buflen - neededpaddingbytes), neededpaddingbytes); - - resamplebuf = workbuf + buflen; /* skip to second piece of workbuf. */ - SDL_assert(buflen >= neededpaddingbytes); - if (buflen > neededpaddingbytes) { - buflen = stream->resampler_func(stream, workbuf, buflen - neededpaddingbytes, resamplebuf, resamplebuflen); - } else { - buflen = 0; - } - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: After resampling we have %d bytes\n", buflen); -#endif - } - - if (stream->cvt_after_resampling.needed && (buflen > 0)) { - stream->cvt_after_resampling.buf = resamplebuf; - stream->cvt_after_resampling.len = buflen; - if (SDL_ConvertAudio(&stream->cvt_after_resampling) == -1) { - return -1; /* uhoh! */ - } - buflen = stream->cvt_after_resampling.len_cvt; - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: After final conversion we have %d bytes\n", buflen); -#endif - } - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: Final output is %d bytes\n", buflen); -#endif - - if (maxputbytes) { - const int maxbytes = *maxputbytes; - if (buflen > maxbytes) { - buflen = maxbytes; - } - *maxputbytes -= buflen; - } - - /* resamplebuf holds the final output, even if we didn't resample. */ - return buflen ? SDL_WriteToDataQueue(stream->queue, resamplebuf, buflen) : 0; + return retval; } int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) { - /* !!! FIXME: several converters can take advantage of SIMD, but only - !!! FIXME: if the data is aligned to 16 bytes. EnsureStreamBufferSize() - !!! FIXME: guarantees the buffer will align, but the - !!! FIXME: converters will iterate over the data backwards if - !!! FIXME: the output grows, and this means we won't align if buflen - !!! FIXME: isn't a multiple of 16. In these cases, we should chop off - !!! FIXME: a few samples at the end and convert them separately. */ + int retval; #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: wants to put %d preconverted bytes\n", buflen); + SDL_Log("AUDIOSTREAM: wants to put %d preconverted bytes", len); #endif if (stream == NULL) { return SDL_InvalidParamError("stream"); - } - if (buf == NULL) { + } else if (buf == NULL) { return SDL_InvalidParamError("buf"); - } - if (len == 0) { + } else if (len == 0) { return 0; /* nothing to do. */ } + + SDL_LockMutex(stream->lock); + if ((len % stream->src_sample_frame_size) != 0) { + SDL_UnlockMutex(stream->lock); return SDL_SetError("Can't add partial sample frames"); } - if (!stream->cvt_before_resampling.needed && - (stream->dst_rate == stream->src_rate) && - !stream->cvt_after_resampling.needed) { -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: no conversion needed at all, queueing %d bytes.\n", len); -#endif - return SDL_WriteToDataQueue(stream->queue, buf, len); - } + /* just queue the data, we convert/resample when dequeueing. */ + retval = SDL_WriteToDataQueue(stream->queue, buf, len); + stream->flushed = SDL_FALSE; + SDL_UnlockMutex(stream->lock); - while (len > 0) { - int amount; - - /* If we don't have a staging buffer or we're given enough data that - we don't need to store it for later, skip the staging process. - */ - if (!stream->staging_buffer_filled && len >= stream->staging_buffer_size) { - return SDL_PutAudioStreamInternal(stream, buf, len, NULL); - } - - /* If there's not enough data to fill the staging buffer, just save it */ - if ((stream->staging_buffer_filled + len) < stream->staging_buffer_size) { - SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, len); - stream->staging_buffer_filled += len; - return 0; - } - - /* Fill the staging buffer, process it, and continue */ - amount = (stream->staging_buffer_size - stream->staging_buffer_filled); - SDL_assert(amount > 0); - SDL_memcpy(stream->staging_buffer + stream->staging_buffer_filled, buf, amount); - stream->staging_buffer_filled = 0; - if (SDL_PutAudioStreamInternal(stream, stream->staging_buffer, stream->staging_buffer_size, NULL) < 0) { - return -1; - } - buf = (void *)((Uint8 *)buf + amount); - len -= amount; - } - return 0; + return retval; } + int SDL_FlushAudioStream(SDL_AudioStream *stream) { if (stream == NULL) { return SDL_InvalidParamError("stream"); } -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: flushing! staging_buffer_filled=%d bytes\n", stream->staging_buffer_filled); -#endif - - /* shouldn't use a staging buffer if we're not resampling. */ - SDL_assert((stream->dst_rate != stream->src_rate) || (stream->staging_buffer_filled == 0)); - - if (stream->staging_buffer_filled > 0) { - /* push the staging buffer + silence. We need to flush out not just - the staging buffer, but the piece that the stream was saving off - for right-side resampler padding. */ - const SDL_bool first_run = stream->first_run; - const int filled = stream->staging_buffer_filled; - int actual_input_frames = filled / stream->src_sample_frame_size; - if (!first_run) { - actual_input_frames += stream->resampler_padding_samples / stream->pre_resample_channels; - } - - if (actual_input_frames > 0) { /* don't bother if nothing to flush. */ - /* This is how many bytes we're expecting without silence appended. */ - int flush_remaining = ((int)SDL_ceil(actual_input_frames * stream->rate_incr)) * stream->dst_sample_frame_size; - -#if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: flushing with padding to get max %d bytes!\n", flush_remaining); -#endif - - SDL_memset(stream->staging_buffer + filled, '\0', stream->staging_buffer_size - filled); - if (SDL_PutAudioStreamInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) { - return -1; - } - - /* we have flushed out (or initially filled) the pending right-side - resampler padding, but we need to push more silence to guarantee - the staging buffer is fully flushed out, too. */ - SDL_memset(stream->staging_buffer, '\0', filled); - if (SDL_PutAudioStreamInternal(stream, stream->staging_buffer, stream->staging_buffer_size, &flush_remaining) < 0) { - return -1; - } - } - } - - stream->staging_buffer_filled = 0; - stream->first_run = SDL_TRUE; + SDL_LockMutex(stream->lock); + stream->flushed = SDL_TRUE; + SDL_UnlockMutex(stream->lock); return 0; } -/* get converted/resampled data from the stream */ -int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *buf, int len) +/* this does not save the previous contents of stream->work_buffer. It's a work buffer!! + The returned buffer is aligned/padded for use with SIMD instructions. */ +static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen) { + Uint8 *ptr; + + if (stream->work_buffer_allocation >= newlen) { + return stream->work_buffer; + } + + ptr = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), newlen); + if (ptr == NULL) { + SDL_OutOfMemory(); + return NULL; /* previous work buffer is still valid! */ + } + + SDL_aligned_free(stream->work_buffer); + stream->work_buffer = ptr; + stream->work_buffer_allocation = newlen; + return ptr; +} + +static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int len) +{ + int workbuf_frames = len / stream->src_sample_frame_size; /* start with requested sample frames */ + int workbuflen = len; + int inputlen; + + inputlen = workbuf_frames * stream->dst_sample_frame_size; + if (inputlen > workbuflen) { + workbuflen = inputlen; + } + + inputlen = workbuf_frames * stream->pre_resample_channels * sizeof (float); + if (inputlen > workbuflen) { + workbuflen = inputlen; + } + + if (stream->dst_rate != stream->src_rate) { + /* calculate requested sample frames needed before resampling. Use a Uint64 so the multiplication doesn't overflow. */ + const int input_frames = ((int) ((((Uint64) workbuf_frames) * stream->src_rate) / stream->dst_rate)); + inputlen = input_frames * stream->src_sample_frame_size; + if (inputlen > workbuflen) { + workbuflen = inputlen; + } + /* Calculate space needed to move to format/channels used for resampling stage. */ + inputlen = input_frames * stream->pre_resample_channels * sizeof (float); + if (inputlen > workbuflen) { + workbuflen = inputlen; + } + /* Calculate space needed after resample (which lives in a second copy in the same buffer). */ + inputlen += workbuf_frames * stream->pre_resample_channels * sizeof (float); + if (inputlen > workbuflen) { + workbuflen = inputlen; + } + } + + return workbuflen; +} + +/* You must hold stream->lock and validate your parameters before calling this! */ +static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int len) +{ + const int max_available = SDL_GetAudioStreamAvailable(stream); + const SDL_AudioFormat src_format = stream->src_format; + const int src_channels = stream->src_channels; + const int src_rate = stream->src_rate; + const int src_sample_frame_size = stream->src_sample_frame_size; + const SDL_AudioFormat dst_format = stream->dst_format; + const int dst_channels = stream->dst_channels; + const int dst_rate = stream->dst_rate; + const int dst_sample_frame_size = stream->dst_sample_frame_size; + const int pre_resample_channels = stream->pre_resample_channels; + const int resampler_padding_frames = stream->resampler_padding_frames; + const int history_buffer_frames = stream->history_buffer_frames; + int future_buffer_filled_frames = stream->future_buffer_filled_frames; + Uint8 *future_buffer = stream->future_buffer; + Uint8 *history_buffer = stream->history_buffer; + float *resample_outbuf; + int input_frames; + int output_frames; + Uint8 *workbuf; + int workbuflen; + int workbuf_frames; + int br; + #if DEBUG_AUDIOSTREAM - SDL_Log("AUDIOSTREAM: want to get %d converted bytes\n", len); + SDL_Log("AUDIOSTREAM: asking for an output chunk of %d bytes.", len); +#endif + + if (len > max_available) { + len = max_available; + } + + output_frames = len / dst_sample_frame_size; + + if (output_frames == 0) { + return 0; /* nothing to do. */ + } + + /* !!! FIXME: this could be less aggressive about allocation, if we decide the necessary size at each stage and select the maximum required. */ + workbuflen = CalculateAudioStreamWorkBufSize(stream, len); + workbuf = EnsureStreamWorkBufferSize(stream, workbuflen); + if (!workbuf) { + return -1; + } + + /* figure out how much data we need to fulfill the request. */ + input_frames = len / dst_sample_frame_size; /* total sample frames caller wants */ + if (dst_rate != src_rate) { + /* calculate requested sample frames needed before resampling. Use a Uint64 so the multiplication doesn't overflow. */ + input_frames = (int) ((((Uint64) input_frames) * src_rate) / dst_rate); + if (input_frames == 0) { + return 0; /* if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame. */ + } + } + + workbuf_frames = 0; /* no input has been moved to the workbuf yet. */ + + /* move any previous right-padding to the start of the buffer to convert, as those would have been the next samples from the queue ("the future buffer"). */ + if (future_buffer_filled_frames) { + const int cpyframes = SDL_min(input_frames, future_buffer_filled_frames); + const int cpy = cpyframes * src_sample_frame_size; + SDL_memcpy(workbuf, future_buffer, cpy); + workbuf_frames = cpyframes; + if (future_buffer_filled_frames == cpyframes) { + stream->future_buffer_filled_frames = future_buffer_filled_frames = 0; + } else { /* slide any remaining bytes to the start of the padding buffer, if this was a small request. */ + SDL_memmove(future_buffer, future_buffer + cpy, (future_buffer_filled_frames - cpyframes) * src_sample_frame_size); + future_buffer_filled_frames -= cpyframes; + stream->future_buffer_filled_frames = future_buffer_filled_frames; + } + } + + /* we either consumed all the future buffer or we don't need to read more from the queue. If this assert fails, we will have data in the wrong order in the future buffer when we top it off. */ + SDL_assert((future_buffer_filled_frames == 0) || (workbuf_frames == input_frames)); + + /* now read unconverted data from the queue into the work buffer to fulfill the request. */ + if (input_frames > workbuf_frames) { /* need more data? */ + const int workbufpos = workbuf_frames * src_sample_frame_size; + const int request_bytes = (input_frames - workbuf_frames) * src_sample_frame_size; + int read_frames; + SDL_assert((workbufpos + request_bytes) <= workbuflen); + br = (int) SDL_ReadFromDataQueue(stream->queue, workbuf + workbufpos, request_bytes); + read_frames = br / src_sample_frame_size; + workbuf_frames += read_frames; + input_frames = workbuf_frames; /* what we actually have to work with */ + } + + /* for some resamples, we need to fill up the future buffer, too, to use as right padding. */ + if (future_buffer_filled_frames < resampler_padding_frames) { + const int cpyframes = resampler_padding_frames - future_buffer_filled_frames; + const int cpy = cpyframes * src_sample_frame_size; + int brframes; + br = (int) SDL_ReadFromDataQueue(stream->queue, future_buffer + (future_buffer_filled_frames * src_sample_frame_size), cpy); + brframes = br / src_sample_frame_size; + future_buffer_filled_frames += brframes; + stream->future_buffer_filled_frames = future_buffer_filled_frames; + if (br < cpy) { /* we couldn't fill the future buffer with enough padding! */ + if (stream->flushed) { /* that's okay, we're flushing, just silence the still-needed padding. */ + SDL_memset(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), GetMemsetSilenceValue(src_format), cpy - br); + } else { /* Drastic measures: steal from the work buffer! */ + const int stealcpyframes = SDL_min(workbuf_frames, cpyframes - brframes); + const int stealcpy = stealcpyframes * src_sample_frame_size; + SDL_memcpy(future_buffer + (future_buffer_filled_frames * src_sample_frame_size), workbuf + ((workbuf_frames - stealcpyframes) * src_sample_frame_size), stealcpy); + workbuf_frames -= stealcpyframes; + input_frames = workbuf_frames; /* what we actually have to work with, now */ + future_buffer_filled_frames += stealcpyframes; + SDL_assert(future_buffer_filled_frames <= resampler_padding_frames); + } + } + } + + /* Now, the work buffer has enough sample frames to fulfill the request (or all the frames available if not), and the future buffer is loaded if necessary. */ + + /* If we have resampling padding buffers, convert the current history and future buffers to float32. */ + if (resampler_padding_frames > 0) { + const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size; + const int resampler_padding_bytes = resampler_padding_frames * src_sample_frame_size; + SDL_assert(src_rate != dst_rate); + SDL_assert(history_buffer_bytes >= resampler_padding_bytes); + ConvertAudio(resampler_padding_frames, history_buffer + (history_buffer_bytes - resampler_padding_bytes), src_format, src_channels, stream->left_padding, AUDIO_F32, pre_resample_channels); + ConvertAudio(resampler_padding_frames, future_buffer, src_format, src_channels, stream->right_padding, AUDIO_F32, pre_resample_channels); + } + + /* slide in new data to the history buffer, shuffling out the oldest, for the next run, since we've already updated left_padding with current data. */ + { + const int history_buffer_bytes = history_buffer_frames * src_sample_frame_size; + const int request_bytes = input_frames * src_sample_frame_size; + if (history_buffer_frames > input_frames) { + const int preserve_bytes = history_buffer_bytes - request_bytes; + SDL_memmove(history_buffer, history_buffer + request_bytes, preserve_bytes); + SDL_memcpy(history_buffer + preserve_bytes, workbuf, request_bytes); + } else { /* are we just replacing the whole thing instead? */ + SDL_memcpy(history_buffer, (workbuf + request_bytes) - history_buffer_bytes, history_buffer_bytes); + } + } + + /* Not resampling? It's an easy conversion (and maybe not even that!) */ + if (src_rate == dst_rate) { + SDL_assert(resampler_padding_frames == 0); + ConvertAudio(input_frames, workbuf, src_format, src_channels, buf, dst_format, dst_channels); + return input_frames * dst_sample_frame_size; + } + + /* Resampling! get the work buffer to float32 format, etc, in-place. */ + ConvertAudio(input_frames, workbuf, src_format, src_channels, workbuf, AUDIO_F32, pre_resample_channels); + + if ((dst_format == AUDIO_F32) && (dst_channels == pre_resample_channels)) { + resample_outbuf = (float *) buf; + } else { + const int input_bytes = input_frames * pre_resample_channels * sizeof (float); + resample_outbuf = (float *) (workbuf + input_bytes); + } + + ResampleAudio(pre_resample_channels, src_rate, dst_rate, + stream->left_padding, stream->right_padding, + (const float *) workbuf, input_frames, + resample_outbuf, output_frames); + + /* Get us to the final format! */ + ConvertAudio(output_frames, resample_outbuf, AUDIO_F32, src_channels, buf, dst_format, dst_channels); + return (int) (output_frames * dst_sample_frame_size); +} + +/* get converted/resampled data from the stream */ +int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len) +{ + Uint8 *buf = (Uint8 *) voidbuf; + int retval = 0; + +#if DEBUG_AUDIOSTREAM + SDL_Log("AUDIOSTREAM: want to get %d converted bytes", len); #endif if (stream == NULL) { return SDL_InvalidParamError("stream"); - } - if (buf == NULL) { + } else if (buf == NULL) { return SDL_InvalidParamError("buf"); - } - if (len <= 0) { + } else if (len < 0) { + return SDL_InvalidParamError("len"); + } else if (len == 0) { return 0; /* nothing to do. */ } - if ((len % stream->dst_sample_frame_size) != 0) { - return SDL_SetError("Can't request partial sample frames"); + + SDL_LockMutex(stream->lock); + + len -= len % stream->dst_sample_frame_size; /* chop off any fractional sample frame. */ + + /* we convert in chunks, so we don't end up allocating a massive work buffer, etc. */ + while (len > 0) { /* didn't ask for a whole sample frame, nothing to do */ + const int chunk_size = 1024 * 1024; /* !!! FIXME: a megabyte might be overly-aggressive. */ + const int rc = GetAudioStreamDataInternal(stream, buf, SDL_min(len, chunk_size)); + + if (rc == -1) { + #if DEBUG_AUDIOSTREAM + SDL_Log("AUDIOSTREAM: output chunk ended up producing an error!"); + #endif + if (retval == 0) { + retval = -1; + } + break; + } else { + #if DEBUG_AUDIOSTREAM + SDL_Log("AUDIOSTREAM: output chunk ended up being %d bytes.", rc); + #endif + buf += rc; + len -= rc; + retval += rc; + if (rc < chunk_size) { + break; + } + } } - return (int)SDL_ReadFromDataQueue(stream->queue, buf, len); + SDL_UnlockMutex(stream->lock); + +#if DEBUG_AUDIOSTREAM + SDL_Log("AUDIOSTREAM: Final result was %d", retval); +#endif + + return retval; } /* number of converted/resampled bytes available */ int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) { - return stream ? (int)SDL_GetDataQueueSize(stream->queue) : 0; + const int max_int = 0x7FFFFFFF; /* !!! FIXME: This will blow up on weird processors. Is there an SDL_INT_MAX? */ + size_t count; + + if (!stream) { + return SDL_InvalidParamError("stream"); + } + + SDL_LockMutex(stream->lock); + + /* total bytes available in source format in data queue */ + count = SDL_GetDataQueueSize(stream->queue); + + /* total sample frames available in data queue */ + count /= stream->src_sample_frame_size; + count += stream->future_buffer_filled_frames; + + /* sample frames after resampling */ + if (stream->src_rate != stream->dst_rate) { + if (!stream->flushed) { + /* have to save some samples for padding. They aren't available until more data is added or the stream is flushed. */ + count = (count < ((size_t) stream->resampler_padding_frames)) ? 0 : (count - stream->resampler_padding_frames); + } + /* calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow. */ + count = (size_t) ((((Uint64) count) * stream->dst_rate) / stream->src_rate); + } + + /* convert from sample frames to bytes in destination format. */ + count *= stream->dst_sample_frame_size; + + SDL_UnlockMutex(stream->lock); + + /* if this overflows an int, just clamp it to a maximum. */ + return (count >= ((size_t) max_int)) ? max_int : ((int) count); } int SDL_ClearAudioStream(SDL_AudioStream *stream) { if (stream == NULL) { return SDL_InvalidParamError("stream"); - } else { - SDL_ClearDataQueue(stream->queue, (size_t)stream->packetlen * 2); - if (stream->reset_resampler_func) { - stream->reset_resampler_func(stream); - } - stream->first_run = SDL_TRUE; - stream->staging_buffer_filled = 0; - return 0; } + + SDL_LockMutex(stream->lock); + SDL_ClearDataQueue(stream->queue, (size_t)stream->packetlen * 2); + SDL_memset(stream->history_buffer, GetMemsetSilenceValue(stream->src_format), stream->history_buffer_frames * stream->src_channels * sizeof (float)); + stream->future_buffer_filled_frames = 0; + stream->flushed = SDL_FALSE; + SDL_UnlockMutex(stream->lock); + return 0; } -/* dispose of a stream */ void SDL_DestroyAudioStream(SDL_AudioStream *stream) { if (stream) { - if (stream->cleanup_resampler_func) { - stream->cleanup_resampler_func(stream); - } + /* do not destroy stream->lock! it's a copy of `stream->queue`'s mutex, so destroying the queue will handle it. */ SDL_DestroyDataQueue(stream->queue); - SDL_free(stream->staging_buffer); - SDL_free(stream->work_buffer_base); - SDL_free(stream->resampler_padding); + SDL_aligned_free(stream->work_buffer); + SDL_aligned_free(stream->history_buffer); + SDL_aligned_free(stream->future_buffer); + SDL_aligned_free(stream->left_padding); + SDL_aligned_free(stream->right_padding); SDL_free(stream); } } diff --git a/src/audio/SDL_audiocvt_c.h b/src/audio/SDL_audiocvt_c.h deleted file mode 100644 index 441249254..000000000 --- a/src/audio/SDL_audiocvt_c.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2023 Sam Lantinga - - 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; - diff --git a/src/audio/SDL_audiotypecvt.c b/src/audio/SDL_audiotypecvt.c index 48e0085d3..6946f76db 100644 --- a/src/audio/SDL_audiotypecvt.c +++ b/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; +/* 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; \ + } \ } - cvt->len_cvt *= 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); - } -} +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 -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; +/* 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); \ + } \ + } \ } - cvt->len_cvt *= 4; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); - } -} +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 -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 +#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 diff --git a/src/audio/SDL_mixer.c b/src/audio/SDL_mixer.c index e1d257d99..5cfc9b6b1 100644 --- a/src/audio/SDL_mixer.c +++ b/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) { diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index ab7ad8051..457afa5c9 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/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: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 55d74be9a..f62b70981 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 5f5beeecd..856d8abd3 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/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) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e679e1c7b..669646c5f 100644 --- a/test/CMakeLists.txt +++ b/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}) diff --git a/test/testaudiostreamdynamicresample.c b/test/testaudiostreamdynamicresample.c new file mode 100644 index 000000000..78dfe3b4f --- /dev/null +++ b/test/testaudiostreamdynamicresample.c @@ -0,0 +1,123 @@ +/* + Copyright (C) 1997-2023 Sam Lantinga + + 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 +#include +#include + +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; +} +