mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-05 19:08:12 +00:00
audio: Added SDL_SetAudioIterationCallbacks().
This commit is contained in:
@@ -1931,6 +1931,84 @@ extern SDL_DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream)
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_AudioStream * SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata);
|
||||
|
||||
/**
|
||||
* A callback that fires around an audio device's processing work.
|
||||
*
|
||||
* This callback fires when a logical audio device is about to start
|
||||
* accessing its bound audio streams, and fires again when it has
|
||||
* finished accessing them. It covers the range of one "iteration" of
|
||||
* the audio device.
|
||||
*
|
||||
* It can be useful to use this callback to update state that must
|
||||
* apply to all bound audio streams atomically: to make sure state
|
||||
* changes don't happen while half of the streams are already processed
|
||||
* for the latest audio buffer.
|
||||
*
|
||||
* This callback should run as quickly as possible and not block for any
|
||||
* significant time, as this callback delays submission of data to the audio
|
||||
* device, which can cause audio playback problems. This callback delays all
|
||||
* audio processing across a single physical audio device: all its logical
|
||||
* devices and all bound audio streams. Use it carefully.
|
||||
*
|
||||
* \param userdata a pointer provided by the app through
|
||||
* SDL_SetAudioPostmixCallback, for its own use.
|
||||
* \param devid the audio device this callback is running for.
|
||||
* \param start true if this is the start of the iteration, false if the end.
|
||||
*
|
||||
* \threadsafety This will run from a background thread owned by SDL. The
|
||||
* application is responsible for locking resources the callback
|
||||
* touches that need to be protected.
|
||||
*
|
||||
* \since This datatype is available since SDL 3.4.0.
|
||||
*
|
||||
* \sa SDL_SetAudioIterationCallbacks
|
||||
*/
|
||||
typedef void (SDLCALL *SDL_AudioIterationCallback)(void *userdata, SDL_AudioDeviceID devid, bool start);
|
||||
|
||||
/**
|
||||
* Set callbacks that fire around a new iteration of audio device processing.
|
||||
*
|
||||
* Two callbacks are provided here: one that runs when a device is about to
|
||||
* process its bound audio streams, and another that runs when the device has
|
||||
* finished processing them.
|
||||
*
|
||||
* These callbacks can run at any time, and from any thread; if you need to
|
||||
* serialize access to your app's data, you should provide and use a mutex or
|
||||
* other synchronization device.
|
||||
*
|
||||
* Generally these callbacks are used to apply state that applies to multiple
|
||||
* bound audio streams, with a guarantee that the audio device's thread isn't
|
||||
* halfway through processing them. Generally a finer-grained lock through
|
||||
* SDL_LockAudioStream() is more appropriate.
|
||||
*
|
||||
* The callbacks are extremely time-sensitive; the callback should do the
|
||||
* least amount of work possible and return as quickly as it can. The longer
|
||||
* the callback runs, the higher the risk of audio dropouts or other problems.
|
||||
*
|
||||
* This function will block until the audio device is in between iterations,
|
||||
* so any existing callback that might be running will finish before this
|
||||
* function sets the new callback and returns.
|
||||
*
|
||||
* Physical devices do not accept these callbacks, only logical devices
|
||||
* created through SDL_OpenAudioDevice() can be.
|
||||
*
|
||||
* Setting a NULL callback function disables any previously-set callback.
|
||||
* Either callback may be NULL, and the same callback is permitted to be used
|
||||
* for both.
|
||||
*
|
||||
* \param devid the ID of an opened audio device.
|
||||
* \param start a callback function to be called at the start of an iteration. Can be NULL.
|
||||
* \param end a callback function to be called at the end of an iteration. Can be NULL.
|
||||
* \param userdata app-controlled pointer passed to callback. Can be NULL.
|
||||
* \returns true on success or false on failure; call SDL_GetError() for more
|
||||
* information.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.4.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioIterationCallbacks(SDL_AudioDeviceID devid, SDL_AudioIterationCallback start, SDL_AudioIterationCallback end, void *userdata);
|
||||
|
||||
/**
|
||||
* A callback that fires when data is about to be fed to an audio device.
|
||||
*
|
||||
|
@@ -1147,7 +1147,20 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
|
||||
// We should have updated this elsewhere if the format changed!
|
||||
SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec, NULL, NULL));
|
||||
|
||||
const int br = SDL_GetAtomicInt(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain);
|
||||
int br = 0;
|
||||
|
||||
if (!SDL_GetAtomicInt(&logdev->paused)) {
|
||||
if (logdev->iteration_start) {
|
||||
logdev->iteration_start(logdev->iteration_userdata, logdev->instance_id, true);
|
||||
}
|
||||
|
||||
br = SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain);
|
||||
|
||||
if (logdev->iteration_end) {
|
||||
logdev->iteration_end(logdev->iteration_userdata, logdev->instance_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
|
||||
failed = true;
|
||||
SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die.
|
||||
@@ -1185,6 +1198,10 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
|
||||
SDL_memset(mix_buffer, '\0', work_buffer_size); // start with silence.
|
||||
}
|
||||
|
||||
if (logdev->iteration_start) {
|
||||
logdev->iteration_start(logdev->iteration_userdata, logdev->instance_id, true);
|
||||
}
|
||||
|
||||
for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
|
||||
// We should have updated this elsewhere if the format changed!
|
||||
SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &outspec, NULL, NULL));
|
||||
@@ -1207,6 +1224,10 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
|
||||
}
|
||||
}
|
||||
|
||||
if (logdev->iteration_end) {
|
||||
logdev->iteration_end(logdev->iteration_userdata, logdev->instance_id, false);
|
||||
}
|
||||
|
||||
if (postmix) {
|
||||
SDL_assert(mix_buffer == device->postmix_buffer);
|
||||
postmix(logdev->postmix_userdata, &outspec, mix_buffer, work_buffer_size);
|
||||
@@ -1902,8 +1923,9 @@ bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallba
|
||||
{
|
||||
SDL_AudioDevice *device = NULL;
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device);
|
||||
bool result = true;
|
||||
bool result = false;
|
||||
if (logdev) {
|
||||
result = true;
|
||||
if (callback && !device->postmix_buffer) {
|
||||
device->postmix_buffer = (float *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), device->work_buffer_size);
|
||||
if (!device->postmix_buffer) {
|
||||
@@ -1922,6 +1944,21 @@ bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallba
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SDL_SetAudioIterationCallbacks(SDL_AudioDeviceID devid, SDL_AudioIterationCallback iter_start, SDL_AudioIterationCallback iter_end, void *userdata)
|
||||
{
|
||||
SDL_AudioDevice *device = NULL;
|
||||
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device);
|
||||
bool result = false;
|
||||
if (logdev) {
|
||||
logdev->iteration_start = iter_start;
|
||||
logdev->iteration_end = iter_end;
|
||||
logdev->iteration_userdata = userdata;
|
||||
result = true;
|
||||
}
|
||||
ReleaseAudioDevice(device);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams)
|
||||
{
|
||||
const bool islogical = !(devid & (1<<1));
|
||||
|
@@ -264,6 +264,13 @@ struct SDL_LogicalAudioDevice
|
||||
// true if device was opened with SDL_OpenAudioDeviceStream (so it forbids binding changes, etc).
|
||||
bool simplified;
|
||||
|
||||
// If non-NULL, callback into the app that alerts it to start/end of device iteration.
|
||||
SDL_AudioIterationCallback iteration_start;
|
||||
SDL_AudioIterationCallback iteration_end;
|
||||
|
||||
// App-supplied pointer for iteration callbacks.
|
||||
void *iteration_userdata;
|
||||
|
||||
// If non-NULL, callback into the app that lets them access the final postmix buffer.
|
||||
SDL_AudioPostmixCallback postmix;
|
||||
|
||||
|
@@ -1251,6 +1251,7 @@ SDL3_0.0.0 {
|
||||
SDL_GetGPUDeviceProperties;
|
||||
SDL_CreateGPURenderer;
|
||||
SDL_PutAudioStreamPlanarData;
|
||||
SDL_SetAudioIterationCallbacks;
|
||||
# extra symbols go here (don't modify this line)
|
||||
local: *;
|
||||
};
|
||||
|
@@ -1276,3 +1276,4 @@
|
||||
#define SDL_GetGPUDeviceProperties SDL_GetGPUDeviceProperties_REAL
|
||||
#define SDL_CreateGPURenderer SDL_CreateGPURenderer_REAL
|
||||
#define SDL_PutAudioStreamPlanarData SDL_PutAudioStreamPlanarData_REAL
|
||||
#define SDL_SetAudioIterationCallbacks SDL_SetAudioIterationCallbacks_REAL
|
||||
|
@@ -1284,3 +1284,4 @@ SDL_DYNAPI_PROC(bool,SDL_GetRenderTextureAddressMode,(SDL_Renderer *a,SDL_Textur
|
||||
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGPUDeviceProperties,(SDL_GPUDevice *a),(a),return)
|
||||
SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateGPURenderer,(SDL_Window *a,SDL_GPUShaderFormat b,SDL_GPUDevice **c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(bool,SDL_PutAudioStreamPlanarData,(SDL_AudioStream *a,const void * const*b,int c),(a,b,c),return)
|
||||
SDL_DYNAPI_PROC(bool,SDL_SetAudioIterationCallbacks,(SDL_AudioDeviceID a,SDL_AudioIterationCallback b,SDL_AudioIterationCallback c,void *d),(a,b,c,d),return)
|
||||
|
Reference in New Issue
Block a user