diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h index c1eeda2cde..7b76a85de1 100644 --- a/include/SDL3/SDL_audio.h +++ b/include/SDL3/SDL_audio.h @@ -858,6 +858,39 @@ extern DECLSPEC int SDLCALL SDL_GetAudioStreamData(SDL_AudioStream *stream, void */ extern DECLSPEC int SDLCALL SDL_GetAudioStreamAvailable(SDL_AudioStream *stream); + +/** + * Get the number of sample frames currently queued. + * + * Since audio streams can change their input format at any time, even if there + * is still data queued in a different format, this reports the queued _sample + * frames_, so if you queue two stereo samples in float32 format and then + * queue five mono samples in Sint16 format, this will return 6. + * + * Queued data is not converted until it is consumed by SDL_GetAudioStreamData, + * so this value should be representative of the exact data that was put into + * the stream. + * + * 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 sample frames queued. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_PutAudioStreamData + * \sa SDL_GetAudioStreamData + * \sa SDL_ClearAudioStream + */ +extern DECLSPEC int SDLCALL SDL_GetAudioStreamQueued(SDL_AudioStream *stream); + + /** * Tell the stream that you're done sending data, and anything being buffered * should be converted/resampled and made available immediately. diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index eefc61101a..018701d3e8 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -657,9 +657,12 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len) retval = SDL_WriteToAudioQueue(stream->queue, &stream->src_spec, buf, len); } - if ((retval == 0) && stream->put_callback) { - const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available; - stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail); + if (retval == 0) { + stream->total_frames_queued += len / SDL_AUDIO_FRAMESIZE(stream->src_spec); + if (stream->put_callback) { + const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available; + stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail); + } } SDL_UnlockMutex(stream->lock); @@ -858,6 +861,8 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou SDL_assert(!"Not enough data in queue (read)"); } + stream->total_frames_queued -= output_frames; + // Even if we aren't currently resampling, we always need to update the history buffer UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, NULL, 0); @@ -946,6 +951,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou if (SDL_ReadFromAudioQueue(stream->queue, input_buffer, input_bytes) != 0) { SDL_assert(!"Not enough data in queue (resample read)"); } + stream->total_frames_queued -= input_frames; // Update the history buffer and fill in the left padding UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, left_padding, padding_bytes); @@ -1083,7 +1089,7 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len) return total; } -// number of converted/resampled bytes available +// number of converted/resampled bytes available for output int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) { if (!stream) { @@ -1108,6 +1114,21 @@ int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) return (int) SDL_min(count, SDL_INT_MAX); } +// number of sample frames that are currently queued as input. +int SDL_GetAudioStreamQueued(SDL_AudioStream *stream) +{ + if (!stream) { + return SDL_InvalidParamError("stream"); + } + + SDL_LockMutex(stream->lock); + const Uint64 total = stream->total_frames_queued; + SDL_UnlockMutex(stream->lock); + + // if this overflows an int, just clamp it to a maximum. + return (int) SDL_min(total, SDL_INT_MAX); +} + int SDL_ClearAudioStream(SDL_AudioStream *stream) { if (stream == NULL) { @@ -1119,6 +1140,7 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream) SDL_ClearAudioQueue(stream->queue); SDL_zero(stream->input_spec); stream->resample_offset = 0; + stream->total_frames_queued = 0; SDL_UnlockMutex(stream->lock); return 0; diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 6563978314..abd16e800b 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -175,6 +175,7 @@ struct SDL_AudioStream float freq_ratio; struct SDL_AudioQueue* queue; + Uint64 total_frames_queued; SDL_AudioSpec input_spec; // The spec of input data currently being processed Sint64 resample_offset; diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index f1d418eb65..54467c5f51 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -906,6 +906,7 @@ SDL3_0.0.0 { SDL_GetAudioStreamFrequencyRatio; SDL_SetAudioStreamFrequencyRatio; SDL_SetAudioPostmixCallback; + SDL_GetAudioStreamQueued; # 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 ffb08c079d..da300e2913 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -931,3 +931,4 @@ #define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL #define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL #define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL +#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 71bda6eff3..a4ffe8d12c 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -977,3 +977,4 @@ SDL_DYNAPI_PROC(int,SDL_SetWindowFocusable,(SDL_Window *a, SDL_bool b),(a,b),ret SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetAudioPostmixCallback,(SDL_AudioDeviceID a, SDL_AudioPostmixCallback b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)