mirror of
https://github.com/storytold/soundcraft.git
synced 2026-11-12 13:22:25 +00:00
Merge pull request #1436 from Silvertree2010/fix/1433-separate-splits-locked-clips-edi
Keep time- and edit-locked clips whole on separate and range edits
This commit is contained in:
3 files changed
+63
-4
No files matched your search
@@ -154,6 +154,7 @@ fn insert_time(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let s = e.session_mut();
|
||||
let ids: Vec<_> = s.tracks.iter().map(|t| t.id).collect();
|
||||
for t in ids {
|
||||
crate::edit::separate_all_at(s, t, r.start);
|
||||
crate::edit::insert_silence(s, t, r);
|
||||
if let Some(tr) = s.track_mut(t) {
|
||||
for l in &mut tr.automation {
|
||||
@@ -185,6 +186,8 @@ fn cut_time(e: &mut Engine, p: &Value) -> Result<Value> {
|
||||
let s = e.session_mut();
|
||||
let ids: Vec<_> = s.tracks.iter().map(|t| t.id).collect();
|
||||
for t in ids {
|
||||
crate::edit::separate_all_at(s, t, r.start);
|
||||
crate::edit::separate_all_at(s, t, r.end);
|
||||
crate::edit::clear_range(s, t, r, false);
|
||||
crate::edit::shift_from(s, t, r.end, -r.len());
|
||||
if let Some(tr) = s.track_mut(t) {
|
||||
|
||||
@@ -78,9 +78,9 @@ fn rebase_midi_start(clip: &mut Clip, old_start: Samples, t: TimeCtx<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Split every clip on a track's active playlist that spans `at`. The piece before `at` keeps the
|
||||
/// clip's name. The piece after it is a new clip, and takes the next name of the clip's Auto Rename
|
||||
/// scheme when it has one.
|
||||
/// Split every clip on a track's active playlist that spans `at`, except time- or edit-locked ones.
|
||||
/// The piece before `at` keeps the clip's name. The piece after it is a new clip, and takes the next
|
||||
/// name of the clip's Auto Rename scheme when it has one.
|
||||
pub fn separate_at(s: &mut Session, track: TrackId, at: Samples) -> usize {
|
||||
separate_at_if(s, track, at, |_| true)
|
||||
}
|
||||
@@ -88,11 +88,21 @@ pub fn separate_at(s: &mut Session, track: TrackId, at: Samples) -> usize {
|
||||
/// [`separate_at`], giving the piece after `at` the next Auto Rename name only when `auto_name`
|
||||
/// returns true for it. Any other piece keeps the clip's name, and its scheme does not count up.
|
||||
pub fn separate_at_if(s: &mut Session, track: TrackId, at: Samples, auto_name: impl Fn(&Clip) -> bool) -> usize {
|
||||
split_at(s, track, at, auto_name, true)
|
||||
}
|
||||
|
||||
/// [`separate_at`], splitting time- and edit-locked clips too. For the Event time operations, which
|
||||
/// move the whole timeline, locked clips included.
|
||||
pub fn separate_all_at(s: &mut Session, track: TrackId, at: Samples) -> usize {
|
||||
split_at(s, track, at, |_| true, false)
|
||||
}
|
||||
|
||||
fn split_at(s: &mut Session, track: TrackId, at: Samples, auto_name: impl Fn(&Clip) -> bool, skip_locked: bool) -> usize {
|
||||
let tc_tempo = s.tempo.clone();
|
||||
let sr = s.sample_rate;
|
||||
let t = TimeCtx { tempo: &tc_tempo, sr };
|
||||
let mut ids = Vec::new();
|
||||
let n_split = s.track(track).map_or(0, |tr| tr.clips().iter().filter(|c| at > c.start && at < c.end()).count());
|
||||
let n_split = s.track(track).map_or(0, |tr| tr.clips().iter().filter(|c| at > c.start && at < c.end() && !(skip_locked && locked(c))).count());
|
||||
for _ in 0..n_split {
|
||||
ids.push(s.new_clip_id());
|
||||
}
|
||||
@@ -106,6 +116,7 @@ pub fn separate_at_if(s: &mut Session, track: TrackId, at: Samples, auto_name: i
|
||||
for c in pl.clips.drain(..) {
|
||||
if at > c.start
|
||||
&& at < c.end()
|
||||
&& !(skip_locked && locked(&c))
|
||||
&& let Some(id) = ids.pop()
|
||||
&& let Some((l, mut r)) = split_clip(&c, at, id, t)
|
||||
{
|
||||
|
||||
@@ -579,3 +579,48 @@ fn moving_a_group_left_past_zero_keeps_its_spacing() {
|
||||
run(&mut e, "edit.move_clips", json!({"clips": [ids[0].0, ids[1].0], "by": -1000}));
|
||||
assert_eq!(clips(&e, t), [(0, 100), (1500, 100)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn separate_and_range_edits_leave_locked_clips_whole() {
|
||||
let edits = [
|
||||
("edit.separate", json!({"at": 24_000})),
|
||||
("edit.separate", json!({"start": 12_000, "end": 36_000})),
|
||||
("edit.separate_on_grid", json!({"start": 12_000, "end": 36_000})),
|
||||
("edit.clear", json!({"start": 12_000, "end": 36_000})),
|
||||
("edit.insert_silence", json!({"start": 24_000, "end": 25_000})),
|
||||
];
|
||||
for lock in [None, Some("clip.edit_lock"), Some("clip.time_lock")] {
|
||||
for (cmd, p) in &edits {
|
||||
let (mut e, t, ids) = long("slip", &[(0, 48_000, 0)]);
|
||||
if let Some(lock) = lock {
|
||||
run(&mut e, lock, json!({"clips": [ids[0].0]}));
|
||||
}
|
||||
let mut p = p.clone();
|
||||
p["tracks"] = json!([t.0]);
|
||||
run(&mut e, cmd, p);
|
||||
if lock.is_some() {
|
||||
assert_eq!(clips(&e, t), vec![(0, 48_000)], "{lock:?} {cmd}");
|
||||
} else {
|
||||
assert_ne!(clips(&e, t), vec![(0, 48_000)], "unlocked control {cmd}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Event time edits move the entire timeline, including clips protected from ordinary edits.
|
||||
#[test]
|
||||
fn event_time_edits_split_and_move_locked_clips() {
|
||||
for lock in ["clip.edit_lock", "clip.time_lock"] {
|
||||
for (command, expected) in [
|
||||
("event.insert_time", vec![(0, 12_000, 0), (36_000, 36_000, 12_000)]),
|
||||
("event.cut_time", vec![(0, 12_000, 0), (12_000, 12_000, 36_000)]),
|
||||
] {
|
||||
let (mut e, t, ids) = long("slip", &[(0, 48_000, 0)]);
|
||||
run(&mut e, lock, json!({"clips": [ids[0].0]}));
|
||||
run(&mut e, command, json!({"start": 12_000, "end": 36_000}));
|
||||
let actual: Vec<_> = e.session().track(t).unwrap().clips().iter().map(|c| (c.start, c.length, c.source_offset())).collect();
|
||||
assert_eq!(actual, expected, "{lock} {command}");
|
||||
assert!(e.session().track(t).unwrap().clips().iter().all(soundcraft_engine::edit::locked));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user