From e128a6aca198d9797d3eb87fc4155106feba651f Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sat, 29 Aug 2026 19:51:25 +0800 Subject: [PATCH] render: clamp mixed audio to [-1,1] before it reaches the device Overlapping clips sum linearly in mix_audio_montage and can exceed full scale (two hot clips reach +/-2; gain > 1 would too); the cpal sink forwarded samples unclamped, so overlaps clipped at the DAC. Clamp the accumulator after the montage mix (both the heap and shm-slot paths share mix_audio_montage) and document it on render_audio_samples. --- crates/oak-render/src/eval.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/oak-render/src/eval.rs b/crates/oak-render/src/eval.rs index 6f51bf00a..223fd0f58 100644 --- a/crates/oak-render/src/eval.rs +++ b/crates/oak-render/src/eval.rs @@ -1169,7 +1169,8 @@ pub fn render_graph_frame( /// Render the audio montage over `params.range` (M12 P1): every clip /// overlapping the range is decoded (interleaved f32 at the output rate -/// and layout) and mixed with its gain; uncovered parts stay silent. +/// and layout) and mixed with its gain; uncovered parts stay silent. The +/// mixed output is clamped to [-1, 1]. pub fn render_audio_samples( params: &crate::ticket::AudioTicketParams, ) -> Result { @@ -1273,6 +1274,12 @@ fn mix_audio_montage( acc[start_frame * channels as usize + i] += buf[i] * clip.gain; } } + // Clamp to [-1, 1]: overlapping clips sum linearly and can exceed + // full scale, and the playback sink (cpal) forwards samples without + // any clamping of its own. + for sample in acc.iter_mut() { + *sample = sample.clamp(-1.0, 1.0); + } Ok(()) }