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!
This commit is contained in:
Ryan C. Gordon 2023-04-01 22:29:30 -04:00
parent 44bec9c01c
commit e5a6c24c82
20 changed files with 1434 additions and 2369 deletions

View File

@ -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)

View File

@ -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(" {");

View File

@ -240,53 +240,6 @@ macro(CheckSNDIO)
endif()
endmacro()
# Requires:
# - SDL_LIBSAMPLERATE
# Optional:
# - SDL_LIBSAMPLERATE_SHARED opt
# - HAVE_SDL_LOADSO opt
macro(CheckLibSampleRate)
if(SDL_LIBSAMPLERATE)
find_package(SampleRate QUIET)
if(SampleRate_FOUND AND TARGET SampleRate::samplerate)
set(HAVE_LIBSAMPLERATE TRUE)
if(SDL_LIBSAMPLERATE_SHARED)
target_include_directories(sdl-build-options INTERFACE $<TARGET_PROPERTY:SampleRate::samplerate,INTERFACE_INCLUDE_DIRECTORIES>)
if(NOT HAVE_SDL_LOADSO)
message_warn("You must have SDL_LoadObject() support for dynamic libsamplerate loading")
else()
get_property(_samplerate_type TARGET SampleRate::samplerate PROPERTY TYPE)
if(_samplerate_type STREQUAL "SHARED_LIBRARY")
set(HAVE_LIBSAMPLERATE_SHARED TRUE)
if(WIN32)
set(SDL_LIBSAMPLERATE_DYNAMIC "\"$<TARGET_FILE_NAME:SampleRate::samplerate>\"")
else()
set(SDL_LIBSAMPLERATE_DYNAMIC "\"$<TARGET_SONAME_FILE_NAME:SampleRate::samplerate>\"")
endif()
endif()
endif()
else()
target_link_libraries(sdl-build-options INTERFACE SampleRate::samplerate)
endif()
else()
check_include_file(samplerate.h HAVE_LIBSAMPLERATE_H)
if(HAVE_LIBSAMPLERATE_H)
set(HAVE_LIBSAMPLERATE TRUE)
if(SDL_LIBSAMPLERATE_SHARED AND NOT HAVE_SDL_LOADSO)
message_warn("You must have SDL_LoadObject() support for dynamic libsamplerate loading")
endif()
FindLibraryAndSONAME("samplerate")
if(SDL_LIBSAMPLERATE_SHARED AND SAMPLERATE_LIB AND HAVE_SDL_LOADSO)
set(SDL_LIBSAMPLERATE_DYNAMIC "\"${SAMPLERATE_LIB_SONAME}\"")
set(HAVE_LIBSAMPLERATE_SHARED TRUE)
else()
list(APPEND SDL_EXTRA_LIBS samplerate)
endif()
endif()
endif()
endif()
endmacro()
# Requires:
# - n/a
# Optional:

View File

@ -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

View File

@ -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()

View File

@ -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.
*

View File

@ -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
*/

View File

@ -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@

View File

@ -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

View File

@ -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.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +0,0 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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;

View File

@ -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

View File

@ -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)
{

View File

@ -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: *;
};

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

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