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.
This commit is contained in:
2026-08-29 19:51:25 +08:00
parent 6fb9a80afc
commit e128a6aca1
+8 -1
View File
@@ -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<crate::ticket::TicketPayload> {
@@ -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(())
}