mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-27 02:46:08 +08:00
Refactored audio conversion to reduce copying
More of the logic has been moved into SDL_AudioQueue, allowing data to be converted directly from the input buffer.
This commit is contained in:
@@ -789,13 +789,13 @@ extern DECLSPEC float SDLCALL SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *
|
|||||||
extern DECLSPEC int SDLCALL SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio);
|
extern DECLSPEC int SDLCALL SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add data to be converted/resampled to the stream.
|
* Add data to the stream.
|
||||||
*
|
*
|
||||||
* This data must match the format/channels/samplerate specified in the latest
|
* This data must match the format/channels/samplerate specified in the latest
|
||||||
* call to SDL_SetAudioStreamFormat, or the format specified when creating the
|
* call to SDL_SetAudioStreamFormat, or the format specified when creating the
|
||||||
* stream if it hasn't been changed.
|
* stream if it hasn't been changed.
|
||||||
*
|
*
|
||||||
* Note that this call simply queues unconverted data for later. This is
|
* Note that this call simply copies the unconverted data for later. This is
|
||||||
* different than SDL2, where data was converted during the Put call and the
|
* different than SDL2, where data was converted during the Put call and the
|
||||||
* Get call would just dequeue the previously-converted data.
|
* Get call would just dequeue the previously-converted data.
|
||||||
*
|
*
|
||||||
|
|||||||
+79
-135
@@ -308,20 +308,10 @@ static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSp
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t history_buffer_allocation = SDL_GetResamplerHistoryFrames() * SDL_AUDIO_FRAMESIZE(*spec);
|
if (SDL_ResetAudioQueueHistory(stream->queue, SDL_GetResamplerHistoryFrames()) != 0) {
|
||||||
Uint8 *history_buffer = stream->history_buffer;
|
return -1;
|
||||||
|
|
||||||
if (stream->history_buffer_allocation < history_buffer_allocation) {
|
|
||||||
history_buffer = (Uint8 *) SDL_aligned_alloc(SDL_SIMDGetAlignment(), history_buffer_allocation);
|
|
||||||
if (!history_buffer) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
SDL_aligned_free(stream->history_buffer);
|
|
||||||
stream->history_buffer = history_buffer;
|
|
||||||
stream->history_buffer_allocation = history_buffer_allocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(spec->format), history_buffer_allocation);
|
|
||||||
SDL_copyp(&stream->input_spec, spec);
|
SDL_copyp(&stream->input_spec, spec);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -338,7 +328,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_
|
|||||||
}
|
}
|
||||||
|
|
||||||
retval->freq_ratio = 1.0f;
|
retval->freq_ratio = 1.0f;
|
||||||
retval->queue = SDL_CreateAudioQueue(4096);
|
retval->queue = SDL_CreateAudioQueue(8192);
|
||||||
|
|
||||||
if (!retval->queue) {
|
if (!retval->queue) {
|
||||||
SDL_free(retval);
|
SDL_free(retval);
|
||||||
@@ -550,22 +540,12 @@ static int CheckAudioStreamIsFullySetup(SDL_AudioStream *stream)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
static int PutAudioStreamBuffer(SDL_AudioStream *stream, const void *buf, int len, SDL_ReleaseAudioBufferCallback callback, void* userdata)
|
||||||
{
|
{
|
||||||
#if DEBUG_AUDIOSTREAM
|
#if DEBUG_AUDIOSTREAM
|
||||||
SDL_Log("AUDIOSTREAM: wants to put %d bytes", len);
|
SDL_Log("AUDIOSTREAM: wants to put %d bytes", len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!stream) {
|
|
||||||
return SDL_InvalidParamError("stream");
|
|
||||||
} else if (!buf) {
|
|
||||||
return SDL_InvalidParamError("buf");
|
|
||||||
} else if (len < 0) {
|
|
||||||
return SDL_InvalidParamError("len");
|
|
||||||
} else if (len == 0) {
|
|
||||||
return 0; // nothing to do.
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LockMutex(stream->lock);
|
SDL_LockMutex(stream->lock);
|
||||||
|
|
||||||
if (CheckAudioStreamIsFullySetup(stream) != 0) {
|
if (CheckAudioStreamIsFullySetup(stream) != 0) {
|
||||||
@@ -580,24 +560,13 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
|||||||
|
|
||||||
SDL_AudioTrack* track = NULL;
|
SDL_AudioTrack* track = NULL;
|
||||||
|
|
||||||
// When copying in large amounts of data, try and do as much work as possible
|
if (callback) {
|
||||||
// outside of the stream lock, otherwise the output device is likely to be starved.
|
track = SDL_CreateAudioTrack(stream->queue, &stream->src_spec, (Uint8 *)buf, len, len, callback, userdata);
|
||||||
const int large_input_thresh = 1024 * 1024;
|
|
||||||
|
|
||||||
if (len >= large_input_thresh) {
|
|
||||||
SDL_AudioSpec src_spec;
|
|
||||||
SDL_copyp(&src_spec, &stream->src_spec);
|
|
||||||
|
|
||||||
SDL_UnlockMutex(stream->lock);
|
|
||||||
|
|
||||||
size_t chunk_size = SDL_GetAudioQueueChunkSize(stream->queue);
|
|
||||||
track = SDL_CreateChunkedAudioTrack(&src_spec, (const Uint8 *)buf, len, chunk_size);
|
|
||||||
|
|
||||||
if (!track) {
|
if (!track) {
|
||||||
|
SDL_UnlockMutex(stream->lock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_LockMutex(stream->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0;
|
const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0;
|
||||||
@@ -611,7 +580,6 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
stream->total_bytes_queued += len;
|
|
||||||
if (stream->put_callback) {
|
if (stream->put_callback) {
|
||||||
const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available;
|
const int newavail = SDL_GetAudioStreamAvailable(stream) - prev_available;
|
||||||
stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail);
|
stream->put_callback(stream->put_callback_userdata, stream, newavail, newavail);
|
||||||
@@ -623,6 +591,49 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SDLCALL FreeAllocatedAudioBuffer(void *userdata, const void *buf, int len)
|
||||||
|
{
|
||||||
|
SDL_free((void*) buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
||||||
|
{
|
||||||
|
if (!stream) {
|
||||||
|
return SDL_InvalidParamError("stream");
|
||||||
|
} else if (!buf) {
|
||||||
|
return SDL_InvalidParamError("buf");
|
||||||
|
} else if (len < 0) {
|
||||||
|
return SDL_InvalidParamError("len");
|
||||||
|
} else if (len == 0) {
|
||||||
|
return 0; // nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
// When copying in large amounts of data, try and do as much work as possible
|
||||||
|
// outside of the stream lock, otherwise the output device is likely to be starved.
|
||||||
|
const int large_input_thresh = 64 * 1024;
|
||||||
|
|
||||||
|
if (len >= large_input_thresh) {
|
||||||
|
void* data = SDL_malloc(len);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_memcpy(data, buf, len);
|
||||||
|
buf = data;
|
||||||
|
|
||||||
|
int ret = PutAudioStreamBuffer(stream, buf, len, FreeAllocatedAudioBuffer, NULL);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
SDL_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PutAudioStreamBuffer(stream, buf, len, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int SDL_FlushAudioStream(SDL_AudioStream *stream)
|
int SDL_FlushAudioStream(SDL_AudioStream *stream)
|
||||||
{
|
{
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
@@ -655,31 +666,6 @@ static Uint8 *EnsureAudioStreamWorkBufferSize(SDL_AudioStream *stream, size_t ne
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateAudioStreamHistoryBuffer(SDL_AudioStream* stream,
|
|
||||||
Uint8* input_buffer, int input_bytes, Uint8* left_padding, int padding_bytes)
|
|
||||||
{
|
|
||||||
const int history_buffer_frames = SDL_GetResamplerHistoryFrames();
|
|
||||||
|
|
||||||
// Even if we aren't currently resampling, we always need to update the history buffer
|
|
||||||
Uint8 *history_buffer = stream->history_buffer;
|
|
||||||
int history_bytes = history_buffer_frames * SDL_AUDIO_FRAMESIZE(stream->input_spec);
|
|
||||||
|
|
||||||
if (left_padding) {
|
|
||||||
// Fill in the left padding using the history buffer
|
|
||||||
SDL_assert(padding_bytes <= history_bytes);
|
|
||||||
SDL_memcpy(left_padding, history_buffer + history_bytes - padding_bytes, padding_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the history buffer using the new input data
|
|
||||||
if (input_bytes >= history_bytes) {
|
|
||||||
SDL_memcpy(history_buffer, input_buffer + (input_bytes - history_bytes), history_bytes);
|
|
||||||
} else {
|
|
||||||
int preserve_bytes = history_bytes - input_bytes;
|
|
||||||
SDL_memmove(history_buffer, history_buffer + input_bytes, preserve_bytes);
|
|
||||||
SDL_memcpy(history_buffer + preserve_bytes, input_buffer, input_bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter,
|
static Sint64 NextAudioStreamIter(SDL_AudioStream* stream, void** inout_iter,
|
||||||
Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, SDL_bool* out_flushed)
|
Sint64* inout_resample_offset, SDL_AudioSpec* out_spec, SDL_bool* out_flushed)
|
||||||
{
|
{
|
||||||
@@ -777,7 +763,6 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
|
|
||||||
const SDL_AudioFormat src_format = src_spec->format;
|
const SDL_AudioFormat src_format = src_spec->format;
|
||||||
const int src_channels = src_spec->channels;
|
const int src_channels = src_spec->channels;
|
||||||
const int src_frame_size = SDL_AUDIO_FRAMESIZE(*src_spec);
|
|
||||||
|
|
||||||
const SDL_AudioFormat dst_format = dst_spec->format;
|
const SDL_AudioFormat dst_format = dst_spec->format;
|
||||||
const int dst_channels = dst_spec->channels;
|
const int dst_channels = dst_spec->channels;
|
||||||
@@ -793,34 +778,19 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
|
|
||||||
// Not resampling? It's an easy conversion (and maybe not even that!)
|
// Not resampling? It's an easy conversion (and maybe not even that!)
|
||||||
if (resample_rate == 0) {
|
if (resample_rate == 0) {
|
||||||
Uint8* input_buffer = NULL;
|
Uint8* work_buffer = NULL;
|
||||||
|
|
||||||
// If no conversion is happening, read straight into the output buffer.
|
// Ensure we have enough scratch space for any conversions
|
||||||
// Note, this is just to avoid extra copies.
|
if ((src_format != dst_format) || (src_channels != dst_channels)) {
|
||||||
// Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer.
|
work_buffer = EnsureAudioStreamWorkBufferSize(stream, output_frames * max_frame_size);
|
||||||
if ((src_format == dst_format) && (src_channels == dst_channels)) {
|
|
||||||
input_buffer = (Uint8 *)buf;
|
|
||||||
} else {
|
|
||||||
input_buffer = EnsureAudioStreamWorkBufferSize(stream, output_frames * max_frame_size);
|
|
||||||
|
|
||||||
if (!input_buffer) {
|
if (!work_buffer) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const int input_bytes = output_frames * src_frame_size;
|
if (SDL_ReadFromAudioQueue(stream->queue, buf, dst_format, dst_channels, 0, output_frames, 0, work_buffer) != buf) {
|
||||||
if (SDL_ReadFromAudioQueue(stream->queue, input_buffer, input_bytes) != 0) {
|
return SDL_SetError("Not enough data in queue");
|
||||||
SDL_assert(!"Not enough data in queue (read)");
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->total_bytes_queued -= input_bytes;
|
|
||||||
|
|
||||||
// Even if we aren't currently resampling, we always need to update the history buffer
|
|
||||||
UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, NULL, 0);
|
|
||||||
|
|
||||||
// Convert the data, if necessary
|
|
||||||
if (buf != input_buffer) {
|
|
||||||
ConvertAudio(output_frames, input_buffer, src_format, src_channels, buf, dst_format, dst_channels, input_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -832,9 +802,10 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
// can require a different number of input_frames, depending on the resample_offset.
|
// can require a different number of input_frames, depending on the resample_offset.
|
||||||
// Infact, input_frames can sometimes even be zero when upsampling.
|
// Infact, input_frames can sometimes even be zero when upsampling.
|
||||||
const int input_frames = (int) SDL_GetResamplerInputFrames(output_frames, resample_rate, stream->resample_offset);
|
const int input_frames = (int) SDL_GetResamplerInputFrames(output_frames, resample_rate, stream->resample_offset);
|
||||||
const int input_bytes = input_frames * src_frame_size;
|
|
||||||
|
|
||||||
const int resampler_padding_frames = SDL_GetResamplerPaddingFrames(resample_rate);
|
const int padding_frames = SDL_GetResamplerPaddingFrames(resample_rate);
|
||||||
|
|
||||||
|
const SDL_AudioFormat resample_format = SDL_AUDIO_F32;
|
||||||
|
|
||||||
// If increasing channels, do it after resampling, since we'd just
|
// If increasing channels, do it after resampling, since we'd just
|
||||||
// do more work to resample duplicate channels. If we're decreasing, do
|
// do more work to resample duplicate channels. If we're decreasing, do
|
||||||
@@ -843,7 +814,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
const int resample_channels = SDL_min(src_channels, dst_channels);
|
const int resample_channels = SDL_min(src_channels, dst_channels);
|
||||||
|
|
||||||
// The size of the frame used when resampling
|
// The size of the frame used when resampling
|
||||||
const int resample_frame_size = resample_channels * sizeof(float);
|
const int resample_frame_size = SDL_AUDIO_BYTESIZE(resample_format) * resample_channels;
|
||||||
|
|
||||||
// The main portion of the work_buffer can be used to store 3 things:
|
// The main portion of the work_buffer can be used to store 3 things:
|
||||||
// src_sample_frame_size * (left_padding+input_buffer+right_padding)
|
// src_sample_frame_size * (left_padding+input_buffer+right_padding)
|
||||||
@@ -854,14 +825,14 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
// resample_frame_size * output_frames
|
// resample_frame_size * output_frames
|
||||||
//
|
//
|
||||||
// Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space
|
// Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space
|
||||||
const int work_buffer_frames = input_frames + (resampler_padding_frames * 2);
|
const int work_buffer_frames = input_frames + (padding_frames * 2);
|
||||||
int work_buffer_capacity = work_buffer_frames * max_frame_size;
|
int work_buffer_capacity = work_buffer_frames * max_frame_size;
|
||||||
int resample_buffer_offset = -1;
|
int resample_buffer_offset = -1;
|
||||||
|
|
||||||
// Check if we can resample directly into the output buffer.
|
// Check if we can resample directly into the output buffer.
|
||||||
// Note, this is just to avoid extra copies.
|
// Note, this is just to avoid extra copies.
|
||||||
// Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer.
|
// Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer.
|
||||||
if ((dst_format != SDL_AUDIO_F32) || (dst_channels != resample_channels)) {
|
if ((dst_format != resample_format) || (dst_channels != resample_channels)) {
|
||||||
// Allocate space for converting the resampled output to the destination format
|
// Allocate space for converting the resampled output to the destination format
|
||||||
int resample_convert_bytes = output_frames * max_frame_size;
|
int resample_convert_bytes = output_frames * max_frame_size;
|
||||||
work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes);
|
work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes);
|
||||||
@@ -883,45 +854,15 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int padding_bytes = resampler_padding_frames * src_frame_size;
|
const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue,
|
||||||
|
NULL, resample_format, resample_channels,
|
||||||
|
padding_frames, input_frames, padding_frames, work_buffer);
|
||||||
|
|
||||||
Uint8* work_buffer_tail = work_buffer;
|
if (!input_buffer) {
|
||||||
|
return SDL_SetError("Not enough data in queue (resample)");
|
||||||
// Split the work_buffer into [left_padding][input_buffer][right_padding]
|
|
||||||
Uint8* left_padding = work_buffer_tail;
|
|
||||||
work_buffer_tail += padding_bytes;
|
|
||||||
|
|
||||||
Uint8* input_buffer = work_buffer_tail;
|
|
||||||
work_buffer_tail += input_bytes;
|
|
||||||
|
|
||||||
Uint8* right_padding = work_buffer_tail;
|
|
||||||
work_buffer_tail += padding_bytes;
|
|
||||||
|
|
||||||
SDL_assert((work_buffer_tail - work_buffer) <= work_buffer_capacity);
|
|
||||||
|
|
||||||
// Now read unconverted data from the queue into the work buffer to fulfill the request.
|
|
||||||
if (SDL_ReadFromAudioQueue(stream->queue, input_buffer, input_bytes) != 0) {
|
|
||||||
SDL_assert(!"Not enough data in queue (resample read)");
|
|
||||||
}
|
|
||||||
stream->total_bytes_queued -= input_bytes;
|
|
||||||
|
|
||||||
// Update the history buffer and fill in the left padding
|
|
||||||
UpdateAudioStreamHistoryBuffer(stream, input_buffer, input_bytes, left_padding, padding_bytes);
|
|
||||||
|
|
||||||
// Fill in the right padding by peeking into the input queue (missing data is filled with silence)
|
|
||||||
if (SDL_PeekIntoAudioQueue(stream->queue, right_padding, padding_bytes) != 0) {
|
|
||||||
SDL_assert(!"Not enough data in queue (resample peek)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_assert(work_buffer_frames == input_frames + (resampler_padding_frames * 2));
|
input_buffer += padding_frames * resample_frame_size;
|
||||||
|
|
||||||
// Resampling! get the work buffer to float32 format, etc, in-place.
|
|
||||||
ConvertAudio(work_buffer_frames, work_buffer, src_format, src_channels, work_buffer, SDL_AUDIO_F32, resample_channels, NULL);
|
|
||||||
|
|
||||||
// Update the work_buffer pointers based on the new frame size
|
|
||||||
input_buffer = work_buffer + ((input_buffer - work_buffer) / src_frame_size * resample_frame_size);
|
|
||||||
work_buffer_tail = work_buffer + ((work_buffer_tail - work_buffer) / src_frame_size * resample_frame_size);
|
|
||||||
SDL_assert((work_buffer_tail - work_buffer) <= work_buffer_capacity);
|
|
||||||
|
|
||||||
// Decide where the resampled output goes
|
// Decide where the resampled output goes
|
||||||
void* resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf;
|
void* resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf;
|
||||||
@@ -932,9 +873,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
|
|||||||
resample_rate, &stream->resample_offset);
|
resample_rate, &stream->resample_offset);
|
||||||
|
|
||||||
// Convert to the final format, if necessary
|
// Convert to the final format, if necessary
|
||||||
if (buf != resample_buffer) {
|
ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, buf, dst_format, dst_channels, work_buffer);
|
||||||
ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32, resample_channels, buf, dst_format, dst_channels, work_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1074,7 +1013,9 @@ int SDL_GetAudioStreamQueued(SDL_AudioStream *stream)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SDL_LockMutex(stream->lock);
|
SDL_LockMutex(stream->lock);
|
||||||
const Uint64 total = stream->total_bytes_queued;
|
|
||||||
|
size_t total = SDL_GetAudioQueueQueued(stream->queue);
|
||||||
|
|
||||||
SDL_UnlockMutex(stream->lock);
|
SDL_UnlockMutex(stream->lock);
|
||||||
|
|
||||||
// if this overflows an int, just clamp it to a maximum.
|
// if this overflows an int, just clamp it to a maximum.
|
||||||
@@ -1092,7 +1033,6 @@ int SDL_ClearAudioStream(SDL_AudioStream *stream)
|
|||||||
SDL_ClearAudioQueue(stream->queue);
|
SDL_ClearAudioQueue(stream->queue);
|
||||||
SDL_zero(stream->input_spec);
|
SDL_zero(stream->input_spec);
|
||||||
stream->resample_offset = 0;
|
stream->resample_offset = 0;
|
||||||
stream->total_bytes_queued = 0;
|
|
||||||
|
|
||||||
SDL_UnlockMutex(stream->lock);
|
SDL_UnlockMutex(stream->lock);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1118,7 +1058,6 @@ void SDL_DestroyAudioStream(SDL_AudioStream *stream)
|
|||||||
SDL_UnbindAudioStream(stream);
|
SDL_UnbindAudioStream(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_aligned_free(stream->history_buffer);
|
|
||||||
SDL_aligned_free(stream->work_buffer);
|
SDL_aligned_free(stream->work_buffer);
|
||||||
SDL_DestroyAudioQueue(stream->queue);
|
SDL_DestroyAudioQueue(stream->queue);
|
||||||
SDL_DestroyMutex(stream->lock);
|
SDL_DestroyMutex(stream->lock);
|
||||||
@@ -1126,6 +1065,11 @@ void SDL_DestroyAudioStream(SDL_AudioStream *stream)
|
|||||||
SDL_free(stream);
|
SDL_free(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SDLCALL DontFreeThisAudioBuffer(void *userdata, const void *buf, int len)
|
||||||
|
{
|
||||||
|
// We don't own the buffer, but know it will outlive the stream
|
||||||
|
}
|
||||||
|
|
||||||
int SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len,
|
int SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len,
|
||||||
const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len)
|
const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len)
|
||||||
{
|
{
|
||||||
@@ -1153,7 +1097,7 @@ int SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data
|
|||||||
|
|
||||||
SDL_AudioStream *stream = SDL_CreateAudioStream(src_spec, dst_spec);
|
SDL_AudioStream *stream = SDL_CreateAudioStream(src_spec, dst_spec);
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if ((SDL_PutAudioStreamData(stream, src_data, src_len) == 0) && (SDL_FlushAudioStream(stream) == 0)) {
|
if ((PutAudioStreamBuffer(stream, src_data, src_len, DontFreeThisAudioBuffer, NULL) == 0) && (SDL_FlushAudioStream(stream) == 0)) {
|
||||||
dstlen = SDL_GetAudioStreamAvailable(stream);
|
dstlen = SDL_GetAudioStreamAvailable(stream);
|
||||||
if (dstlen >= 0) {
|
if (dstlen >= 0) {
|
||||||
dst = (Uint8 *)SDL_malloc(dstlen);
|
dst = (Uint8 *)SDL_malloc(dstlen);
|
||||||
|
|||||||
+376
-249
File diff suppressed because it is too large
Load Diff
+14
-12
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
// Internal functions used by SDL_AudioStream for queueing audio.
|
// Internal functions used by SDL_AudioStream for queueing audio.
|
||||||
|
|
||||||
|
typedef void(SDLCALL *SDL_ReleaseAudioBufferCallback)(void *userdata, const void *buffer, int buflen);
|
||||||
|
|
||||||
typedef struct SDL_AudioQueue SDL_AudioQueue;
|
typedef struct SDL_AudioQueue SDL_AudioQueue;
|
||||||
typedef struct SDL_AudioTrack SDL_AudioTrack;
|
typedef struct SDL_AudioTrack SDL_AudioTrack;
|
||||||
|
|
||||||
@@ -44,16 +46,14 @@ void SDL_FlushAudioQueue(SDL_AudioQueue *queue);
|
|||||||
// REQUIRES: The head track must exist, and must have been flushed
|
// REQUIRES: The head track must exist, and must have been flushed
|
||||||
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
|
void SDL_PopAudioQueueHead(SDL_AudioQueue *queue);
|
||||||
|
|
||||||
// Get the chunk size, mostly for use with SDL_CreateChunkedAudioTrack
|
|
||||||
// This can be called from any thread
|
|
||||||
size_t SDL_GetAudioQueueChunkSize(SDL_AudioQueue *queue);
|
|
||||||
|
|
||||||
// Write data to the end of queue
|
// Write data to the end of queue
|
||||||
// REQUIRES: If the spec has changed, the last track must have been flushed
|
// REQUIRES: If the spec has changed, the last track must have been flushed
|
||||||
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len);
|
int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len);
|
||||||
|
|
||||||
// Create a track without needing to hold any locks
|
// Create a track where the input data is owned by the caller
|
||||||
SDL_AudioTrack *SDL_CreateChunkedAudioTrack(const SDL_AudioSpec *spec, const Uint8 *data, size_t len, size_t chunk_size);
|
SDL_AudioTrack *SDL_CreateAudioTrack(SDL_AudioQueue *queue,
|
||||||
|
const SDL_AudioSpec *spec, Uint8 *data, size_t len, size_t capacity,
|
||||||
|
SDL_ReleaseAudioBufferCallback callback, void *userdata);
|
||||||
|
|
||||||
// Add a track to the end of the queue
|
// Add a track to the end of the queue
|
||||||
// REQUIRES: `track != NULL`
|
// REQUIRES: `track != NULL`
|
||||||
@@ -66,12 +66,14 @@ void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue);
|
|||||||
// REQUIRES: `*inout_iter != NULL` (a valid iterator)
|
// REQUIRES: `*inout_iter != NULL` (a valid iterator)
|
||||||
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed);
|
size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, SDL_bool *out_flushed);
|
||||||
|
|
||||||
// Read data from the start of the queue
|
const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
|
||||||
// REQUIRES: There must be enough data in the queue
|
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels,
|
||||||
int SDL_ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
int past_frames, int present_frames, int future_frames,
|
||||||
|
Uint8 *scratch);
|
||||||
|
|
||||||
// Peek into the start of the queue
|
// Get the total number of bytes currently queued
|
||||||
// REQUIRES: There must be enough data in the queue, unless it has been flushed, in which case missing data is filled with silence.
|
size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue);
|
||||||
int SDL_PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len);
|
|
||||||
|
int SDL_ResetAudioQueueHistory(SDL_AudioQueue *queue, int num_frames);
|
||||||
|
|
||||||
#endif // SDL_audioqueue_h_
|
#endif // SDL_audioqueue_h_
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ struct SDL_AudioStream
|
|||||||
float freq_ratio;
|
float freq_ratio;
|
||||||
|
|
||||||
struct SDL_AudioQueue* queue;
|
struct SDL_AudioQueue* queue;
|
||||||
Uint64 total_bytes_queued;
|
|
||||||
|
|
||||||
SDL_AudioSpec input_spec; // The spec of input data currently being processed
|
SDL_AudioSpec input_spec; // The spec of input data currently being processed
|
||||||
Sint64 resample_offset;
|
Sint64 resample_offset;
|
||||||
@@ -198,9 +197,6 @@ struct SDL_AudioStream
|
|||||||
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
|
Uint8 *work_buffer; // used for scratch space during data conversion/resampling.
|
||||||
size_t work_buffer_allocation;
|
size_t work_buffer_allocation;
|
||||||
|
|
||||||
Uint8 *history_buffer; // history for left padding and future sample rate changes.
|
|
||||||
size_t history_buffer_allocation;
|
|
||||||
|
|
||||||
SDL_bool simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream
|
SDL_bool simplified; // SDL_TRUE if created via SDL_OpenAudioDeviceStream
|
||||||
|
|
||||||
SDL_LogicalAudioDevice *bound_device;
|
SDL_LogicalAudioDevice *bound_device;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
freely.
|
freely.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* !!! FIXME: this code is not up to standards for SDL3 test apps. Someone should improve this. */
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_main.h>
|
#include <SDL3/SDL_main.h>
|
||||||
#include <SDL3/SDL_test.h>
|
#include <SDL3/SDL_test.h>
|
||||||
@@ -117,6 +115,7 @@ static void queue_audio()
|
|||||||
|
|
||||||
SDL_Log("Converting audio from %i to %i", spec.freq, new_spec.freq);
|
SDL_Log("Converting audio from %i to %i", spec.freq, new_spec.freq);
|
||||||
|
|
||||||
|
/* You shouldn't actually use SDL_ConvertAudioSamples like this (just put the data straight into the stream and let it handle conversion) */
|
||||||
retval = retval ? retval : SDL_ConvertAudioSamples(&spec, audio_buf, audio_len, &new_spec, &new_data, &new_len);
|
retval = retval ? retval : SDL_ConvertAudioSamples(&spec, audio_buf, audio_len, &new_spec, &new_data, &new_len);
|
||||||
retval = retval ? retval : SDL_SetAudioStreamFormat(stream, &new_spec, NULL);
|
retval = retval ? retval : SDL_SetAudioStreamFormat(stream, &new_spec, NULL);
|
||||||
retval = retval ? retval : SDL_PutAudioStreamData(stream, new_data, new_len);
|
retval = retval ? retval : SDL_PutAudioStreamData(stream, new_data, new_len);
|
||||||
@@ -207,6 +206,7 @@ static void loop(void)
|
|||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
SDL_FPoint p;
|
SDL_FPoint p;
|
||||||
SDL_AudioSpec src_spec, dst_spec;
|
SDL_AudioSpec src_spec, dst_spec;
|
||||||
|
int queued_bytes = 0;
|
||||||
int available_bytes = 0;
|
int available_bytes = 0;
|
||||||
float available_seconds = 0;
|
float available_seconds = 0;
|
||||||
|
|
||||||
@@ -294,6 +294,8 @@ static void loop(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queued_bytes = SDL_GetAudioStreamQueued(stream);
|
||||||
|
|
||||||
for (i = 0; i < state->num_windows; i++) {
|
for (i = 0; i < state->num_windows; i++) {
|
||||||
int draw_y = 0;
|
int draw_y = 0;
|
||||||
SDL_Renderer* rend = state->renderers[i];
|
SDL_Renderer* rend = state->renderers[i];
|
||||||
@@ -326,6 +328,9 @@ static void loop(void)
|
|||||||
draw_textf(rend, 0, draw_y, "Available: %4.2f (%i bytes)", available_seconds, available_bytes);
|
draw_textf(rend, 0, draw_y, "Available: %4.2f (%i bytes)", available_seconds, available_bytes);
|
||||||
draw_y += FONT_LINE_HEIGHT;
|
draw_y += FONT_LINE_HEIGHT;
|
||||||
|
|
||||||
|
draw_textf(rend, 0, draw_y, "Queued: %i bytes", queued_bytes);
|
||||||
|
draw_y += FONT_LINE_HEIGHT;
|
||||||
|
|
||||||
SDL_LockAudioStream(stream);
|
SDL_LockAudioStream(stream);
|
||||||
|
|
||||||
draw_textf(rend, 0, draw_y, "Get Callback: %i/%i bytes, %2i ms ago",
|
draw_textf(rend, 0, draw_y, "Get Callback: %i/%i bytes, %2i ms ago",
|
||||||
|
|||||||
@@ -821,11 +821,6 @@ static double sine_wave_sample(const Sint64 idx, const Sint64 rate, const Sint64
|
|||||||
return SDL_sin(((double)(idx * freq % rate)) / ((double)rate) * (SDL_PI_D * 2) + phase);
|
return SDL_sin(((double)(idx * freq % rate)) / ((double)rate) * (SDL_PI_D * 2) + phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_audio_buffer(void* userdata, const void* buf, int len)
|
|
||||||
{
|
|
||||||
SDL_free((void*) buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Split the data into randomly sized chunks */
|
/* Split the data into randomly sized chunks */
|
||||||
static int put_audio_data_split(SDL_AudioStream* stream, const void* buf, int len)
|
static int put_audio_data_split(SDL_AudioStream* stream, const void* buf, int len)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user