diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 7e4f2d5..39add57 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -25,7 +25,9 @@ pub fn specs() -> Vec { p, "edit.clear_clip_gain" )), - cmd!("edit.cut_all_automation", "All Automation", ["Edit", "Cut Special"], None, "{}", has_range, |e, p| clear_automation(e, p, None)), + cmd!("edit.cut_all_automation", "All Automation", ["Edit", "Cut Special"], None, "{tracks?, start?, end?}", has_range, |e, p| { + super::edit_more::cut_all_automation(e, p) + }), cmd!("edit.clear_all_automation", "All Automation", ["Edit", "Clear Special"], None, "{}", has_range, |e, p| clear_automation(e, p, None)), cmd!("edit.clear_pan_automation", "Pan Automation", ["Edit", "Clear Special"], None, "{}", has_range, |e, p| clear_automation( e, diff --git a/crates/engine/src/cmd/edit_more.rs b/crates/engine/src/cmd/edit_more.rs index ae16f88..c5c1082 100644 --- a/crates/engine/src/cmd/edit_more.rs +++ b/crates/engine/src/cmd/edit_more.rs @@ -277,6 +277,10 @@ fn nonempty_range(e: &Engine, p: &Value, id: &str) -> Result { if r.is_empty() { Err(bad(id, "the selection is empty")) } else { Ok(r) } } +pub(super) fn cut_all_automation(e: &mut Engine, p: &Value) -> Result { + auto_copy(e, p, Lanes::All, true, "edit.cut_all_automation") +} + fn auto_copy(e: &mut Engine, p: &Value, which: Lanes, cut: bool, id: &str) -> Result { let tracks = tracks_required(e, id, p)?; let r = nonempty_range(e, p, id)?; @@ -984,6 +988,21 @@ mod tests { assert_eq!(starts(&e, t), vec![0, 900, 1000]); } + #[test] + fn cut_all_automation_can_be_pasted() { + let (mut e, t, _) = session_with_clips(&[(0, 1000)]); + e.execute("automation.set_point", &json!({"track": t.0, "param": "volume", "at": 100, "value": -6.0})).unwrap(); + e.execute("automation.set_point", &json!({"track": t.0, "param": "volume", "at": 400, "value": -12.0})).unwrap(); + let cut = e.execute("edit.cut_all_automation", &json!({"start": 50, "end": 250})).unwrap(); + assert!(cut["lanes"].as_u64().unwrap() >= 1, "{cut}"); + assert!(e.clipboard.automation.iter().any(|lanes| !lanes.is_empty())); + let lane = e.session().track(t).unwrap().lane(&AutoParam::Volume).unwrap().clone(); + assert!(lane.points.iter().all(|p| p.at != 100), "the cut point is gone: {:?}", lane.points); + e.execute("edit.paste_to_current_automation", &json!({"at": 1000})).unwrap(); + let vol = e.session().track(t).unwrap().lane(&AutoParam::Volume).unwrap().clone(); + assert!(vol.points.iter().any(|p| p.at >= 1000 && (p.value + 6.0).abs() < 1e-2), "paste wrote nothing at -6 dB: {:?}", vol.points); + } + #[test] fn snap_selected_neighbours_keeps_both_clips() { let (mut e, t, ids) = session_with_clips(&[(0, 100), (300, 100), (1000, 100)]);