perf: coalesce per-peer audio levels to ~10/sec for the UI

The mixer ran at the fixed 20ms playback cadence and pushed an
AudioLevels event every tick — ~50/sec — each waking the iced runtime
for a full re-render. Levels are now peak-held per peer across a 100ms
window and emitted once per window (~10/sec), cutting UI-bound events
5x. Peak-hold (rather than last-sample) means a brief speech transient
inside a window still registers, so the speaking indicator stays
responsive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 17:15:06 -04:00
co-authored by Claude Opus 4.8
parent ccf2bff87c
commit 66c912e279
+21 -6
View File
@@ -325,20 +325,28 @@ async fn run_core_loop(
let mut interval = tokio::time::interval(Duration::from_millis(20));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
// The 20ms mix cadence is fixed by playback, but pushing a
// level event every tick floods the UI runtime at ~50/sec. We
// peak-hold per-peer levels across this many ticks and emit
// once per window (~10/sec) — peak-hold so a brief transient
// inside the window still lights the speaking indicator.
const LEVEL_EMIT_TICKS: u32 = 5;
let mut level_peaks: HashMap<EndpointId, f32> = HashMap::new();
let mut ticks_since_emit: u32 = 0;
loop {
interval.tick().await;
let current_volumes = peer_volumes_mixer.lock().await.clone();
let mut active_levels = Vec::new();
let mut peer_frames = Vec::new();
{
let mut guard = jitter_mixer.lock().await;
for (&peer_id, buffer) in guard.iter_mut() {
// `None` means idle/buffering: contribute nothing
// and report a zero level so the UI shows idle.
// `None` means idle/buffering: contribute nothing,
// but keep a (zero) entry so the UI sees it idle.
let Some(mut frame) = buffer.pop_frame() else {
active_levels.push((peer_id, 0.0));
level_peaks.entry(peer_id).or_insert(0.0);
continue;
};
@@ -352,7 +360,8 @@ async fn run_core_loop(
let sum_sq: f32 = frame.iter().map(|&x| (x as f32).powi(2)).sum();
let rms = (sum_sq / frame.len().max(1) as f32).sqrt();
let level = (rms / 32768.0).clamp(0.0, 1.0);
active_levels.push((peer_id, level));
let peak = level_peaks.entry(peer_id).or_insert(0.0);
*peak = peak.max(level);
peer_frames.push(frame);
}
@@ -379,7 +388,13 @@ async fn run_core_loop(
break;
}
let _ = ui_tx_mixer.send(UiEvent::AudioLevels(active_levels)).await;
// Emit coalesced peaks once per window, then reset.
ticks_since_emit += 1;
if ticks_since_emit >= LEVEL_EMIT_TICKS {
let levels: Vec<(EndpointId, f32)> = level_peaks.drain().collect();
let _ = ui_tx_mixer.send(UiEvent::AudioLevels(levels)).await;
ticks_since_emit = 0;
}
}
});