feat: decouple plot resolution from frame rate via a 200 Hz ring buffer

Feed the scrolling plot from a lock-free SPSC ScopeRing instead of sampling one
atomic per egui frame, so horizontal resolution is set by the audio-clocked
bucket rate (~200 Hz) rather than the ~60 fps repaint. process() accumulates a
bucket every sample_rate/BUCKET_HZ samples (peak-preserving, spanning blocks)
and pushes it; the editor drains all new buckets each frame and folds them into
PLOT_N columns. Fast transients between frames are no longer dropped, and the
plot is now audio-clocked (freezes on pause, falls to silence on stop/reset).

Drop the per-frame plot_* atomics (the per-channel lamp now reads the decayed
bar level). Also fix the area fill: render it as a strip of per-segment convex
quads instead of one concave polygon, which egui fan-filled from a corner and
left stray triangles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2026-06-24 14:53:00 +09:00
parent fe4033b772
commit 9a60b0ea72
4 changed files with 209 additions and 98 deletions
+1 -1
View File
@@ -84,7 +84,7 @@ pub(super) fn draw(ui: &mut egui::Ui, meters: &Meters, state: &mut MeterState) {
// Per-channel lamp: latch on output reaching 0 dBFS; the ALL channel also latches when the
// output limiter is catching peaks (the true master-ceiling event).
let over_db = util::gain_to_db(meters.plot_out[i].load(Ordering::Relaxed));
let over_db = l_db.max(r_db);
let mut triggered = over_db >= OVER_DB;
if i == NUM_CHANNELS - 1 {
triggered |= meters.limiter_gr_db.load(Ordering::Relaxed) > LAMP_TRIGGER_DB;