From b5c3743d9e94c18e39794d19185ac0e23744e75d Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Sun, 2 Aug 2026 18:12:03 +0800 Subject: [PATCH] fix(ui): latch the DSP percentage readout at ~2 Hz The DSP number re-rendered the audio callback's live load figure every UI frame, making it unreadable; the CPU percentage next to it is sampled once a second and reads fine. Latch the displayed DSP value (and its threshold color) on the same slow path, updated every 0.5 s. The bar fill keeps tracking the live value. Co-Authored-By: Claude Fable 5 --- crates/halo/src/app.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/halo/src/app.rs b/crates/halo/src/app.rs index 2f6776a..97d97bc 100644 --- a/crates/halo/src/app.rs +++ b/crates/halo/src/app.rs @@ -517,6 +517,9 @@ pub struct HaloApp { kb_prev: [[bool; 8]; 2], /// Process CPU sampling: (last wall instant, last cpu seconds, percent). cpu_sample: (std::time::Instant, f64, f32), + /// DSP percentage latched for the toolbar readout: (last update, value). + /// The number would be unreadable at frame rate; the bar stays live. + dsp_display: (std::time::Instant, f32), status: String, /// Absolute x of the mixer column's center, captured while rendering the /// central panel and read a frame later by the toolbar to center the @@ -670,6 +673,7 @@ impl HaloApp { available_devices: Vec::new(), kb_prev: [[false; 8]; 2], cpu_sample: (std::time::Instant::now(), process_cpu_secs(), 0.0), + dsp_display: (std::time::Instant::now(), 0.0), status, mixer_center_x: 0.0, }; @@ -1657,7 +1661,8 @@ impl HaloApp { } } - /// Sample process-wide CPU roughly once a second. + /// Sample process-wide CPU roughly once a second, and latch the DSP + /// readout a couple of times a second. fn update_cpu(&mut self) { let (last_at, last_cpu, _) = self.cpu_sample; let elapsed = last_at.elapsed().as_secs_f64(); @@ -1666,6 +1671,9 @@ impl HaloApp { let pct = ((cpu_now - last_cpu) / elapsed * 100.0) as f32; self.cpu_sample = (std::time::Instant::now(), cpu_now, pct.max(0.0)); } + if self.dsp_display.0.elapsed().as_secs_f32() >= 0.5 { + self.dsp_display = (std::time::Instant::now(), self.mixer.cpu_load.load()); + } } fn settings_window(&mut self, ctx: &egui::Context) { @@ -2637,7 +2645,12 @@ impl eframe::App for HaloApp { } } ui.separator(); - cpu_meter(ui, self.mixer.cpu_load.load(), self.cpu_sample.2); + cpu_meter( + ui, + self.mixer.cpu_load.load(), + self.dsp_display.1, + self.cpu_sample.2, + ); ui.separator(); // Right-to-left layout: the output bar lands between // the DSP cluster and the master knob, i.e. just to @@ -4305,9 +4318,10 @@ fn master_level_meter(ui: &mut egui::Ui, level: f32) { /// Toolbar meter: DSP = audio-callback load (render time ÷ buffer time, /// the number that matters for dropouts) with a bar; CPU = whole-process -/// usage. -fn cpu_meter(ui: &mut egui::Ui, dsp_load: f32, process_pct: f32) { - let dsp_pct = (dsp_load * 100.0).clamp(0.0, 999.0); +/// usage. The bar fill tracks `dsp_load` live; the percentage text and +/// color use `dsp_latched` (updated ~2×/s) so the readout is legible. +fn cpu_meter(ui: &mut egui::Ui, dsp_load: f32, dsp_latched: f32, process_pct: f32) { + let dsp_pct = (dsp_latched * 100.0).clamp(0.0, 999.0); let color = if dsp_pct < 50.0 { egui::Color32::from_rgb(110, 200, 110) } else if dsp_pct < 80.0 {