audio: Readded (logical) device pausing.

This commit is contained in:
Ryan C. Gordon 2023-06-22 00:59:26 -04:00
parent fd4c9f4e11
commit 01f7b53865
No known key found for this signature in database
GPG key ID: FA148B892AB48044
6 changed files with 134 additions and 3 deletions

View file

@ -144,9 +144,11 @@ Rather than iterating over audio devices using a device index, there is a new fu
SDL_LockAudioDevice() and SDL_UnlockAudioDevice() have been removed, since there is no callback in another thread to protect. Internally, SDL's audio subsystem and SDL_AudioStream maintain their own locks internally, so audio streams are safe to use from any thread. SDL_LockAudioDevice() and SDL_UnlockAudioDevice() have been removed, since there is no callback in another thread to protect. Internally, SDL's audio subsystem and SDL_AudioStream maintain their own locks internally, so audio streams are safe to use from any thread.
SDL_PauseAudioDevice() has been removed; unbinding an audio stream from a device with SDL_UnbindAudioStream() will leave the stream still containing any unconsumed data, effectively pausing it until rebound with SDL_BindAudioStream() again. Devices act like they are "paused" after open, like SDL2, until a stream is bound to it. SDL_PauseAudioDevice() no longer takes a second argument; it always pauses the device. To unpause, use SDL_UnpauseAudioDevice().
SDL_GetAudioDeviceStatus() has been removed; there is no more concept of "pausing" a device, just whether streams are bound, so please keep track of your audio streams! Audio devices, opened by SDL_OpenAudioDevice(), no longer start in a paused state, as they don't begin processing audio until a stream is bound.
SDL_GetAudioDeviceStatus() has been removed; there is now SDL_IsAudioDevicePaused().
SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality. SDL_QueueAudio(), SDL_DequeueAudio, and SDL_ClearQueuedAudio and SDL_GetQueuedAudioSize() have been removed; an SDL_AudioStream bound to a device provides the exact same functionality.
@ -230,7 +232,6 @@ The following functions have been removed:
* SDL_OpenAudio() * SDL_OpenAudio()
* SDL_CloseAudio() * SDL_CloseAudio()
* SDL_PauseAudio() * SDL_PauseAudio()
* SDL_PauseAudioDevice
* SDL_GetAudioStatus() * SDL_GetAudioStatus()
* SDL_GetAudioDeviceStatus() * SDL_GetAudioDeviceStatus()
* SDL_LockAudio() * SDL_LockAudio()

View file

@ -407,6 +407,87 @@ extern DECLSPEC int SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SD
*/ */
extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec); extern DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec);
/**
* Use this function to pause audio playback on a specified device.
*
* This function pauses audio processing for a given device. Any bound
* audio streams will not progress, and no audio will be generated.
* Pausing one device does not prevent other unpaused devices from running.
*
* Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
* has to bind a stream before any audio will flow. Pausing a paused
* device is a legal no-op.
*
* Pausing a device can be useful to halt all audio without unbinding all
* the audio streams. This might be useful while a game is paused, or
* a level is loading, etc.
*
* Physical devices can not be paused or unpaused, only logical devices
* created through SDL_OpenAudioDevice() can be.
*
* \param dev a device opened by SDL_OpenAudioDevice()
* \returns 0 on success or a negative error code 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.0.0.
*
* \sa SDL_UnpauseAudioDevice
* \sa SDL_IsAudioDevicePaused
*/
extern DECLSPEC int SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev);
/**
* Use this function to unpause audio playback on a specified device.
*
* This function unpauses audio processing for a given device that has
* previously been paused with SDL_PauseAudioDevice(). Once unpaused, any
* bound audio streams will begin to progress again, and audio can be
* generated.
*
* Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
* has to bind a stream before any audio will flow. Unpausing an unpaused
* device is a legal no-op.
*
* Physical devices can not be paused or unpaused, only logical devices
* created through SDL_OpenAudioDevice() can be.
*
* \param dev a device opened by SDL_OpenAudioDevice()
* \returns 0 on success or a negative error code 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.0.0.
*
* \sa SDL_UnpauseAudioDevice
* \sa SDL_IsAudioDevicePaused
*/
extern DECLSPEC int SDLCALL SDL_UnpauseAudioDevice(SDL_AudioDeviceID dev);
/**
* Use this function to query if an audio device is paused.
*
* Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
* has to bind a stream before any audio will flow.
*
* Physical devices can not be paused or unpaused, only logical devices
* created through SDL_OpenAudioDevice() can be. Physical and invalid
* device IDs will report themselves as unpaused here.
*
* \param dev a device opened by SDL_OpenAudioDevice()
* \returns SDL_TRUE if device is valid and paused, SDL_FALSE otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_PauseAudioDevice
* \sa SDL_UnpauseAudioDevice
* \sa SDL_IsAudioDevicePaused
*/
extern DECLSPEC SDL_bool SDLCALL SDL_IsAudioDevicePaused(SDL_AudioDeviceID dev);
/** /**
* Close a previously-opened audio device. * Close a previously-opened audio device.

View file

@ -603,7 +603,12 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
} else { } else {
SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more. SDL_assert(buffer_size <= device->buffer_size); // you can ask for less, but not more.
SDL_memset(mix_buffer, device->silence_value, buffer_size); // start with silence. SDL_memset(mix_buffer, device->silence_value, buffer_size); // start with silence.
for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) { for (SDL_LogicalAudioDevice *logdev = device->logical_devices; logdev != NULL; logdev = logdev->next) {
if (SDL_AtomicGet(&logdev->paused)) {
continue; // paused? Skip this logical device.
}
for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) { for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
/* this will hold a lock on `stream` while getting. We don't explicitly lock the streams /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
for iterating here because the binding linked list can only change while the device lock is held. for iterating here because the binding linked list can only change while the device lock is held.
@ -1165,6 +1170,41 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
return retval; return retval;
} }
static int SetLogicalAudioDevicePauseState(SDL_AudioDeviceID devid, int value)
{
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
if (!logdev) {
return -1; // ObtainLogicalAudioDevice will have set an error.
}
SDL_AtomicSet(&logdev->paused, value);
SDL_UnlockMutex(logdev->physical_device->lock);
return 0;
}
int SDL_PauseAudioDevice(SDL_AudioDeviceID devid)
{
return SetLogicalAudioDevicePauseState(devid, 1);
}
int SDLCALL SDL_UnpauseAudioDevice(SDL_AudioDeviceID devid)
{
return SetLogicalAudioDevicePauseState(devid, 0);
}
SDL_bool SDL_IsAudioDevicePaused(SDL_AudioDeviceID devid)
{
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid);
SDL_bool retval = SDL_FALSE;
if (logdev) {
if (SDL_AtomicGet(&logdev->paused)) {
retval = SDL_TRUE;
}
SDL_UnlockMutex(logdev->physical_device->lock);
}
return retval;
}
int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams) int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int num_streams)
{ {
const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE; const SDL_bool islogical = (devid & (1<<1)) ? SDL_FALSE : SDL_TRUE;

View file

@ -885,6 +885,9 @@ SDL3_0.0.0 {
SDL_ConvertAudioSamples; SDL_ConvertAudioSamples;
SDL_GetSilenceValueForFormat; SDL_GetSilenceValueForFormat;
SDL_LoadWAV; SDL_LoadWAV;
SDL_PauseAudioDevice;
SDL_UnpauseAudioDevice;
SDL_IsAudioDevicePaused;
# extra symbols go here (don't modify this line) # extra symbols go here (don't modify this line)
local: *; local: *;
}; };

View file

@ -911,3 +911,6 @@
#define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL #define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL
#define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL #define SDL_GetSilenceValueForFormat SDL_GetSilenceValueForFormat_REAL
#define SDL_LoadWAV SDL_LoadWAV_REAL #define SDL_LoadWAV SDL_LoadWAV_REAL
#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
#define SDL_UnpauseAudioDevice SDL_UnpauseAudioDevice_REAL
#define SDL_IsAudioDevicePaused SDL_IsAudioDevicePaused_REAL

View file

@ -955,3 +955,6 @@ SDL_DYNAPI_PROC(int,SDL_MixAudioFormat,(Uint8 *a, const Uint8 *b, SDL_AudioForma
SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(int,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetSilenceValueForFormat,(SDL_AudioFormat a),(a),return)
SDL_DYNAPI_PROC(int,SDL_LoadWAV,(const char *a, SDL_AudioSpec *b, Uint8 **c, Uint32 *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_LoadWAV,(const char *a, SDL_AudioSpec *b, Uint8 **c, Uint32 *d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_PauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnpauseAudioDevice,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_IsAudioDevicePaused,(SDL_AudioDeviceID a),(a),return)