Merge pull request #187 from grovebit/meter-fixes
Keep armed tracks' meters live while stopped; make Clear All Clip Indicators work
This commit is contained in:
commit
a10eb47032
|
|
@ -168,6 +168,8 @@ impl AudioState {
|
|||
}
|
||||
Cmd::Stop => {
|
||||
self.playing = false;
|
||||
self.end = None;
|
||||
self.looped = None;
|
||||
self.mix.reset();
|
||||
}
|
||||
Cmd::Locate(at) => {
|
||||
|
|
@ -206,6 +208,7 @@ impl AudioState {
|
|||
} else if let Some(e) = self.end {
|
||||
if self.pos >= e {
|
||||
self.playing = false;
|
||||
self.end = None;
|
||||
self.shared.playing.store(false, Ordering::Relaxed);
|
||||
n = 0;
|
||||
} else {
|
||||
|
|
@ -231,11 +234,6 @@ impl AudioState {
|
|||
self.mix.render(&self.session, self.pos, n, &mut self.pending);
|
||||
self.send_garbage();
|
||||
self.pending_len = n;
|
||||
if !self.playing {
|
||||
return;
|
||||
}
|
||||
self.pos += n as i64;
|
||||
self.shared.position.store(self.pos, Ordering::Relaxed);
|
||||
if let Ok(mut m) = self.shared.meters.try_lock() {
|
||||
m.main.copy_from(&self.mix.main_meter);
|
||||
for (k, v) in &self.mix.meters {
|
||||
|
|
@ -247,6 +245,10 @@ impl AudioState {
|
|||
}
|
||||
}
|
||||
}
|
||||
if self.playing {
|
||||
self.pos += n as i64;
|
||||
self.shared.position.store(self.pos, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill an interleaved device buffer.
|
||||
|
|
@ -942,4 +944,30 @@ mod tests {
|
|||
assert!(p <= 1000, "position {p} should stay inside the loop");
|
||||
assert!(shared.playing.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn armed_tracks_are_heard_and_metered_after_playback_stops() {
|
||||
use soundcraft_model::{ChannelFormat, TrackKind};
|
||||
let mut s = soundcraft_model::Session::default();
|
||||
let t = s.add_track(TrackKind::Audio, ChannelFormat::Mono, None);
|
||||
s.track_mut(t).unwrap().mixer.record_arm = true;
|
||||
let shared = Arc::new(Shared {
|
||||
position: AtomicI64::new(0),
|
||||
playing: AtomicBool::new(false),
|
||||
speed: AtomicU32::new(1000),
|
||||
meters: Mutex::new(MeterSnapshot::default()),
|
||||
});
|
||||
let (tx, rx) = channel();
|
||||
let mut st = AudioState::new(rx, Arc::clone(&shared), Arc::new(s), 48_000.0, channel().0);
|
||||
let ring = record::InputRing::new(1, 48_000);
|
||||
tx.send(Cmd::Input(Arc::clone(&ring))).unwrap();
|
||||
tx.send(Cmd::Play { from: 0, end: Some(1000), looped: None }).unwrap();
|
||||
let mut out = vec![0.0f32; 4096];
|
||||
st.fill(&mut out, 2);
|
||||
assert!(!shared.playing.load(Ordering::Relaxed));
|
||||
ring.push(&[0.5; 4096]);
|
||||
st.fill(&mut out, 2);
|
||||
assert!(out.iter().any(|x| x.abs() > 0.1), "the input is heard");
|
||||
assert!(shared.meters.lock().unwrap().tracks.get(&t).is_some_and(|m| m.peak[0] > 0.1), "and metered");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,6 +334,9 @@ impl SoundApp {
|
|||
// Re-selecting a scrolling mode resumes the playhead follow.
|
||||
self.edit_layout.follow_hold = false;
|
||||
}
|
||||
if id == "track.clear_clip_indicators" {
|
||||
self.meters.values_mut().chain([&mut self.main_meter]).for_each(|m| m.clip = false);
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -519,6 +522,12 @@ impl SoundApp {
|
|||
best.map(|(_, p)| p)
|
||||
}
|
||||
|
||||
/// Whether the meters are live: while playing, and while armed or input-monitored tracks are heard.
|
||||
fn meters_live(&self) -> bool {
|
||||
self.engine.transport.playing
|
||||
|| (self.recorder.is_some() && self.engine.session().tracks.iter().any(|t| t.mixer.input_monitor || t.mixer.record_arm))
|
||||
}
|
||||
|
||||
/// Open the input device once (for recording and input monitoring) and feed the player.
|
||||
fn ensure_input(&mut self) {
|
||||
if self.recorder.is_some() || self.recorder_failed || self.player.is_none() {
|
||||
|
|
@ -762,7 +771,7 @@ impl SoundApp {
|
|||
self.stop_play();
|
||||
}
|
||||
}
|
||||
if !self.engine.transport.playing {
|
||||
if !self.meters_live() {
|
||||
for d in self.meters.values_mut() {
|
||||
feed_meter(d, [0.0, 0.0], 0.0, dt);
|
||||
}
|
||||
|
|
@ -818,7 +827,7 @@ impl SoundApp {
|
|||
{
|
||||
self.ensure_input();
|
||||
}
|
||||
if self.is_playing() {
|
||||
if self.meters_live() {
|
||||
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) {
|
||||
|
|
@ -997,6 +1006,16 @@ mod tests {
|
|||
assert!(!app.engine.is_dirty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_all_clip_indicators_clears_every_meter() {
|
||||
let mut app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default());
|
||||
let kick = app.engine.session().track_by_name("Kick").unwrap().id;
|
||||
app.meters.entry(kick).or_default().clip = true;
|
||||
app.main_meter.clip = true;
|
||||
app.run("track.clear_clip_indicators", json!({})).unwrap();
|
||||
assert!(!app.meters[&kick].clip && !app.main_meter.clip);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automation_first_pass_after_start_is_due_without_overflow() {
|
||||
// `last_write_at` starts (and resets) at `i64::MIN`: the previous
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ pub fn show(app: &mut SoundApp, ui: &mut Ui) {
|
|||
pub(crate) fn content(app: &mut SoundApp, ui: &mut Ui) {
|
||||
let ids: Vec<TrackId> = app.engine.session().tracks.iter().filter(|x| !x.hidden && x.kind != TrackKind::Video).map(|x| x.id).collect();
|
||||
// Per-channel peaks for multichannel meters (read once per frame).
|
||||
let snap = app
|
||||
.player
|
||||
.as_ref()
|
||||
.filter(|_| app.engine.transport.playing || app.engine.session().tracks.iter().any(|t| t.mixer.input_monitor))
|
||||
.map(|p| p.meters());
|
||||
let snap = app.player.as_ref().filter(|_| app.meters_live()).map(|p| p.meters());
|
||||
egui::ScrollArea::horizontal().auto_shrink([false, false]).show(ui, |ui| {
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 1.0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue