audio: Add gain support to audio streams and logical audio devices.

Fixes #10028.
This commit is contained in:
Ryan C. Gordon
2024-07-01 15:08:20 -04:00
parent 74cc06db1b
commit 2a8f1e11ca
10 changed files with 286 additions and 26 deletions

View File

@@ -718,6 +718,62 @@ extern SDL_DECLSPEC int SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID dev);
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_AudioDevicePaused(SDL_AudioDeviceID dev);
/**
* Get the gain of an audio device.
*
* The gain of a device is its volume; a larger gain means a louder output,
* with a gain of zero being silence.
*
* Audio devices default to a gain of 1.0f (no change in output).
*
* Physical devices may not have their gain changed, only logical devices,
* and this function will always return -1.0f when used on physical devices.
*
* \param devid the audio device to query.
* \returns the gain of the device, or -1.0f on error.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioDeviceGain
*/
extern SDL_DECLSPEC float SDLCALL SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid);
/**
* Change the gain of an audio device.
*
* The gain of a device is its volume; a larger gain means a louder output,
* with a gain of zero being silence.
*
* Audio devices default to a gain of 1.0f (no change in output).
*
* Physical devices may not have their gain changed, only logical devices,
* and this function will always return -1 when used on physical devices. While
* it might seem attractive to adjust several logical devices at once in this
* way, it would allow an app or library to interfere with another portion of
* the program's otherwise-isolated devices.
*
* This is applied, along with any per-audiostream gain, during playback to
* the hardware, and can be continuously changed to create various effects.
* On recording devices, this will adjust the gain before passing the data
* into an audiostream; that recording audiostream can then adjust its gain
* further when outputting the data elsewhere, if it likes, but that second
* gain is not applied until the data leaves the audiostream again.
*
* \param devid the audio device on which to change gain.
* \param gain the gain. 1.0f is no change, 0.0f is silence.
* \returns 0 on success, or -1 on error.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetAudioDeviceGain
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain);
/**
* Close a previously-opened audio device.
*
@@ -981,6 +1037,51 @@ extern SDL_DECLSPEC float SDLCALL SDL_GetAudioStreamFrequencyRatio(SDL_AudioStre
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio);
/**
* Get the gain of an audio stream.
*
* The gain of a stream is its volume; a larger gain means a louder output,
* with a gain of zero being silence.
*
* Audio streams default to a gain of 1.0f (no change in output).
*
* \param stream the SDL_AudioStream to query.
* \returns the gain of the stream, or -1.0f on error.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAudioStreamGain
*/
extern SDL_DECLSPEC float SDLCALL SDL_GetAudioStreamGain(SDL_AudioStream *stream);
/**
* Change the gain of an audio stream.
*
* The gain of a stream is its volume; a larger gain means a louder output,
* with a gain of zero being silence.
*
* Audio streams default to a gain of 1.0f (no change in output).
*
* This is applied during SDL_GetAudioStreamData, and can be continuously
* changed to create various effects.
*
* \param stream the stream on which the gain is being changed.
* \param gain the gain. 1.0f is no change, 0.0f is silence.
* \returns 0 on success, or -1 on error.
*
* \threadsafety It is safe to call this function from any thread, as it holds
* a stream-specific mutex while running.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetAudioStreamGain
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain);
/**
* Add data to the stream.
*
@@ -1465,6 +1566,10 @@ extern SDL_DECLSPEC SDL_AudioStream *SDLCALL SDL_OpenAudioDeviceStream(SDL_Audio
* changed. However, this only covers frequency and channel count; data is
* always provided here in SDL_AUDIO_F32 format.
*
* The postmix callback runs _after_ logical device gain and audiostream gain
* have been applied, which is to say you can make the output data louder
* at this point than the gain settings would suggest.
*
* \param userdata a pointer provided by the app through
* SDL_SetAudioPostmixCallback, for its own use.
* \param spec the current format of audio that is to be submitted to the

View File

@@ -1103,7 +1103,7 @@ SDL_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));
const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamData(stream, device_buffer, buffer_size);
const int br = SDL_AtomicGet(&logdev->paused) ? 0 : SDL_GetAudioStreamDataAdjustGain(stream, device_buffer, buffer_size, logdev->gain);
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
failed = SDL_TRUE;
SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die.
@@ -1143,7 +1143,7 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
for iterating here because the binding linked list can only change while the device lock is held.
(we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
the same stream to different devices at the same time, though.) */
const int br = SDL_GetAudioStreamData(stream, device->work_buffer, work_buffer_size);
const int br = SDL_GetAudioStreamDataAdjustGain(stream, device->work_buffer, work_buffer_size, logdev->gain);
if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
failed = SDL_TRUE;
break;
@@ -1161,8 +1161,8 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
if (((Uint8 *) final_mix_buffer) != device_buffer) {
// !!! FIXME: we can't promise the device buf is aligned/padded for SIMD.
//ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device_buffer, device->spec.format, device->spec.channels, NULL, NULL);
ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device->work_buffer, device->spec.format, device->spec.channels, NULL, NULL);
//ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f);
ConvertAudio(needed_samples / device->spec.channels, final_mix_buffer, SDL_AUDIO_F32, device->spec.channels, NULL, device->work_buffer, device->spec.format, device->spec.channels, NULL, NULL, 1.0f);
SDL_memcpy(device_buffer, device->work_buffer, buffer_size);
}
}
@@ -1250,7 +1250,7 @@ SDL_bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
void *output_buffer = device->work_buffer;
// I don't know why someone would want a postmix on a recording device, but we offer it for API consistency.
if (logdev->postmix) {
if (logdev->postmix || (logdev->gain != 1.0f)) {
// move to float format.
SDL_AudioSpec outspec;
SDL_copyp(&outspec, &device->spec);
@@ -1258,13 +1258,15 @@ SDL_bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
output_buffer = device->postmix_buffer;
const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec);
br = frames * SDL_AUDIO_FRAMESIZE(outspec);
ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, NULL, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL, NULL);
ConvertAudio(frames, device->work_buffer, device->spec.format, outspec.channels, NULL, device->postmix_buffer, SDL_AUDIO_F32, outspec.channels, NULL, NULL, logdev->gain);
if (logdev->postmix) {
logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br);
}
}
for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// We should have updated this elsewhere if the format changed!
SDL_assert(stream->src_spec.format == (logdev->postmix ? SDL_AUDIO_F32 : device->spec.format));
SDL_assert(stream->src_spec.format == ((logdev->postmix || (logdev->gain != 1.0f)) ? SDL_AUDIO_F32 : device->spec.format));
SDL_assert(stream->src_spec.channels == device->spec.channels);
SDL_assert(stream->src_spec.freq == device->spec.freq);
@@ -1695,6 +1697,7 @@ SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSp
SDL_AtomicSet(&logdev->paused, 0);
retval = logdev->instance_id = AssignAudioDeviceInstanceId(device->recording, /*islogical=*/SDL_TRUE);
logdev->physical_device = device;
logdev->gain = 1.0f;
logdev->opened_as_default = wants_default;
logdev->next = device->logical_devices;
if (device->logical_devices) {
@@ -1752,6 +1755,44 @@ SDL_bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid)
return retval;
}
float SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid)
{
SDL_AudioDevice *device = NULL;
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device);
const float retval = logdev ? logdev->gain : -1.0f;
ReleaseAudioDevice(device);
return retval;
}
int SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain)
{
if (gain < 0.0f) {
return SDL_InvalidParamError("gain");
}
SDL_AudioDevice *device = NULL;
SDL_LogicalAudioDevice *logdev = ObtainLogicalAudioDevice(devid, &device);
int retval = -1;
if (logdev) {
logdev->gain = gain;
if (device->recording) {
const SDL_bool need_float32 = (logdev->postmix || logdev->gain != 1.0f);
for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// set the proper end of the stream to the device's format.
// SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec.
SDL_LockMutex(stream->lock);
stream->src_spec.format = need_float32 ? SDL_AUDIO_F32 : device->spec.format;
SDL_UnlockMutex(stream->lock);
}
}
UpdateAudioStreamFormatsPhysical(device);
retval = 0;
}
ReleaseAudioDevice(device);
return retval;
}
int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata)
{
SDL_AudioDevice *device = NULL;
@@ -1770,11 +1811,12 @@ int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallbac
logdev->postmix_userdata = userdata;
if (device->recording) {
const SDL_bool need_float32 = (callback || logdev->gain != 1.0f);
for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// set the proper end of the stream to the device's format.
// SDL_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec.
SDL_LockMutex(stream->lock);
stream->src_spec.format = callback ? SDL_AUDIO_F32 : device->spec.format;
stream->src_spec.format = need_float32 ? SDL_AUDIO_F32 : device->spec.format;
SDL_UnlockMutex(stream->lock);
}
}

View File

@@ -194,10 +194,14 @@ static void SwizzleAudio(const int num_frames, void *dst, const void *src, int c
//
// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes.
// If the scratch buffer is NULL, this restriction applies to the output buffer instead.
//
// Since this is a convenient point that audio goes through even if it doesn't need format conversion,
// we also handle gain adjustment here, so we don't have to make another pass over the data later.
// Strictly speaking, this is also a "conversion". :)
void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
void* scratch)
void* scratch, float gain)
{
SDL_assert(src != NULL);
SDL_assert(dst != NULL);
@@ -243,7 +247,7 @@ void ConvertAudio(int num_frames,
}
// see if we can skip float conversion entirely.
if (src_channels == dst_channels) {
if ((src_channels == dst_channels) && (gain == 1.0f)) {
if (src_format == dst_format) {
// nothing to do, we're already in the right format, just copy it over if necessary.
if (dst_map) {
@@ -280,6 +284,23 @@ void ConvertAudio(int num_frames,
src = buf;
}
// Gain adjustment
if (gain != 1.0f) {
float *buf = (float *)(dstconvert ? scratch : dst);
const int total_samples = num_frames * src_channels;
if (src == buf) {
for (int i = 0; i < total_samples; i++) {
buf[i] *= gain;
}
} else {
float *fsrc = (float *)src;
for (int i = 0; i < total_samples; i++) {
buf[i] = fsrc[i] * gain;
}
}
src = buf;
}
// Channel conversion
if (channelconvert) {
@@ -379,6 +400,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_
}
retval->freq_ratio = 1.0f;
retval->gain = 1.0f;
retval->queue = SDL_CreateAudioQueue(8192);
if (!retval->queue) {
@@ -593,6 +615,35 @@ int SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float freq_ratio)
return 0;
}
float SDL_GetAudioStreamGain(SDL_AudioStream *stream)
{
if (!stream) {
SDL_InvalidParamError("stream");
return -1.0f;
}
SDL_LockMutex(stream->lock);
const float gain = stream->gain;
SDL_UnlockMutex(stream->lock);
return gain;
}
int SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain)
{
if (!stream) {
return SDL_InvalidParamError("stream");
} else if (gain < 0.0f) {
return SDL_InvalidParamError("gain");
}
SDL_LockMutex(stream->lock);
stream->gain = gain;
SDL_UnlockMutex(stream->lock);
return 0;
}
static int CheckAudioStreamIsFullySetup(SDL_AudioStream *stream)
{
if (stream->src_spec.format == 0) {
@@ -820,7 +871,7 @@ static Sint64 GetAudioStreamHead(SDL_AudioStream* stream, SDL_AudioSpec* out_spe
// You must hold stream->lock and validate your parameters before calling this!
// Enough input data MUST be available!
static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int output_frames)
static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int output_frames, float gain)
{
const SDL_AudioSpec* src_spec = &stream->input_spec;
const SDL_AudioSpec* dst_spec = &stream->dst_spec;
@@ -854,7 +905,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
}
}
if (SDL_ReadFromAudioQueue(stream->queue, buf, dst_format, dst_channels, dst_map, 0, output_frames, 0, work_buffer) != buf) {
if (SDL_ReadFromAudioQueue(stream->queue, buf, dst_format, dst_channels, dst_map, 0, output_frames, 0, work_buffer, gain) != buf) {
return SDL_SetError("Not enough data in queue");
}
@@ -919,10 +970,15 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
return -1;
}
// adjust gain either before resampling or after, depending on which point has less
// samples to process.
const float preresample_gain = (input_frames > output_frames) ? 1.0f : gain;
const float postresample_gain = (input_frames > output_frames) ? gain : 1.0f;
// (dst channel map is NULL because we'll do the final swizzle on ConvertAudio after resample.)
const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue,
NULL, resample_format, resample_channels, NULL,
padding_frames, input_frames, padding_frames, work_buffer);
padding_frames, input_frames, padding_frames, work_buffer, preresample_gain);
if (!input_buffer) {
return SDL_SetError("Not enough data in queue (resample)");
@@ -939,13 +995,13 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
resample_rate, &stream->resample_offset);
// Convert to the final format, if necessary (src channel map is NULL because SDL_ReadFromAudioQueue already handled this).
ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, NULL, buf, dst_format, dst_channels, dst_map, work_buffer);
ConvertAudio(output_frames, resample_buffer, resample_format, resample_channels, NULL, buf, dst_format, dst_channels, dst_map, work_buffer, postresample_gain);
return 0;
}
// get converted/resampled data from the stream
int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float extra_gain)
{
Uint8 *buf = (Uint8 *) voidbuf;
@@ -970,6 +1026,7 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
return -1;
}
const float gain = stream->gain * extra_gain;
const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec);
len -= len % dst_frame_size; // chop off any fractional sample frame.
@@ -1029,7 +1086,7 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
output_frames = SDL_min(output_frames, chunk_size);
output_frames = (int) SDL_min(output_frames, available_frames);
if (GetAudioStreamDataInternal(stream, &buf[total], output_frames) != 0) {
if (GetAudioStreamDataInternal(stream, &buf[total], output_frames, gain) != 0) {
total = total ? total : -1;
break;
}
@@ -1046,6 +1103,11 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
return total;
}
int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
{
return SDL_GetAudioStreamDataAdjustGain(stream, voidbuf, len, 1.0f);
}
// number of converted/resampled bytes available for output
int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream)
{

View File

@@ -514,7 +514,7 @@ static const Uint8 *PeekIntoAudioQueueFuture(SDL_AudioQueue *queue, Uint8 *data,
const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames,
Uint8 *scratch)
Uint8 *scratch, float gain)
{
SDL_AudioTrack *track = queue->head;
@@ -552,7 +552,7 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
// Do we still need to copy/convert the data?
if (dst) {
ConvertAudio(past_frames + present_frames + future_frames, ptr,
src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
ptr = dst;
}
@@ -570,19 +570,19 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *ptr = dst;
if (src_past_bytes) {
ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
dst += dst_past_bytes;
scratch += dst_past_bytes;
}
if (src_present_bytes) {
ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
dst += dst_present_bytes;
scratch += dst_present_bytes;
}
if (src_future_bytes) {
ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch);
ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
dst += dst_future_bytes;
scratch += dst_future_bytes;
}

View File

@@ -69,7 +69,7 @@ size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_Audi
const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames,
Uint8 *scratch);
Uint8 *scratch, float gain);
// Get the total number of bytes currently queued
size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue);

View File

@@ -118,17 +118,19 @@ extern SDL_bool SDL_ChannelMapIsBogus(const Uint8 *map, int channels);
extern void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map,
void *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
void* scratch);
void* scratch, float gain);
// Compare two SDL_AudioSpecs, return SDL_TRUE if they match exactly.
// Using SDL_memcmp directly isn't safe, since potential padding (and unused parts of the channel map) might not be initialized.
extern SDL_bool SDL_AudioSpecsEqual(const SDL_AudioSpec *a, const SDL_AudioSpec *b);
// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this.
extern void OnAudioStreamCreated(SDL_AudioStream *stream);
extern void OnAudioStreamDestroy(SDL_AudioStream *stream);
// This just lets audio playback apply logical device gain at the same time as audiostream gain, so it's one multiplication instead of thousands.
extern int SDL_GetAudioStreamDataAdjustGain(SDL_AudioStream *stream, void *voidbuf, int len, float extra_gain);
typedef struct SDL_AudioDriverImpl
{
void (*DetectDevices)(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
@@ -196,6 +198,7 @@ struct SDL_AudioStream
SDL_AudioSpec src_spec;
SDL_AudioSpec dst_spec;
float freq_ratio;
float gain;
struct SDL_AudioQueue* queue;
@@ -230,6 +233,9 @@ struct SDL_LogicalAudioDevice
// If whole logical device is paused (process no streams bound to this device).
SDL_AtomicInt paused;
// Volume of the device output.
float gain;
// double-linked list of all audio streams currently bound to this opened device.
SDL_AudioStream *bound_streams;

View File

@@ -167,6 +167,7 @@ SDL3_0.0.0 {
SDL_GetAssertionHandler;
SDL_GetAssertionReport;
SDL_GetAudioDeviceFormat;
SDL_GetAudioDeviceGain;
SDL_GetAudioDeviceName;
SDL_GetAudioDriver;
SDL_GetAudioPlaybackDevices;
@@ -176,6 +177,7 @@ SDL3_0.0.0 {
SDL_GetAudioStreamDevice;
SDL_GetAudioStreamFormat;
SDL_GetAudioStreamFrequencyRatio;
SDL_GetAudioStreamGain;
SDL_GetAudioStreamProperties;
SDL_GetAudioStreamQueued;
SDL_GetBasePath;
@@ -682,9 +684,11 @@ SDL3_0.0.0 {
SDL_SendJoystickEffect;
SDL_SendJoystickVirtualSensorData;
SDL_SetAssertionHandler;
SDL_SetAudioDeviceGain;
SDL_SetAudioPostmixCallback;
SDL_SetAudioStreamFormat;
SDL_SetAudioStreamFrequencyRatio;
SDL_SetAudioStreamGain;
SDL_SetAudioStreamGetCallback;
SDL_SetAudioStreamPutCallback;
SDL_SetBooleanProperty;

View File

@@ -191,6 +191,7 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
@@ -201,6 +202,7 @@
#define SDL_GetAudioStreamDevice SDL_GetAudioStreamDevice_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL
#define SDL_GetAudioStreamGain SDL_GetAudioStreamGain_REAL
#define SDL_GetAudioStreamProperties SDL_GetAudioStreamProperties_REAL
#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL
@@ -707,9 +709,11 @@
#define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL
#define SDL_SendJoystickVirtualSensorData SDL_SendJoystickVirtualSensorData_REAL
#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
#define SDL_SetAudioDeviceGain SDL_SetAudioDeviceGain_REAL
#define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL
#define SDL_SetAudioStreamGain SDL_SetAudioStreamGain_REAL
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
#define SDL_SetBooleanProperty SDL_SetBooleanProperty_REAL

View File

@@ -212,6 +212,7 @@ SDL_DYNAPI_PROC(int,SDL_GetAndroidSDKVersion,(void),(),return)
SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioDeviceGain,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int a),(a),return)
SDL_DYNAPI_PROC(SDL_AudioDeviceID*,SDL_GetAudioPlaybackDevices,(int *a),(a),return)
@@ -221,6 +222,7 @@ SDL_DYNAPI_PROC(int,SDL_GetAudioStreamData,(SDL_AudioStream *a, void *b, int c),
SDL_DYNAPI_PROC(SDL_AudioDeviceID,SDL_GetAudioStreamDevice,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamFormat,(SDL_AudioStream *a, SDL_AudioSpec *b, SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioStreamFrequencyRatio,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(float,SDL_GetAudioStreamGain,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetAudioStreamProperties,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),return)
@@ -718,9 +720,11 @@ SDL_DYNAPI_PROC(int,SDL_SendGamepadEffect,(SDL_Gamepad *a, const void *b, int c)
SDL_DYNAPI_PROC(int,SDL_SendJoystickEffect,(SDL_Joystick *a, const void *b, int c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SendJoystickVirtualSensorData,(SDL_Joystick *a, SDL_SensorType b, Uint64 c, const float *d, int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),)
SDL_DYNAPI_PROC(int,SDL_SetAudioDeviceGain,(SDL_AudioDeviceID 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_SetAudioStreamFormat,(SDL_AudioStream *a, const SDL_AudioSpec *b, const SDL_AudioSpec *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamFrequencyRatio,(SDL_AudioStream *a, float b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGain,(SDL_AudioStream *a, float b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamGetCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetAudioStreamPutCallback,(SDL_AudioStream *a, SDL_AudioStreamCallback b, void *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)

View File

@@ -94,6 +94,7 @@ struct Thing
float z;
Uint8 r, g, b, a;
float progress;
float meter;
float scale;
Uint64 createticks;
Texture *texture;
@@ -103,6 +104,7 @@ struct Thing
void (*ondrag)(Thing *thing, int button, float x, float y);
void (*ondrop)(Thing *thing, int button, float x, float y);
void (*ondraw)(Thing *thing, SDL_Renderer *renderer);
void (*onmousewheel)(Thing *thing, float y);
Thing *prev;
Thing *next;
@@ -355,6 +357,16 @@ static void DrawOneThing(SDL_Renderer *renderer, Thing *thing)
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128);
SDL_RenderFillRect(renderer, &r);
}
if (thing->meter > 0.0f) {
SDL_FRect r;
r.w = 10.0f;
r.h = thing->rect.h * ((thing->meter > 1.0f) ? 1.0f : thing->meter);
r.x = thing->rect.x + thing->rect.w + 2.0f;
r.y = (thing->rect.y + thing->rect.h) - r.h;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128);
SDL_RenderFillRect(renderer, &r);
}
}
static void DrawThings(SDL_Renderer *renderer)
@@ -581,6 +593,13 @@ static void StreamThing_ondrop(Thing *thing, int button, float x, float y)
}
}
static void StreamThing_onmousewheel(Thing *thing, float y)
{
thing->meter += y * 0.01f;
thing->meter = SDL_clamp(thing->meter, 0.0f, 1.0f);
SDL_SetAudioStreamGain(thing->data.stream.stream, thing->meter);
}
static void StreamThing_ondraw(Thing *thing, SDL_Renderer *renderer)
{
if (thing->line_connected_to) { /* are we playing? Update progress bar, and bounce the levels a little. */
@@ -618,7 +637,9 @@ static Thing *CreateStreamThing(const SDL_AudioSpec *spec, const Uint8 *buf, con
thing->ondrag = StreamThing_ondrag;
thing->ondrop = StreamThing_ondrop;
thing->ondraw = StreamThing_ondraw;
thing->onmousewheel = StreamThing_onmousewheel;
thing->can_be_dropped_onto = can_be_dropped_onto;
thing->meter = 1.0f; /* gain. */
}
return thing;
}
@@ -898,6 +919,13 @@ static void LogicalDeviceThing_ondraw(Thing *thing, SDL_Renderer *renderer)
}
}
static void LogicalDeviceThing_onmousewheel(Thing *thing, float y)
{
thing->meter += y * 0.01f;
thing->meter = SDL_clamp(thing->meter, 0.0f, 1.0f);
SDL_SetAudioDeviceGain(thing->data.logdev.devid, thing->meter);
}
static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID which, const float x, const float y)
{
static const ThingType can_be_dropped_onto[] = { THING_TRASHCAN, THING_NULL };
@@ -917,10 +945,12 @@ static Thing *CreateLogicalDeviceThing(Thing *parent, const SDL_AudioDeviceID wh
SDL_SetTextureBlendMode(thing->data.logdev.visualizer, SDL_BLENDMODE_BLEND);
}
thing->line_connected_to = physthing;
thing->meter = 1.0f;
thing->ontick = LogicalDeviceThing_ontick;
thing->ondrag = DeviceThing_ondrag;
thing->ondrop = LogicalDeviceThing_ondrop;
thing->ondraw = LogicalDeviceThing_ondraw;
thing->onmousewheel = LogicalDeviceThing_onmousewheel;
thing->can_be_dropped_onto = can_be_dropped_onto;
SetLogicalDeviceTitlebar(thing);
@@ -1181,7 +1211,10 @@ int SDL_AppEvent(void *appstate, const SDL_Event *event)
break;
case SDL_EVENT_MOUSE_WHEEL:
UpdateMouseOver(event->wheel.mouse_x, event->wheel.mouse_y);
thing = UpdateMouseOver(event->wheel.mouse_x, event->wheel.mouse_y);
if (thing && thing->onmousewheel) {
thing->onmousewheel(thing, event->wheel.y * ((event->wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f));
}
break;
case SDL_EVENT_KEY_DOWN: