From 052b14eb654a18ed899325b03bc53fd1c62cde33 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 23 Jan 2023 08:24:49 +0100 Subject: [PATCH] Add SDL_ConvertAudioSamples() helper function --- WhatsNew.txt | 1 + docs/README-migration.md | 27 ++++++------ include/SDL3/SDL_audio.h | 31 ++++++++++++++ src/audio/SDL_audio.c | 70 +++++++++++++++++++++++++++++++ src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + test/testresample.c | 49 +++------------------- 8 files changed, 124 insertions(+), 57 deletions(-) diff --git a/WhatsNew.txt b/WhatsNew.txt index 093928bbb..08f6f323a 100644 --- a/WhatsNew.txt +++ b/WhatsNew.txt @@ -24,3 +24,4 @@ General: * Added SDL_aligned_alloc() and SDL_aligned_free() to allocate and free memory with a given alignment * Added SDL_GetRenderVSync() to get vsync of the given renderer * Added SDL_PlayAudioDevice() to start audio playback +* Added SDL_ConvertAudioSamples() to convert audio samples from one format to another diff --git a/docs/README-migration.md b/docs/README-migration.md index 0edbfb2fc..519e1fad8 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -54,29 +54,28 @@ SDL_PauseAudioDevice() is only used to pause audio playback. Use SDL_PlayAudioDe SDL_FreeWAV has been removed and calls can be replaced with SDL_free. -SDL_AudioCVT interface is removed, SDL_AudioStream can be used instead. +SDL_AudioCVT interface is removed, SDL_AudioStream interface or SDL_ConvertAudioSamples() helper function can be used. Code that used to look like this: ```c SDL_AudioCVT cvt; - SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, spec.format, cvtchans, cvtfreq); - cvt.len = len; - cvt.buf = (Uint8 *) SDL_malloc(len * cvt.len_mult); - SDL_memcpy(cvt.buf, data, len); + SDL_BuildAudioCVT(&cvt, src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); + cvt.len = src_len; + cvt.buf = (Uint8 *) SDL_malloc(src_len * cvt.len_mult); + SDL_memcpy(cvt.buf, src_data, src_len); SDL_ConvertAudio(&cvt); do_something(cvt.buf, cvt.len_cvt); ``` should be changed to: ```c - SDL_AudioStream *stream = SDL_CreateAudioStream(spec.format, spec.channels, spec.freq, spec.format, cvtchans, cvtfreq); - int src_samplesize = (SDL_AUDIO_BITSIZE(spec.format) / 8) * spec.channels; - int src_len = len & ~(src_samplesize - 1); // need to be rounded to samplesize - SDL_PutAudioStreamData(stream, data, src_len); - SDL_FlushAudioStream(stream); - int dst_len = expected_dst_len & ~(dst_samplesize - 1); // need to be rounded to samplesize - Uint8 *dst_buf = (Uint8 *)SDL_malloc(dst_len); - int real_dst_len = SDL_GetAudioStreamData(stream, dst_buf, dst_len); - do_something(dst_buf, real_dst_len); + Uint8 *dst_data = NULL; + int dst_len = 0; + if (SDL_ConvertAudioSamples(src_format, src_channels, src_rate, src_len, src_data + dst_format, dst_channels, dst_rate, &dst_len, &dst_data) < 0) { + /* error */ + } + do_something(dst_data, dst_len); + SDL_free(dst_data); ``` diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h index fe7bc7b61..28500ea16 100644 --- a/include/SDL3/SDL_audio.h +++ b/include/SDL3/SDL_audio.h @@ -1113,6 +1113,37 @@ extern DECLSPEC void SDLCALL SDL_UnlockAudioDevice(SDL_AudioDeviceID dev); */ extern DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID dev); +/** + * Convert some audio data of one format to another format. + * + * \param src_format The format of the source audio + * \param src_channels The number of channels of the source audio + * \param src_rate The sampling rate of the source audio + * \param src_len The len of src_data + * \param src_data The audio data to be converted + * \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 + * \param dst_len Will be filled with the len of dst_data + * \param dst_data Will be filled with a pointer to converted audio data, which should be freed with SDL_free(). + * + * \returns 0 on success or a negative error code on failure. On error, *dst_data will be NULL and so not allocated. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_CreateAudioStream + */ +extern DECLSPEC int SDLCALL SDL_ConvertAudioSamples(SDL_AudioFormat src_format, + Uint8 src_channels, + int src_rate, + int src_len, + Uint8 *src_data, + SDL_AudioFormat dst_format, + Uint8 dst_channels, + int dst_rate, + int *dst_len, + Uint8 **dst_data); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 08a1160cb..877895fc6 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1662,3 +1662,73 @@ void SDL_CalculateAudioSpec(SDL_AudioSpec *spec) spec->size *= spec->samples; } +int SDL_ConvertAudioSamples( + SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, int src_len, Uint8 *src_data, + SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate, int *dst_len, Uint8 **dst_data) +{ + int ret = -1; + SDL_AudioStream *stream = NULL; + + int src_samplesize, dst_samplesize; + int real_dst_len; + + + if (src_len < 0) { + return SDL_InvalidParamError("src_len"); + } + if (src_data == NULL) { + return SDL_InvalidParamError("src_data"); + } + if (dst_len == NULL) { + return SDL_InvalidParamError("dst_len"); + } + if (dst_data == NULL) { + return SDL_InvalidParamError("dst_data"); + } + + *dst_len = 0; + *dst_data = NULL; + + stream = SDL_CreateAudioStream(src_format, src_channels, src_rate, dst_format, dst_channels, dst_rate); + if (stream == NULL) { + goto end; + } + + src_samplesize = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; + dst_samplesize = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels; + + src_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); + } + + *dst_len &= ~(dst_samplesize - 1); + *dst_data = (Uint8 *)SDL_malloc(*dst_len); + if (*dst_data == NULL) { + goto end; + } + + if (SDL_PutAudioStreamData(stream, src_data, src_len) < 0 || + SDL_FlushAudioStream(stream) < 0) { + goto end; + } + + real_dst_len = SDL_GetAudioStreamData(stream, *dst_data, *dst_len); + if (real_dst_len < 0) { + goto end; + } + + *dst_len = real_dst_len; + ret = 0; + +end: + if (ret != 0) { + SDL_free(*dst_data); + *dst_len = 0; + *dst_data = NULL; + } + SDL_DestroyAudioStream(stream); + return ret; +} diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index b9d980ae2..74e6d25ef 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -841,6 +841,7 @@ SDL3_0.0.0 { SDL_PlayAudioDevice; SDL_aligned_alloc; SDL_aligned_free; + SDL_ConvertAudioSamples; # 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 b74093cde..5c82e562d 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -868,3 +868,4 @@ #define SDL_PlayAudioDevice SDL_PlayAudioDevice_REAL #define SDL_aligned_alloc SDL_aligned_alloc_REAL #define SDL_aligned_free SDL_aligned_free_REAL +#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8e9b300e9..2128ef2c3 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -913,3 +913,4 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderVSync,(SDL_Renderer *a, int *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_PlayAudioDevice,(SDL_AudioDeviceID a),(a),) SDL_DYNAPI_PROC(void*,SDL_aligned_alloc,(size_t a, size_t b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_aligned_free,(void *a),(a),) +SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, Uint8 b, int c, int d, Uint8 *e, SDL_AudioFormat f, Uint8 g, int h, int *i, Uint8 **j),(a,b,c,d,e,f,g,h,i,j),return) diff --git a/test/testresample.c b/test/testresample.c index 6772f2694..15eb76c49 100644 --- a/test/testresample.c +++ b/test/testresample.c @@ -26,8 +26,7 @@ int main(int argc, char **argv) int blockalign = 0; int avgbytes = 0; SDL_RWops *io = NULL; - int src_samplesize, dst_samplesize; - int src_len, dst_len, real_dst_len; + int dst_len; int ret = 0; /* Enable standard application logging */ @@ -54,54 +53,18 @@ int main(int argc, char **argv) goto end; } - - stream = SDL_CreateAudioStream(spec.format, spec.channels, spec.freq, spec.format, cvtchans, cvtfreq); - if (stream == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to build audio stream: %s\n", SDL_GetError()); + if (SDL_ConvertAudioSamples(spec.format, spec.channels, spec.freq, len, data, + spec.format, cvtchans, cvtfreq, &dst_len, &dst_buf) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "failed to convert samples: %s\n", SDL_GetError()); ret = 4; goto end; } - src_samplesize = (SDL_AUDIO_BITSIZE(spec.format) / 8) * spec.channels; - dst_samplesize = (SDL_AUDIO_BITSIZE(spec.format) / 8) * cvtchans; - - - src_len = len & ~(src_samplesize - 1); - dst_len = dst_samplesize * (src_len / src_samplesize); - if (spec.freq < cvtfreq) { - const double mult = ((double)cvtfreq) / ((double)spec.freq); - dst_len *= (int) SDL_ceil(mult); - } - - dst_len = dst_len & ~(dst_samplesize - 1); - dst_buf = (Uint8 *)SDL_malloc(dst_len); - if (dst_buf == NULL) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Out of memory.\n"); - ret = 5; - goto end; - } - - /* Run the audio converter */ - if (SDL_PutAudioStreamData(stream, data, src_len) < 0 || - SDL_FlushAudioStream(stream) < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Conversion failed: %s\n", SDL_GetError()); - ret = 6; - goto end; - } - - real_dst_len = SDL_GetAudioStreamData(stream, dst_buf, dst_len); - if (real_dst_len < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Conversion failed: %s\n", SDL_GetError()); - ret = 7; - goto end; - } - dst_len = real_dst_len; - /* write out a WAV header... */ io = SDL_RWFromFile(argv[2], "wb"); if (io == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "fopen('%s') failed: %s\n", argv[2], SDL_GetError()); - ret = 8; + ret = 5; goto end; } @@ -126,7 +89,7 @@ int main(int argc, char **argv) if (SDL_RWclose(io) == -1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "fclose('%s') failed: %s\n", argv[2], SDL_GetError()); - ret = 9; + ret = 6; goto end; }