Merge pull request #207 from Jafz2001/perf/adaptive-meters

perf: adapt meter polling and repaint rate
This commit is contained in:
Brandon Thomas
2026-10-11 20:04:56 -04:00
committed by GitHub
2 changed files with 48 additions and 2 deletions
+30
View File
@@ -638,6 +638,21 @@ impl Player {
pub fn meters(&self) -> MeterSnapshot {
self.shared.meters.lock().unwrap_or_else(PoisonError::into_inner).clone()
}
/// Copy meters into reusable UI storage. Once track/channel counts are stable this performs
/// no allocation, unlike cloning a new snapshot every video frame.
pub fn copy_meters(&self, out: &mut MeterSnapshot) {
let source = self.shared.meters.lock().unwrap_or_else(PoisonError::into_inner);
copy_meter_snapshot(&source, out);
}
}
fn copy_meter_snapshot(source: &MeterSnapshot, out: &mut MeterSnapshot) {
out.tracks.retain(|id, _| source.tracks.contains_key(id));
for (id, meter) in &source.tracks {
out.tracks.entry(*id).or_default().copy_from(meter);
}
out.main.copy_from(&source.main);
}
impl Side {
@@ -735,6 +750,21 @@ fn open_device(want_rate: u32, want_ch: u16) -> Result<(cpal::Device, cpal::Stre
mod tests {
use super::*;
#[test]
fn reusable_meter_snapshot_updates_and_drops_stale_tracks() {
let keep = TrackId(1);
let stale = TrackId(2);
let mut source = MeterSnapshot::default();
source.tracks.insert(keep, StripMeter { peak: [0.5, 0.25], peaks: vec![0.5, 0.25], gain_reduction_db: 3.0 });
source.main.peaks = vec![0.75, 0.5];
let mut out = MeterSnapshot::default();
out.tracks.insert(stale, StripMeter::default());
copy_meter_snapshot(&source, &mut out);
assert_eq!(out.tracks.len(), 1);
assert_eq!(out.tracks[&keep], source.tracks[&keep]);
assert_eq!(out.main, source.main);
}
#[test]
fn audio_state_plays_and_stops_at_end() {
let s = Arc::new(soundcraft_model::Session::default());
+18 -2
View File
@@ -220,6 +220,8 @@ pub struct SoundApp {
pub dialogs: dialogs::Dialogs,
pub meters: HashMap<TrackId, MeterDisplay>,
pub main_meter: MeterDisplay,
/// Reused multichannel snapshot; populated at an adaptive rate by `logic`.
meter_snapshot: soundcraft_playback::MeterSnapshot,
pub gesture: Option<Gesture>,
pub edit_layout: edit_window::EditLayout,
pub midi: midi_editor::MidiEditorState,
@@ -247,6 +249,7 @@ pub struct SoundApp {
last_write_at: Samples,
pub frame_ms: f32,
last_frame: Option<f64>,
last_meter_poll: f64,
/// Movies for the Video window and Video track (decoded in the background).
pub video: video_track::VideoPool,
}
@@ -280,6 +283,7 @@ impl SoundApp {
dialogs: dialogs::Dialogs::default(),
meters: HashMap::new(),
main_meter: MeterDisplay::default(),
meter_snapshot: soundcraft_playback::MeterSnapshot::default(),
gesture: None,
edit_layout: edit_window::EditLayout::default(),
midi: midi_editor::MidiEditorState::default(),
@@ -302,6 +306,7 @@ impl SoundApp {
last_write_at: i64::MIN,
frame_ms: 0.0,
last_frame: None,
last_meter_poll: f64::NEG_INFINITY,
video: video_track::VideoPool::default(),
}
}
@@ -727,7 +732,13 @@ impl SoundApp {
if self.engine.transport.playing {
self.engine.transport.position = p.position();
}
let snap = p.meters();
let now = self.last_frame.unwrap_or_default();
let meter_interval = if self.ui.window == MainWindow::Mix { 1.0 / 60.0 } else { 1.0 / 20.0 };
if now - self.last_meter_poll >= meter_interval {
p.copy_meters(&mut self.meter_snapshot);
self.last_meter_poll = now;
}
let snap = &self.meter_snapshot;
for (id, m) in &snap.tracks {
let d = self.meters.entry(*id).or_default();
feed_meter(d, m.peak, m.gain_reduction_db, dt);
@@ -807,7 +818,12 @@ impl SoundApp {
{
self.ensure_input();
}
if self.is_playing() || self.meters.values().any(|m| m.level[0] > 0.0001) || !self.pending_shots.is_empty() || !self.synthetic.is_empty() {
if self.is_playing() {
let hz = if self.ui.window == MainWindow::Mix { 60 } else { 30 };
ctx.request_repaint_after(std::time::Duration::from_millis(1000 / hz));
} else if self.meters.values().any(|m| m.level[0] > 0.0001) {
ctx.request_repaint_after(std::time::Duration::from_millis(80));
} else if !self.pending_shots.is_empty() || !self.synthetic.is_empty() {
ctx.request_repaint();
}
control::collect_screenshots(self, ctx);