mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-05-01 03:24:56 +00:00
audio: SDL_AudioStream now has callbacks for Get and Put operations.
This allows code to feed a stream (or feed from a stream) on-demand, which is to say: it can efficiently simulate the SDL2 audio callback.
This commit is contained in:
@@ -629,6 +629,40 @@ SDL_AudioStream *SDL_CreateAudioStream(SDL_AudioFormat src_format,
|
||||
return retval;
|
||||
}
|
||||
|
||||
int SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata)
|
||||
{
|
||||
if (!stream) {
|
||||
return SDL_InvalidParamError("stream");
|
||||
}
|
||||
SDL_LockMutex(stream->lock);
|
||||
stream->get_callback = callback;
|
||||
stream->get_callback_userdata = userdata;
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamRequestCallback callback, void *userdata)
|
||||
{
|
||||
if (!stream) {
|
||||
return SDL_InvalidParamError("stream");
|
||||
}
|
||||
SDL_LockMutex(stream->lock);
|
||||
stream->put_callback = callback;
|
||||
stream->put_callback_userdata = userdata;
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDL_LockAudioStream(SDL_AudioStream *stream)
|
||||
{
|
||||
return stream ? SDL_LockMutex(stream->lock) : SDL_InvalidParamError("stream");
|
||||
}
|
||||
|
||||
int SDL_UnlockAudioStream(SDL_AudioStream *stream)
|
||||
{
|
||||
return stream ? SDL_UnlockMutex(stream->lock) : SDL_InvalidParamError("stream");
|
||||
}
|
||||
|
||||
int 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)
|
||||
{
|
||||
if (!stream) {
|
||||
@@ -696,6 +730,8 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
||||
|
||||
SDL_LockMutex(stream->lock);
|
||||
|
||||
const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0;
|
||||
|
||||
if ((len % stream->src_sample_frame_size) != 0) {
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
return SDL_SetError("Can't add partial sample frames");
|
||||
@@ -704,6 +740,11 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
|
||||
/* just queue the data, we convert/resample when dequeueing. */
|
||||
retval = SDL_WriteToDataQueue(stream->queue, buf, len);
|
||||
stream->flushed = SDL_FALSE;
|
||||
|
||||
if (stream->put_callback) {
|
||||
stream->put_callback(stream, SDL_GetAudioStreamAvailable(stream) - prev_available, stream->put_callback_userdata);
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(stream->lock);
|
||||
|
||||
return retval;
|
||||
@@ -978,6 +1019,23 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
|
||||
|
||||
len -= len % stream->dst_sample_frame_size; /* chop off any fractional sample frame. */
|
||||
|
||||
// give the callback a chance to fill in more stream data if it wants.
|
||||
if (stream->get_callback) {
|
||||
int approx_request = len / stream->dst_sample_frame_size; /* start with sample frames desired */
|
||||
if (stream->src_rate != stream->dst_rate) {
|
||||
/* calculate difference in dataset size after resampling. Use a Uint64 so the multiplication doesn't overflow. */
|
||||
approx_request = (size_t) ((((Uint64) approx_request) * stream->src_rate) / stream->dst_rate);
|
||||
if (!stream->flushed) { /* do we need to fill the future buffer to accomodate this, too? */
|
||||
approx_request += stream->future_buffer_filled_frames - stream->resampler_padding_frames;
|
||||
}
|
||||
}
|
||||
|
||||
approx_request *= stream->src_sample_frame_size; // convert sample frames to bytes.
|
||||
const int already_have = SDL_GetAudioStreamAvailable(stream);
|
||||
approx_request -= SDL_min(approx_request, already_have); // we definitely have this much output already packed in.
|
||||
stream->get_callback(stream, approx_request, stream->get_callback_userdata);
|
||||
}
|
||||
|
||||
/* we convert in chunks, so we don't end up allocating a massive work buffer, etc. */
|
||||
while (len > 0) { /* didn't ask for a whole sample frame, nothing to do */
|
||||
const int chunk_size = 1024 * 1024; /* !!! FIXME: a megabyte might be overly-aggressive. */
|
||||
|
||||
@@ -137,6 +137,11 @@ struct SDL_AudioStream
|
||||
SDL_DataQueue *queue;
|
||||
SDL_Mutex *lock; /* this is just a copy of `queue`'s mutex. We share a lock. */
|
||||
|
||||
SDL_AudioStreamRequestCallback get_callback;
|
||||
void *get_callback_userdata;
|
||||
SDL_AudioStreamRequestCallback put_callback;
|
||||
void *put_callback_userdata;
|
||||
|
||||
Uint8 *work_buffer; /* used for scratch space during data conversion/resampling. */
|
||||
Uint8 *history_buffer; /* history for left padding and future sample rate changes. */
|
||||
Uint8 *future_buffer; /* stuff that left the queue for the right padding and will be next read's data. */
|
||||
|
||||
@@ -879,6 +879,10 @@ SDL3_0.0.0 {
|
||||
SDL_MixAudioFormat;
|
||||
SDL_ConvertAudioSamples;
|
||||
SDL_GetSilenceValueForFormat;
|
||||
SDL_LockAudioStream;
|
||||
SDL_UnlockAudioStream;
|
||||
SDL_SetAudioStreamGetCallback;
|
||||
SDL_SetAudioStreamPutCallback;
|
||||
# extra symbols go here (don't modify this line)
|
||||
local: *;
|
||||
};
|
||||
|
||||
@@ -905,3 +905,7 @@
|
||||
#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
|
||||
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
|
||||
#define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL
|
||||
#define SDL_LockAudioStream SDL_LockAudioStream_REAL
|
||||
#define SDL_UnlockAudioStream SDL_UnlockAudioStream_REAL
|
||||
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
|
||||
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
|
||||
|
||||
@@ -950,3 +950,7 @@ SDL_DYNAPI_PROC(int,SDL_LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioFormat *c, int
|
||||
SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioFormat c, Uint32 d, int e),(a,b,c,d,e),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(SDL_AudioFormat a, int b, int c, const Uint8 *d, int e, SDL_AudioFormat f, int g, int h, Uint8 **i, int *j),(a,b,c,d,e,f,g,h,i,j),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_LockAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_UnlockAudioStream,(SDL_AudioStream *a),(a),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamRequestCallback b, void *c),(a,b,c),return)
|
||||
|
||||
Reference in New Issue
Block a user