collective

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

diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs--- a/crates/engine/src/cmd/edit.rs+++ b/crates/engine/src/cmd/edit.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}`")))?,diff --git a/crates/engine/src/cmd/mod.rs b/crates/engine/src/cmd/mod.rs--- a/crates/engine/src/cmd/mod.rs+++ b/crates/engine/src/cmd/mod.rs@@ -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());         vdiff --git a/crates/engine/src/cmd/strip_silence.rs b/crates/engine/src/cmd/strip_silence.rsnew file mode 100644--- /dev/null+++ b/crates/engine/src/cmd/strip_silence.rs@@ -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}))+}diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs--- a/crates/ui-egui/src/dialogs.rs+++ b/crates/ui-egui/src/dialogs.rs@@ -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;diff --git a/crates/ui-egui/tests/it/strip_silence_rectangles.rs b/crates/ui-egui/tests/it/strip_silence_rectangles.rs--- a/crates/ui-egui/tests/it/strip_silence_rectangles.rs+++ b/crates/ui-egui/tests/it/strip_silence_rectangles.rs@@ -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");+}