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); 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. * 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); 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. * 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 * changed. However, this only covers frequency and channel count; data is
* always provided here in SDL_AUDIO_F32 format. * 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 * \param userdata a pointer provided by the app through
* SDL_SetAudioPostmixCallback, for its own use. * SDL_SetAudioPostmixCallback, for its own use.
* \param spec the current format of audio that is to be submitted to the * \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! // We should have updated this elsewhere if the format changed!
SDL_assert(SDL_AudioSpecsEqual(&stream->dst_spec, &device->spec)); 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. if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
failed = SDL_TRUE; failed = SDL_TRUE;
SDL_memset(device_buffer, device->silence_value, buffer_size); // just supply silence to the device before we die. 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. 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 (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.) */ 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. if (br < 0) { // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
failed = SDL_TRUE; failed = SDL_TRUE;
break; break;
@@ -1161,8 +1161,8 @@ SDL_bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
if (((Uint8 *) final_mix_buffer) != device_buffer) { if (((Uint8 *) final_mix_buffer) != device_buffer) {
// !!! FIXME: we can't promise the device buf is aligned/padded for SIMD. // !!! 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_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); 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); 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; 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. // 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. // move to float format.
SDL_AudioSpec outspec; SDL_AudioSpec outspec;
SDL_copyp(&outspec, &device->spec); SDL_copyp(&outspec, &device->spec);
@@ -1258,13 +1258,15 @@ SDL_bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
output_buffer = device->postmix_buffer; output_buffer = device->postmix_buffer;
const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec); const int frames = br / SDL_AUDIO_FRAMESIZE(device->spec);
br = frames * SDL_AUDIO_FRAMESIZE(outspec); 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);
logdev->postmix(logdev->postmix_userdata, &outspec, device->postmix_buffer, br); 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) { for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// We should have updated this elsewhere if the format changed! // 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.channels == device->spec.channels);
SDL_assert(stream->src_spec.freq == device->spec.freq); 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); SDL_AtomicSet(&logdev->paused, 0);
retval = logdev->instance_id = AssignAudioDeviceInstanceId(device->recording, /*islogical=*/SDL_TRUE); retval = logdev->instance_id = AssignAudioDeviceInstanceId(device->recording, /*islogical=*/SDL_TRUE);
logdev->physical_device = device; logdev->physical_device = device;
logdev->gain = 1.0f;
logdev->opened_as_default = wants_default; logdev->opened_as_default = wants_default;
logdev->next = device->logical_devices; logdev->next = device->logical_devices;
if (device->logical_devices) { if (device->logical_devices) {
@@ -1752,6 +1755,44 @@ SDL_bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid)
return retval; 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) int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata)
{ {
SDL_AudioDevice *device = NULL; SDL_AudioDevice *device = NULL;
@@ -1770,11 +1811,12 @@ int SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallbac
logdev->postmix_userdata = userdata; logdev->postmix_userdata = userdata;
if (device->recording) { 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) { for (SDL_AudioStream *stream = logdev->bound_streams; stream; stream = stream->next_binding) {
// set the proper end of the stream to the device's format. // 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_SetAudioStreamFormat does a ton of validation just to memcpy an audiospec.
SDL_LockMutex(stream->lock); 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); 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. // 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. // 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, void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map, 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 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
void* scratch) void* scratch, float gain)
{ {
SDL_assert(src != NULL); SDL_assert(src != NULL);
SDL_assert(dst != NULL); SDL_assert(dst != NULL);
@@ -243,7 +247,7 @@ void ConvertAudio(int num_frames,
} }
// see if we can skip float conversion entirely. // 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) { if (src_format == dst_format) {
// nothing to do, we're already in the right format, just copy it over if necessary. // nothing to do, we're already in the right format, just copy it over if necessary.
if (dst_map) { if (dst_map) {
@@ -280,6 +284,23 @@ void ConvertAudio(int num_frames,
src = buf; 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 // Channel conversion
if (channelconvert) { if (channelconvert) {
@@ -379,6 +400,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_
} }
retval->freq_ratio = 1.0f; retval->freq_ratio = 1.0f;
retval->gain = 1.0f;
retval->queue = SDL_CreateAudioQueue(8192); retval->queue = SDL_CreateAudioQueue(8192);
if (!retval->queue) { if (!retval->queue) {
@@ -593,6 +615,35 @@ int SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float freq_ratio)
return 0; 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) static int CheckAudioStreamIsFullySetup(SDL_AudioStream *stream)
{ {
if (stream->src_spec.format == 0) { 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! // You must hold stream->lock and validate your parameters before calling this!
// Enough input data MUST be available! // 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* src_spec = &stream->input_spec;
const SDL_AudioSpec* dst_spec = &stream->dst_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"); return SDL_SetError("Not enough data in queue");
} }
@@ -919,10 +970,15 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int ou
return -1; 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.) // (dst channel map is NULL because we'll do the final swizzle on ConvertAudio after resample.)
const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue, const Uint8* input_buffer = SDL_ReadFromAudioQueue(stream->queue,
NULL, resample_format, resample_channels, NULL, 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) { if (!input_buffer) {
return SDL_SetError("Not enough data in queue (resample)"); 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); resample_rate, &stream->resample_offset);
// Convert to the final format, if necessary (src channel map is NULL because SDL_ReadFromAudioQueue already handled this). // 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; return 0;
} }
// get converted/resampled data from the stream // 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; Uint8 *buf = (Uint8 *) voidbuf;
@@ -970,6 +1026,7 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
return -1; return -1;
} }
const float gain = stream->gain * extra_gain;
const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec); const int dst_frame_size = SDL_AUDIO_FRAMESIZE(stream->dst_spec);
len -= len % dst_frame_size; // chop off any fractional sample frame. 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 = SDL_min(output_frames, chunk_size);
output_frames = (int) SDL_min(output_frames, available_frames); 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; total = total ? total : -1;
break; break;
} }
@@ -1046,6 +1103,11 @@ int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *voidbuf, int len)
return total; 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 // number of converted/resampled bytes available for output
int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream) 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, const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch) Uint8 *scratch, float gain)
{ {
SDL_AudioTrack *track = queue->head; 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? // Do we still need to copy/convert the data?
if (dst) { if (dst) {
ConvertAudio(past_frames + present_frames + future_frames, ptr, 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; ptr = dst;
} }
@@ -570,19 +570,19 @@ const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *ptr = dst; Uint8 *ptr = dst;
if (src_past_bytes) { 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; dst += dst_past_bytes;
scratch += dst_past_bytes; scratch += dst_past_bytes;
} }
if (src_present_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; dst += dst_present_bytes;
scratch += dst_present_bytes; scratch += dst_present_bytes;
} }
if (src_future_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; dst += dst_future_bytes;
scratch += 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, const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map, Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const Uint8 *dst_map,
int past_frames, int present_frames, int future_frames, int past_frames, int present_frames, int future_frames,
Uint8 *scratch); Uint8 *scratch, float gain);
// Get the total number of bytes currently queued // Get the total number of bytes currently queued
size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue); 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, extern void ConvertAudio(int num_frames,
const void *src, SDL_AudioFormat src_format, int src_channels, const Uint8 *src_map, 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 *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. // 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. // 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); 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. // 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 OnAudioStreamCreated(SDL_AudioStream *stream);
extern void OnAudioStreamDestroy(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 typedef struct SDL_AudioDriverImpl
{ {
void (*DetectDevices)(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); void (*DetectDevices)(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording);
@@ -196,6 +198,7 @@ struct SDL_AudioStream
SDL_AudioSpec src_spec; SDL_AudioSpec src_spec;
SDL_AudioSpec dst_spec; SDL_AudioSpec dst_spec;
float freq_ratio; float freq_ratio;
float gain;
struct SDL_AudioQueue* queue; struct SDL_AudioQueue* queue;
@@ -230,6 +233,9 @@ struct SDL_LogicalAudioDevice
// If whole logical device is paused (process no streams bound to this device). // If whole logical device is paused (process no streams bound to this device).
SDL_AtomicInt paused; SDL_AtomicInt paused;
// Volume of the device output.
float gain;
// double-linked list of all audio streams currently bound to this opened device. // double-linked list of all audio streams currently bound to this opened device.
SDL_AudioStream *bound_streams; SDL_AudioStream *bound_streams;

View File

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

View File

@@ -191,6 +191,7 @@
#define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL #define SDL_GetAndroidSDKVersion SDL_GetAndroidSDKVersion_REAL
#define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL #define SDL_GetAssertionHandler SDL_GetAssertionHandler_REAL
#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL #define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
#define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL
#define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL #define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL
#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL #define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL #define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
@@ -201,6 +202,7 @@
#define SDL_GetAudioStreamDevice SDL_GetAudioStreamDevice_REAL #define SDL_GetAudioStreamDevice SDL_GetAudioStreamDevice_REAL
#define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL #define SDL_GetAudioStreamFormat SDL_GetAudioStreamFormat_REAL
#define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL #define SDL_GetAudioStreamFrequencyRatio SDL_GetAudioStreamFrequencyRatio_REAL
#define SDL_GetAudioStreamGain SDL_GetAudioStreamGain_REAL
#define SDL_GetAudioStreamProperties SDL_GetAudioStreamProperties_REAL #define SDL_GetAudioStreamProperties SDL_GetAudioStreamProperties_REAL
#define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL #define SDL_GetAudioStreamQueued SDL_GetAudioStreamQueued_REAL
#define SDL_GetBasePath SDL_GetBasePath_REAL #define SDL_GetBasePath SDL_GetBasePath_REAL
@@ -707,9 +709,11 @@
#define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL #define SDL_SendJoystickEffect SDL_SendJoystickEffect_REAL
#define SDL_SendJoystickVirtualSensorData SDL_SendJoystickVirtualSensorData_REAL #define SDL_SendJoystickVirtualSensorData SDL_SendJoystickVirtualSensorData_REAL
#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL #define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
#define SDL_SetAudioDeviceGain SDL_SetAudioDeviceGain_REAL
#define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL #define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL
#define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL #define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL
#define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL #define SDL_SetAudioStreamFrequencyRatio SDL_SetAudioStreamFrequencyRatio_REAL
#define SDL_SetAudioStreamGain SDL_SetAudioStreamGain_REAL
#define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL #define SDL_SetAudioStreamGetCallback SDL_SetAudioStreamGetCallback_REAL
#define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL #define SDL_SetAudioStreamPutCallback SDL_SetAudioStreamPutCallback_REAL
#define SDL_SetBooleanProperty SDL_SetBooleanProperty_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(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),return)
SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),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(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_GetAudioDeviceName,(SDL_AudioDeviceID a),(a),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAudioDriver,(int 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) 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(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(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_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(SDL_PropertiesID,SDL_GetAudioStreamProperties,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetAudioStreamQueued,(SDL_AudioStream *a),(a),return)
SDL_DYNAPI_PROC(char*,SDL_GetBasePath,(void),(),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_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(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(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_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_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_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_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_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) 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; float z;
Uint8 r, g, b, a; Uint8 r, g, b, a;
float progress; float progress;
float meter;
float scale; float scale;
Uint64 createticks; Uint64 createticks;
Texture *texture; Texture *texture;
@@ -103,6 +104,7 @@ struct Thing
void (*ondrag)(Thing *thing, int button, float x, float y); void (*ondrag)(Thing *thing, int button, float x, float y);
void (*ondrop)(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 (*ondraw)(Thing *thing, SDL_Renderer *renderer);
void (*onmousewheel)(Thing *thing, float y);
Thing *prev; Thing *prev;
Thing *next; Thing *next;
@@ -355,6 +357,16 @@ static void DrawOneThing(SDL_Renderer *renderer, Thing *thing)
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128);
SDL_RenderFillRect(renderer, &r); 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) 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) 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. */ 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->ondrag = StreamThing_ondrag;
thing->ondrop = StreamThing_ondrop; thing->ondrop = StreamThing_ondrop;
thing->ondraw = StreamThing_ondraw; thing->ondraw = StreamThing_ondraw;
thing->onmousewheel = StreamThing_onmousewheel;
thing->can_be_dropped_onto = can_be_dropped_onto; thing->can_be_dropped_onto = can_be_dropped_onto;
thing->meter = 1.0f; /* gain. */
} }
return thing; 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 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 }; 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); SDL_SetTextureBlendMode(thing->data.logdev.visualizer, SDL_BLENDMODE_BLEND);
} }
thing->line_connected_to = physthing; thing->line_connected_to = physthing;
thing->meter = 1.0f;
thing->ontick = LogicalDeviceThing_ontick; thing->ontick = LogicalDeviceThing_ontick;
thing->ondrag = DeviceThing_ondrag; thing->ondrag = DeviceThing_ondrag;
thing->ondrop = LogicalDeviceThing_ondrop; thing->ondrop = LogicalDeviceThing_ondrop;
thing->ondraw = LogicalDeviceThing_ondraw; thing->ondraw = LogicalDeviceThing_ondraw;
thing->onmousewheel = LogicalDeviceThing_onmousewheel;
thing->can_be_dropped_onto = can_be_dropped_onto; thing->can_be_dropped_onto = can_be_dropped_onto;
SetLogicalDeviceTitlebar(thing); SetLogicalDeviceTitlebar(thing);
@@ -1181,7 +1211,10 @@ int SDL_AppEvent(void *appstate, const SDL_Event *event)
break; break;
case SDL_EVENT_MOUSE_WHEEL: 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; break;
case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_DOWN: