Compare commits

...
2 changed files with 74 additions and 5 deletions
+37
View File
@@ -125,6 +125,43 @@ pub fn separate_at_if(s: &mut Session, track: TrackId, at: Samples, auto_name: i
count
}
/// Split every clip on a track's active playlist at each of `points`, in one pass over the
/// playlist: [`separate_at_if`] once per point with `|_| false` (no piece takes an Auto Rename
/// name here), without rebuilding the playlist per point. Returns the number of splits.
pub fn separate_at_points(s: &mut Session, track: TrackId, points: &[Samples]) -> usize {
let mut pts = points.to_vec();
pts.sort_unstable();
pts.dedup();
let tc_tempo = s.tempo.clone();
let t = TimeCtx { tempo: &tc_tempo, sr: s.sample_rate };
// The points strictly inside a clip.
let within = |c: &Clip| {
let (lo, hi) = (pts.partition_point(|p| *p <= c.start), pts.partition_point(|p| *p < c.end()));
lo..hi.max(lo)
};
let n_split: usize = s.track(track).map_or(0, |tr| tr.clips().iter().map(|c| within(c).len()).sum());
let mut ids: Vec<ClipId> = (0..n_split).map(|_| s.new_clip_id()).collect();
let Some(pl) = s.track_mut(track).and_then(|t| t.playlist_mut()) else { return 0 };
let mut out = Vec::with_capacity(pl.clips.len() + n_split);
for c in pl.clips.drain(..) {
let mut rest = c;
for p in &pts[within(&rest)] {
let Some(id) = ids.pop() else { break };
match split_clip(&rest, *p, id, t) {
Some((l, r)) => {
out.push(l);
rest = r;
}
None => break,
}
}
out.push(rest);
}
pl.clips = out;
pl.sort();
n_split
}
/// Gives `c` the next name of its Auto Rename scheme, when it has one, and counts the scheme up.
fn take_auto_name(schemes: &mut std::collections::BTreeMap<u64, soundcraft_model::AutoName>, c: &mut Clip) {
if let Some(scheme) = c.auto_name.and_then(|k| schemes.get_mut(&k)) {
+37 -5
View File
@@ -117,9 +117,7 @@ pub fn strip_silence(
}
cut_points.push(r.end);
let old: std::collections::HashSet<_> = s.track(t).map(|x| x.clips().iter().map(|c| c.id).collect()).unwrap_or_default();
for p in &cut_points {
crate::edit::separate_at_if(s, t, *p, |_| false);
}
crate::edit::separate_at_points(s, t, &cut_points);
let kept = strip_pieces(s, t, r, &keep, mode);
crate::edit::auto_name_new_clips(s, t, &old, |c| !(named && c.is_audio() && c.start >= r.start && c.end() <= r.end));
Ok(kept)
@@ -144,10 +142,24 @@ fn strip_pieces(s: &mut Session, t: TrackId, r: Range, keep: &[(Samples, Samples
let audio: Vec<Range> = keep.iter().map(|(a, b)| Range::new(*a, *b)).collect();
let remove = if mode == StripMode::Extract { &audio } else { &gaps };
let inside = |c: &Clip, g: &Range| c.start >= g.start && c.end() <= g.end;
// The range of `rs` holding `c`: by binary search when `rs` is ascending and disjoint (the
// usual case, checked once by the caller), by scan otherwise.
let holder = |rs: &[Range], sorted: bool, c: &Clip| {
if sorted {
let i = rs.partition_point(|g| g.end < c.end());
rs.get(i).filter(|g| inside(c, g)).map(|_| i)
} else {
rs.iter().position(|g| inside(c, g))
}
};
let ordered = |rs: &[Range]| rs.windows(2).all(|w| w[0].end <= w[1].start);
let Some(pl) = s.track_mut(t).and_then(|x| x.playlist_mut()) else { return if mode == StripMode::Extract { 0 } else { keep.len() } };
pl.clips.retain(|c| !remove.iter().any(|g| inside(c, g)));
let sorted = ordered(remove);
pl.clips.retain(|c| holder(remove, sorted, c).is_none());
if mode == StripMode::Extract {
return gaps.iter().filter(|g| pl.clips.iter().any(|c| inside(c, g))).count();
let sorted = ordered(&gaps);
let held: std::collections::HashSet<usize> = pl.clips.iter().filter_map(|c| holder(&gaps, sorted, c)).collect();
return held.len();
}
keep.len()
}
@@ -294,6 +306,26 @@ mod strip_tests {
(r, e.session().track(t).unwrap().clips().iter().map(|c| (c.start, c.end())).collect())
}
#[test]
fn cutting_at_many_points_in_one_pass_equals_cutting_at_each() {
let spans = |e: &Engine, t: TrackId| {
let mut v: Vec<_> = e.session().track(t).unwrap().clips().iter().map(|c| (c.start, c.end())).collect();
v.sort_unstable();
v
};
let points = [30_000, 10, 5_000, 30_000, 120_000, 0, 20_000, 1_000_000];
let (mut one, t) = tones(&[0, 20_000, 100_000], 40_000);
let (mut each, _) = tones(&[0, 20_000, 100_000], 40_000);
crate::edit::separate_at_points(one.session_mut(), t, &points);
for p in points {
crate::edit::separate_at_if(each.session_mut(), t, p, |_| false);
}
assert_eq!(spans(&one, t), spans(&each, t));
assert!(spans(&one, t).len() > 3);
let ids: std::collections::HashSet<_> = one.session().track(t).unwrap().clips().iter().map(|c| c.id).collect();
assert_eq!(ids.len(), spans(&one, t).len(), "new pieces have distinct ids");
}
#[test]
fn strip_keeps_the_audio_and_removes_the_silence() {
let (r, clips) = strip_mode("strip");