Browse / github.com/storytold/soundcraft / patch
Strip Silence: closing the window while Mix shows stops the rectangle scan
Strip Silence: closing the window while Mix shows stops the rectangle scan Follow-up to #1055, for two review comments left open when it merged. On `main`, only the Edit window drops the Strip Silence rectangles and stops their scan when Strip Silence closes (`strip_silence_rectangles`, called from `edit_window::track_row`). If the Mix window is showing when the window closes (after `window.mix`), a scan for old values runs to the end and `strip_scan_running()` stays true. - `dialogs::show`, which runs every frame whichever window shows, now drops the rectangles and stops the scan whenever neither Strip Silence nor its Rename Selected Clips dialog is open. `strip_s
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- dotCooCoo (imported from pull request #1095, open)
- Base
a44d1b830b161a0f705ab27f68e13f24017045e2- Size
- +75 −47 in 5 files
- Review
- pending
- Shared
- 2026-10-28
- Id
0588c68eb5f000100955dd3b
How it went
No other operator has reported using it yet.
Diff
raw · crates/engine/src/cmd/edit.rs crates/engine/src/cmd/mod.rs crates/engine/src/cmd/strip_silence.rs crates/ui-egui/src/dialogs.rs crates/ui-egui/tests/it/strip_silence_rectangles.rs
@@ -1,7 +1,7 @@ //! Edit menu commands. use super::{- CommandSpec, always, bad, bool_or, can_redo, can_undo, clip_id, clip_ids_param, f32_or, f64_or, has_clipboard, has_range, has_selection,- has_tracks, i64_or, position_param, range_param, str_param, track_param, tracks_param, tracks_required,+ CommandSpec, always, bad, bool_or, can_redo, can_undo, clip_id, clip_ids_param, f32_or, has_clipboard, has_range, has_selection, has_tracks,+ i64_or, position_param, range_param, str_param, track_param, tracks_param, tracks_required, }; use soundcraft_time::Samples; @@ -125,15 +125,6 @@ pub fn specs() -> Vec<CommandSpec> { has_range, strip_silence ),- cmd!(- query "edit.strip_silence_preview",- "Strip Silence Rectangles",- [],- None,- "{threshold_db?: -48, min_length_ms?: 50, pad_before_ms?: 5, pad_after_ms?: 20, tracks?, start?, end?} (the ranges edit.strip_silence finds with the same settings, padded, on each track: what Strip keeps and the Strip Silence window draws as rectangles; changes nothing)",- has_range,- strip_silence_preview- ), cmd!( "edit.fades_create", "Create...",@@ -812,38 +803,8 @@ fn copy_to_new_playlist(e: &mut Engine, p: &Value) -> Result<Value> { Ok(json!({"tracks": tracks.len()})) } -/// Strip Silence's tracks, range, threshold, minimum strip duration and clip start and end pads.-type StripSettings = (Vec<TrackId>, soundcraft_time::Range, f32, Samples, (Samples, Samples));--/// The [`StripSettings`] in Strip Silence's params, with their defaults and limits. `cmd` names the-/// command in errors.-fn strip_settings(e: &Engine, cmd: &str, p: &Value) -> Result<StripSettings> {- let tracks = tracks_required(e, cmd, p)?;- let r = range_param(e, cmd, p)?;- let thr = f32_or(p, "threshold_db", -48.0).clamp(-96.0, 0.0);- let sr = e.session().sample_rate;- let ms = |k: &str, d: f64| sr.samples(f64_or(p, k, d).clamp(0.0, 10_000.0) / 1000.0);- Ok((tracks, r, thr, ms("min_length_ms", 50.0), (ms("pad_before_ms", 5.0), ms("pad_after_ms", 20.0))))-}--/// The scan `edit.strip_silence_preview` runs with params `p`, on the session as it is now. The Edit-/// window runs it while the Strip Silence window or its Rename Selected Clips dialog is open: on a-/// worker thread outside the web build, and on the frame thread in the web build or when no thread-/// can be started.-pub fn strip_silence_scan(e: &Engine, p: &Value) -> Result<crate::io::StripScan> {- let (tracks, range, threshold_db, min_len, pad) = strip_settings(e, "edit.strip_silence_preview", p)?;- Ok(crate::io::StripScan { session: e.session_arc(), tracks, range, threshold_db, min_len, pad, stop: Default::default() })-}--fn strip_silence_preview(e: &mut Engine, p: &Value) -> Result<Value> {- let found = strip_silence_scan(e, p)?.run()?;- let tracks: Vec<Value> =- found.iter().map(|(t, ranges)| json!({"track": t.0, "ranges": ranges.iter().map(|x| [x.start, x.end]).collect::<Vec<_>>()})).collect();- Ok(json!({"tracks": tracks}))-}- fn strip_silence(e: &mut Engine, p: &Value) -> Result<Value> {- let (tracks, r, thr, min_len, (pre, post)) = strip_settings(e, "edit.strip_silence", p)?;+ let (tracks, r, thr, min_len, (pre, post)) = super::strip_silence::strip_settings(e, "edit.strip_silence", p)?; let mode = match p.get("mode") { None => crate::io::StripMode::Strip, Some(Value::String(m)) => crate::io::StripMode::from_id(m).ok_or_else(|| bad("edit.strip_silence", format!("unknown mode `{m}`")))?,@@ -28,6 +28,7 @@ mod plugin_state; mod query; pub(crate) mod sampler; mod setup_more;+mod strip_silence; mod surround; mod tab; mod track;@@ -63,7 +64,7 @@ mod panic_probe { pub use file::bounce_job; pub(crate) use edit::remember_selection;-pub use edit::strip_silence_scan;+pub use strip_silence::strip_silence_scan; pub type Run = fn(&mut Engine, &Value) -> Result<Value>; /// `Err(reason)` = disabled.@@ -160,6 +161,7 @@ pub fn command_specs() -> &'static [CommandSpec] { v.extend(sampler::specs()); v.extend(video::specs()); v.extend(zoom_toggle::specs());+ v.extend(strip_silence::specs()); #[cfg(test)] v.extend(panic_probe::specs()); vnew file mode 100644@@ -0,0 +1,49 @@+//! Strip Silence Rectangles: the ranges Strip Silence keeps, which the Strip Silence window draws.+use super::{CommandSpec, f32_or, f64_or, has_range, range_param, tracks_required};+use soundcraft_model::TrackId;+use soundcraft_time::Samples;++use crate::{Engine, Result, cmd};+use serde_json::{Value, json};++pub fn specs() -> Vec<CommandSpec> {+ vec![cmd!(+ query "edit.strip_silence_preview",+ "Strip Silence Rectangles",+ [],+ None,+ "{threshold_db?: -48, min_length_ms?: 50, pad_before_ms?: 5, pad_after_ms?: 20, tracks?, start?, end?} (the ranges edit.strip_silence finds with the same settings, padded, on each track: what Strip keeps and the Strip Silence window draws as rectangles; changes nothing)",+ has_range,+ strip_silence_preview+ )]+}++/// Strip Silence's tracks, range, threshold, minimum strip duration and clip start and end pads.+pub(super) type StripSettings = (Vec<TrackId>, soundcraft_time::Range, f32, Samples, (Samples, Samples));++/// The [`StripSettings`] in Strip Silence's params, with their defaults and limits. `cmd` names the+/// command in errors.+pub(super) fn strip_settings(e: &Engine, cmd: &str, p: &Value) -> Result<StripSettings> {+ let tracks = tracks_required(e, cmd, p)?;+ let r = range_param(e, cmd, p)?;+ let thr = f32_or(p, "threshold_db", -48.0).clamp(-96.0, 0.0);+ let sr = e.session().sample_rate;+ let ms = |k: &str, d: f64| sr.samples(f64_or(p, k, d).clamp(0.0, 10_000.0) / 1000.0);+ Ok((tracks, r, thr, ms("min_length_ms", 50.0), (ms("pad_before_ms", 5.0), ms("pad_after_ms", 20.0))))+}++/// The scan `edit.strip_silence_preview` runs with params `p`, on the session as it is now. The Edit+/// window runs it while the Strip Silence window or its Rename Selected Clips dialog is open: on a+/// worker thread outside the web build, and on the frame thread in the web build or when no thread+/// can be started.+pub fn strip_silence_scan(e: &Engine, p: &Value) -> Result<crate::io::StripScan> {+ let (tracks, range, threshold_db, min_len, pad) = strip_settings(e, "edit.strip_silence_preview", p)?;+ Ok(crate::io::StripScan { session: e.session_arc(), tracks, range, threshold_db, min_len, pad, stop: Default::default() })+}++fn strip_silence_preview(e: &mut Engine, p: &Value) -> Result<Value> {+ let found = strip_silence_scan(e, p)?.run()?;+ let tracks: Vec<Value> =+ found.iter().map(|(t, ranges)| json!({"track": t.0, "ranges": ranges.iter().map(|x| [x.start, x.end]).collect::<Vec<_>>()})).collect();+ Ok(json!({"tracks": tracks}))+}@@ -406,6 +406,10 @@ pub fn show(app: &mut SoundApp, ctx: &egui::Context) { audiosuite_window(app, ctx); crate::bounce::show(app, ctx); crate::import::show(app, ctx);+ // Whichever window shows, closing Strip Silence drops its rectangles and stops their scan.+ if !matches!(app.dialogs.open, Some(Dialog::StripSilence { .. } | Dialog::StripSilenceRename { .. })) {+ app.dialogs.strip_rectangles = StripRectangles::default();+ } let Some(mut d) = app.dialogs.open.take() else { return }; let mut keep = true; let mut action: Option<(String, Value)> = None;@@ -914,10 +918,7 @@ pub fn strip_silence_rectangles(app: &mut SoundApp, ctx: &egui::Context) -> Stri let values = match &app.dialogs.open { Some(Dialog::StripSilence { threshold, min_ms, pre_ms, post_ms }) => [*threshold, *min_ms, *pre_ms, *post_ms], Some(Dialog::StripSilenceRename { strip, .. }) => *strip,- _ => {- app.dialogs.strip_rectangles = StripRectangles::default();- return Arc::default();- }+ _ => return Arc::default(), }; let key: StripKey = (app.engine.revision, values.map(f64::to_bits)); let r = &mut app.dialogs.strip_rectangles;@@ -109,3 +109,18 @@ fn the_rectangles_stay_while_rename_is_open_and_go_when_the_window_closes() { assert_eq!(rectangles(&h), 0); assert_eq!(clips(&h, track), before); }++#[test]+fn closing_the_window_while_the_mix_window_shows_stops_the_scan() {+ let (mut h, _) = strip_silence_dialog();+ if let Some(Dialog::StripSilence { threshold, .. }) = &mut h.state_mut().dialogs.open {+ *threshold = -30.0;+ }+ h.run_steps(1);+ assert!(h.state().dialogs.strip_scan_running(), "a scan for the new threshold started");+ h.state_mut().run("window.mix", json!({})).unwrap();+ h.run_steps(2);+ h.state_mut().dialogs.open = None;+ h.run_steps(2);+ assert!(!h.state().dialogs.strip_scan_running(), "the scan still runs after the window closed");+}