From 17b5c20b39797ea4f470b068a93f758cc175cd09 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Mon, 16 Jan 2023 09:52:02 +0100
Subject: [PATCH] Re-add SDL_AudioCVT interface, based on SDL_AudioStream
---
src/sdl2_compat.c | 223 ++++++++++++++++++++++++++++++++++++-
src/sdl2_compat.h | 56 ++++++++++
src/sdl3_include_wrapper.h | 10 --
src/sdl3_syms.h | 2 -
4 files changed, 274 insertions(+), 17 deletions(-)
diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index 5148917..46dfe37 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -1111,7 +1111,7 @@ SDL_GetRevisionNumber(void)
}
-DECLSPEC void SDLCALL
+DECLSPEC int SDLCALL
SDL_SetError(const char *fmt, ...)
{
char ch;
@@ -1133,6 +1133,7 @@ SDL_SetError(const char *fmt, ...)
SDL3_SetError("%s", str);
SDL3_free(str);
}
+ return -1;
}
DECLSPEC int SDLCALL
@@ -2986,8 +2987,7 @@ SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
}
if (g_audio_id > 0) {
- SDL_SetError("Audio device is already opened");
- return -1;
+ return SDL3_SetError("Audio device is already opened");
}
if (obtained) {
@@ -3081,14 +3081,14 @@ SDL_LockAudioDevice(SDL_AudioDeviceID dev)
SDL3_LockAudioDevice(id);
}
-DECLSPEC void SDLCALL
+DECLSPEC void SDLCALL
SDL_UnlockAudioDevice(SDL_AudioDeviceID dev)
{
SDL_AudioDeviceID id = dev == 1 ? g_audio_id : dev;
SDL3_UnlockAudioDevice(id);
}
-DECLSPEC void SDLCALL
+DECLSPEC void SDLCALL
SDL_CloseAudioDevice(SDL_AudioDeviceID dev)
{
SDL_AudioDeviceID id = dev == 1 ? g_audio_id : dev;
@@ -3914,6 +3914,219 @@ DECLSPEC void SDLCALL SDL_SIMDFree(void *ptr)
}
+
+static SDL_bool SDL_IsSupportedAudioFormat(const SDL_AudioFormat fmt)
+{
+ switch (fmt) {
+ case AUDIO_U8:
+ case AUDIO_S8:
+ case AUDIO_U16LSB:
+ case AUDIO_S16LSB:
+ case AUDIO_U16MSB:
+ case AUDIO_S16MSB:
+ case AUDIO_S32LSB:
+ case AUDIO_S32MSB:
+ case AUDIO_F32LSB:
+ case AUDIO_F32MSB:
+ return SDL_TRUE; /* supported. */
+
+ default:
+ break;
+ }
+
+ return SDL_FALSE; /* unsupported. */
+}
+
+static SDL_bool SDL_IsSupportedChannelCount(const int channels)
+{
+ return ((channels >= 1) && (channels <= 8)) ? SDL_TRUE : SDL_FALSE;
+}
+
+
+typedef struct {
+ SDL_AudioFormat src_format;
+ Uint8 src_channels;
+ int src_rate;
+ SDL_AudioFormat dst_format;
+ Uint8 dst_channels;
+ int dst_rate;
+} AudioParam;
+
+#define RESAMPLER_BITS_PER_SAMPLE 16
+#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))
+
+
+DECLSPEC int SDLCALL 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)
+{
+ /* 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);
+#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);
+
+ /* Use the filters[] to store some data ... */
+ {
+ AudioParam ap;
+ ap.src_format = src_format;
+ ap.src_channels = src_channels;
+ ap.src_rate = src_rate;
+ ap.dst_format = dst_format;
+ ap.dst_channels = dst_channels;
+ ap.dst_rate = dst_rate;
+
+ /* Store at the end of filters[], aligned */
+ SDL_memcpy(
+ (Uint8 *)&cvt->filters[SDL_AUDIOCVT_MAX_FILTERS + 1] - (sizeof(AudioParam) & ~3),
+ &ap,
+ sizeof(ap));
+
+ cvt->filters[0] = NULL;
+ cvt->needed = 1;
+ if (src_format == dst_format && src_rate == dst_rate && src_channels == dst_channels) {
+ cvt->needed = 0;
+ }
+
+ if (src_rate < dst_rate) {
+ const int mult = (dst_rate / src_rate);
+ cvt->len_mult *= mult;
+ cvt->len_ratio *= mult;
+ } else {
+ const int div = (src_rate / dst_rate);
+ cvt->len_ratio /= div;
+ }
+
+ if (src_channels < dst_channels) {
+ cvt->len_mult = ((cvt->len_mult * dst_channels) + (src_channels - 1)) / src_channels;
+ }
+ }
+
+ return cvt->needed;
+}
+
+
+DECLSPEC int SDLCALL SDL_ConvertAudio(SDL_AudioCVT *cvt)
+{
+
+ SDL_AudioStream *stream;
+ SDL_AudioFormat src_format, dst_format;
+ int src_channels, src_rate;
+ int dst_channels, dst_rate;
+
+ int src_len, dst_len, real_dst_len;
+ int src_samplesize, dst_samplesize;
+
+ /* Sanity check target pointer */
+ if (cvt == NULL) {
+ return SDL_InvalidParamError("cvt");
+ }
+
+ {
+ AudioParam ap;
+
+ /* Fetch from the end of filters[], aligned */
+ SDL_memcpy(
+ &ap,
+ (Uint8 *)&cvt->filters[SDL_AUDIOCVT_MAX_FILTERS + 1] - (sizeof(AudioParam) & ~3),
+ sizeof(ap));
+
+ src_format = ap.dst_format;
+ src_channels = ap.src_channels;
+ src_rate = ap.src_rate;
+ dst_format = ap.dst_format;
+ dst_channels = ap.dst_channels;
+ dst_rate = ap.dst_rate;
+ }
+
+ stream = SDL3_CreateAudioStream(src_format, src_channels, src_rate,
+ dst_format, dst_channels, dst_rate);
+ if (stream == NULL) {
+ goto failure;
+ }
+
+ src_samplesize = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels;
+ dst_samplesize = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels;
+
+ src_len = cvt->len & ~(src_samplesize - 1);
+ dst_len = dst_samplesize * (src_len / src_samplesize);
+ if (src_rate < dst_rate) {
+ const double mult = ((double)dst_rate) / ((double)src_rate);
+ dst_len *= (int) SDL_ceil(mult);
+ }
+
+ /* Run the audio converter */
+ if (SDL3_PutAudioStreamData(stream, cvt->buf, src_len) < 0 ||
+ SDL3_FlushAudioStream(stream) < 0) {
+ goto failure;
+ }
+
+ dst_len = SDL_min(dst_len, cvt->len * cvt->len_mult);
+ dst_len = dst_len & ~(dst_samplesize - 1);
+
+ /* Get back in the same buffer */
+ real_dst_len = SDL3_GetAudioStreamData(stream, cvt->buf, dst_len);
+ if (real_dst_len < 0) {
+ goto failure;
+ }
+
+ cvt->len_cvt = real_dst_len;
+
+ SDL3_DestroyAudioStream(stream);
+ return 0;
+
+failure:
+ SDL3_DestroyAudioStream(stream);
+ return -1;
+}
+
+
#ifdef __cplusplus
}
#endif
diff --git a/src/sdl2_compat.h b/src/sdl2_compat.h
index 6097e7c..a276bd0 100644
--- a/src/sdl2_compat.h
+++ b/src/sdl2_compat.h
@@ -35,4 +35,60 @@ typedef Sint32 SDL2_SensorID; /* this became unsigned in SDL3, but we'll just h
typedef Sint64 SDL2_GestureID;
+
+struct SDL_AudioCVT;
+typedef void (SDLCALL * SDL_AudioFilter) (struct SDL_AudioCVT * cvt,
+ SDL_AudioFormat format);
+
+/**
+ * \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;
+
+
+
+
#endif /* sdl2_compat_h */
diff --git a/src/sdl3_include_wrapper.h b/src/sdl3_include_wrapper.h
index 9260720..f697e49 100644
--- a/src/sdl3_include_wrapper.h
+++ b/src/sdl3_include_wrapper.h
@@ -44,8 +44,6 @@
#define SDL_GetAudioDeviceStatus IGNORE_THIS_VERSION_OF_SDL_GetAudioDeviceStatus
#define SDL_PauseAudioDevice IGNORE_THIS_VERSION_OF_SDL_PauseAudioDevice
#define SDL_LoadWAV_RW IGNORE_THIS_VERSION_OF_SDL_LoadWAV_RW
-#define SDL_BuildAudioCVT IGNORE_THIS_VERSION_OF_SDL_BuildAudioCVT
-#define SDL_ConvertAudio IGNORE_THIS_VERSION_OF_SDL_ConvertAudio
#define SDL_CreateAudioStream IGNORE_THIS_VERSION_OF_SDL_CreateAudioStream
#define SDL_PutAudioStreamData IGNORE_THIS_VERSION_OF_SDL_PutAudioStreamData
#define SDL_GetAudioStreamAvailable IGNORE_THIS_VERSION_OF_SDL_GetAudioStreamAvailable
@@ -986,14 +984,6 @@ typedef void (__cdecl *pfnSDL_CurrentEndThread) (unsigned);
#undef SDL_LoadWAV_RW
#endif
-#ifdef SDL_BuildAudioCVT
-#undef SDL_BuildAudioCVT
-#endif
-
-#ifdef SDL_ConvertAudio
-#undef SDL_ConvertAudio
-#endif
-
#ifdef SDL_CreateAudioStream
#undef SDL_CreateAudioStream
#endif
diff --git a/src/sdl3_syms.h b/src/sdl3_syms.h
index f578a99..708f8fb 100644
--- a/src/sdl3_syms.h
+++ b/src/sdl3_syms.h
@@ -107,8 +107,6 @@ SDL3_SYM(SDL_AudioStatus,GetAudioDeviceStatus,(SDL_AudioDeviceID a),(a),return)
SDL3_SYM(void,PlayAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL3_SYM(void,PauseAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL3_SYM(SDL_AudioSpec*,LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
-SDL3_SYM_PASSTHROUGH(int,BuildAudioCVT,(SDL_AudioCVT *a, SDL_AudioFormat b, Uint8 c, int d, SDL_AudioFormat e, Uint8 f, int g),(a,b,c,d,e,f,g),return)
-SDL3_SYM_PASSTHROUGH(int,ConvertAudio,(SDL_AudioCVT *a),(a),return)
SDL3_SYM_PASSTHROUGH(void,MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),)
SDL3_SYM(void,LockAudioDevice,(SDL_AudioDeviceID a),(a),)
SDL3_SYM(void,UnlockAudioDevice,(SDL_AudioDeviceID a),(a),)