Skip to main content

music21/stream/
slicing.rs

1//! Cutting a stream's notes and rests into shorter ones: music21's
2//! `sliceByQuarterLengths`, `sliceByGreatestDivisor`, `sliceAtOffsets`,
3//! `sliceByBeat` and `splitByClass`.
4
5use super::{Stream, StreamElement, StreamEvent, StreamKind};
6use crate::defaults::FloatType;
7use crate::error::{Error, Result};
8use crate::makenotation::{event_order, op_frac, split_element};
9use crate::meter::TimeSignature;
10
11fn stream_error(message: impl Into<String>) -> Error {
12    Error::Stream(message.into())
13}
14
15/// An element cut into pieces of these lengths, which must add up to its
16/// own: music21's `splitByQuarterLengths`.
17fn split_by_lengths(element: &StreamElement, lengths: &[FloatType]) -> Result<Vec<StreamElement>> {
18    if lengths.len() == 1 {
19        return Ok(vec![element.clone()]);
20    }
21    let mut pieces = Vec::with_capacity(lengths.len());
22    let mut remain = element.clone();
23    for length in &lengths[..lengths.len() - 1] {
24        let (piece, rest) = split_element(&remain, *length)?;
25        pieces.push(piece);
26        remain = rest;
27    }
28    pieces.push(remain);
29    Ok(pieces)
30}
31
32/// The sum of some lengths, snapped as music21's `opFrac(sum(...))`.
33fn total(lengths: &[FloatType]) -> FloatType {
34    op_frac(lengths.iter().sum())
35}
36
37/// The greatest length that goes a whole number of times into every one of
38/// `values`, to within a ten-thousandth: music21's `approximateGCD`.
39///
40/// # Errors
41///
42/// No values, a value of nought, which music21 divides by, or no length
43/// that goes into them all.
44pub(crate) fn approximate_gcd(values: &[FloatType]) -> Result<FloatType> {
45    if values.is_empty() {
46        return Err(stream_error("no values to find a common length for"));
47    }
48    if values.iter().copied().reduce(FloatType::min) == Some(0.0) {
49        return Err(stream_error(
50            "a value of nought has no common length with others",
51        ));
52    }
53    crate::common::numbertools::approximate_gcd(values, 0.0001)
54        .map_err(|_| stream_error("cannot find a common divisor"))
55}
56
57/// Where each leaf of a stream went: nowhere moved.
58type Moved = Vec<Option<usize>>;
59
60fn unmoved(stream: &Stream) -> Moved {
61    (0..stream.leaf_total()).map(Some).collect()
62}
63
64/// Where each leaf went after two edits, one after the other.
65fn then(first: Moved, next: &[Option<usize>]) -> Moved {
66    first
67        .into_iter()
68        .map(|place| place.and_then(|at| next.get(at).copied().flatten()))
69        .collect()
70}
71
72/// The direct events of a stream put back in music21's order: each event
73/// with the index it stood at, if it did, and the order it went in.
74fn reorder(stream: &mut Stream, mut events: Vec<(StreamEvent, Option<usize>, usize)>) -> Moved {
75    events.sort_by(|left, right| event_order(&left.0, &right.0).then(left.2.cmp(&right.2)));
76    stream.replace_events(
77        events
78            .into_iter()
79            .map(|(event, origin, _)| (event, origin))
80            .collect(),
81    )
82}
83
84fn is_measure(element: &StreamElement) -> bool {
85    matches!(element, StreamElement::Stream(inner) if inner.kind() == StreamKind::Measure)
86}
87
88fn is_part(element: &StreamElement) -> bool {
89    matches!(element, StreamElement::Stream(inner) if matches!(inner.kind(), StreamKind::Part | StreamKind::PartStaff))
90}
91
92/// Applies `edit` to each nested stream of this stream that `which`
93/// accepts, in place, every spanner kept on what it names. The answer is
94/// where each leaf of this stream went.
95fn each_nested(
96    stream: &mut Stream,
97    which: impl Fn(&StreamElement) -> bool,
98    edit: &mut dyn FnMut(&mut Stream, FloatType) -> Result<Moved>,
99) -> Result<Moved> {
100    let found: Vec<(usize, FloatType)> = stream
101        .events()
102        .iter()
103        .enumerate()
104        .filter(|(_, event)| which(event.element()))
105        .map(|(index, event)| (index, event.offset()))
106        .collect();
107    let mut moved = unmoved(stream);
108    for (index, offset) in found {
109        let (result, step) = stream.edit_nested(&[index], |inner| match edit(inner, offset) {
110            Ok(moved) => (Ok(()), moved),
111            Err(error) => (Err(error), unmoved(inner)),
112        });
113        result?;
114        moved = then(moved, &step);
115    }
116    Ok(moved)
117}
118
119impl Stream {
120    /// A copy of the stream with each note and rest cut into pieces of
121    /// `lengths`, the list repeated as far as it takes to fill the note,
122    /// tied: music21's `sliceByQuarterLengths`. A note shorter than the
123    /// list is left whole. A stream of measures is cut measure by measure,
124    /// and a score part by part.
125    ///
126    /// ```
127    /// use music21::tinynotation::from_tiny_notation;
128    ///
129    /// let line = from_tiny_notation("4/4 c2 d4 e4")?;
130    /// let sliced = line.slice_by_quarter_lengths(&[1.0])?;
131    /// assert_eq!(sliced.notes().len(), 4);
132    /// # Ok::<(), music21::Error>(())
133    /// ```
134    ///
135    /// # Errors
136    ///
137    /// A note the list cannot add up to, which music21 refuses.
138    pub fn slice_by_quarter_lengths(&self, lengths: &[FloatType]) -> Result<Stream> {
139        let mut out = self.clone();
140        out.slice_lengths_in_place(lengths)?;
141        Ok(out)
142    }
143
144    fn slice_lengths_in_place(&mut self, lengths: &[FloatType]) -> Result<Moved> {
145        if self.has_measures() {
146            return each_nested(self, is_measure, &mut |measure, _| {
147                measure.slice_lengths_in_place(lengths)
148            });
149        }
150        if self.has_part_like_streams() {
151            return each_nested(self, is_part, &mut |part, _| {
152                part.slice_lengths_in_place(lengths)
153            });
154        }
155        let sum = total(lengths);
156        let mut events: Vec<(StreamEvent, Option<usize>, usize)> = Vec::new();
157        let mut order = self.events().len();
158        for (index, event) in self.events().iter().enumerate() {
159            let element = event.element();
160            let length = element.quarter_length();
161            if !element.is_instance("GeneralNote") || sum > length {
162                events.push((event.clone(), Some(index), index));
163                continue;
164            }
165            let process: Vec<FloatType> = if sum == length {
166                lengths.to_vec()
167            } else {
168                let mut process = Vec::new();
169                let mut at = 0;
170                while let Some(next) = lengths.get(at % lengths.len()) {
171                    process.push(*next);
172                    at += 1;
173                    if total(&process) >= length {
174                        break;
175                    }
176                }
177                process
178            };
179            if total(&process) != length {
180                return Err(stream_error(format!(
181                    "cannot map quarterLength list into element Duration: {}, {}",
182                    process.iter().sum::<FloatType>(),
183                    length
184                )));
185            }
186            let mut offset = event.offset();
187            for piece in split_by_lengths(element, &process)? {
188                let piece_length = piece.quarter_length();
189                events.push((StreamEvent::new(offset, piece), None, order));
190                order += 1;
191                offset = op_frac(offset + piece_length);
192            }
193        }
194        Ok(reorder(self, events))
195    }
196
197    /// A copy of the stream with each note and rest cut into pieces of the
198    /// greatest length that goes into all of them, measure by measure:
199    /// music21's `sliceByGreatestDivisor`. A score finds one length for
200    /// each measure across its parts.
201    ///
202    /// Grace notes take no time to divide and are left whole; a measure
203    /// with no notes or rests of its own is left as it is.
204    ///
205    /// # Errors
206    ///
207    /// Lengths with no common divisor music21's `approximateGCD` finds; a
208    /// score whose first part has more measures than another, or none of
209    /// its own.
210    pub fn slice_by_greatest_divisor(&self) -> Result<Stream> {
211        let mut out = self.clone();
212        if out.kind() == StreamKind::Score {
213            out.slice_score_by_greatest_divisor()?;
214        } else {
215            out.slice_divisor_in_place()?;
216        }
217        Ok(out)
218    }
219
220    fn unique_lengths(&self, into: &mut Vec<FloatType>) {
221        for event in self.events() {
222            if event.element().is_instance("GeneralNote") {
223                let length = event.element().quarter_length();
224                if length != 0.0 && !into.contains(&length) {
225                    into.push(length);
226                }
227            }
228        }
229    }
230
231    fn slice_divisor_in_place(&mut self) -> Result<Moved> {
232        if self.has_measures() {
233            return each_nested(self, is_measure, &mut |measure, _| {
234                measure.slice_divisor_in_place()
235            });
236        }
237        let mut lengths = Vec::new();
238        self.unique_lengths(&mut lengths);
239        if lengths.is_empty() {
240            return Ok(unmoved(self));
241        }
242        let divisor = approximate_gcd(&lengths)?;
243        self.slice_lengths_in_place(&[divisor])
244    }
245
246    fn slice_score_by_greatest_divisor(&mut self) -> Result<()> {
247        let parts: Vec<usize> = self
248            .events()
249            .iter()
250            .enumerate()
251            .filter(|(_, event)| is_part(event.element()))
252            .map(|(index, _)| index)
253            .collect();
254        let Some(first) = self.parts().first().map(|part| part.measures().len()) else {
255            return Err(stream_error("Cannot sliceByGreatestDivisor without parts"));
256        };
257        let count = first.max(1);
258        let measure_of = |part: &Stream, i: usize| -> Result<Option<usize>> {
259            if !part.has_measures() {
260                return Ok(None);
261            }
262            part.events()
263                .iter()
264                .enumerate()
265                .filter(|(_, event)| is_measure(event.element()))
266                .nth(i)
267                .map(|(index, _)| Some(index))
268                .ok_or_else(|| stream_error("index out of range"))
269        };
270        for i in 0..count {
271            let mut lengths = Vec::new();
272            for &part_index in &parts {
273                let StreamElement::Stream(part) = self.events()[part_index].element() else {
274                    continue;
275                };
276                match measure_of(part, i)? {
277                    Some(index) => {
278                        if let StreamElement::Stream(measure) = part.events()[index].element() {
279                            measure.unique_lengths(&mut lengths);
280                        }
281                    }
282                    None => part.unique_lengths(&mut lengths),
283                }
284            }
285            if lengths.is_empty() {
286                continue;
287            }
288            let divisor = approximate_gcd(&lengths)?;
289            for &part_index in &parts {
290                let StreamElement::Stream(part) = self.events()[part_index].element() else {
291                    continue;
292                };
293                let path: Vec<usize> = match measure_of(part, i)? {
294                    Some(index) => vec![part_index, index],
295                    None => vec![part_index],
296                };
297                let (result, _) = self.edit_nested(&path, |target| {
298                    match target.slice_lengths_in_place(&[divisor]) {
299                        Ok(moved) => (Ok(()), moved),
300                        Err(error) => (Err(error), unmoved(target)),
301                    }
302                });
303                result?;
304            }
305        }
306        Ok(())
307    }
308
309    /// A copy of the stream with each note and rest sounding across one of
310    /// `offsets` cut there, tied: music21's `sliceAtOffsets`. A stream of
311    /// measures is cut measure by measure, each at the offsets that fall in
312    /// it, and a score part by part. A note in a voice is cut in its voice,
313    /// at the offsets counted from the voice's start.
314    ///
315    /// # Errors
316    ///
317    /// A note that cannot be cut at a point.
318    pub fn slice_at_offsets(&self, offsets: &[FloatType]) -> Result<Stream> {
319        let mut out = self.clone();
320        out.slice_offsets_in_place(offsets)?;
321        Ok(out)
322    }
323
324    fn slice_offsets_in_place(&mut self, offsets: &[FloatType]) -> Result<Moved> {
325        if self.has_measures() {
326            return each_nested(self, is_measure, &mut |measure, at| {
327                let local: Vec<FloatType> = offsets.iter().map(|offset| offset - at).collect();
328                measure.slice_offsets_in_place(&local)
329            });
330        }
331        if self.has_part_like_streams() {
332            return each_nested(
333                self,
334                |element| matches!(element, StreamElement::Stream(_)),
335                &mut |part, at| {
336                    let local: Vec<FloatType> = offsets.iter().map(|offset| offset - at).collect();
337                    part.slice_offsets_in_place(&local)
338                },
339            );
340        }
341        let offsets: Vec<FloatType> = offsets.iter().map(|offset| op_frac(*offset)).collect();
342        let cuts = |start: FloatType, end: FloatType| -> Vec<FloatType> {
343            let (start, end) = (op_frac(start), op_frac(end));
344            offsets
345                .iter()
346                .copied()
347                .filter(|offset| start < *offset && *offset < end)
348                .collect()
349        };
350        // Cuts each element in turn: the first piece in its place, the
351        // others gathered to be put in this stream.
352        let mut added: Vec<StreamEvent> = Vec::new();
353        let mut cut_one = |element: &mut StreamElement, start: FloatType| -> Result<()> {
354            if !element.is_instance("GeneralNote") {
355                return Ok(());
356            }
357            let points = cuts(start, start + element.quarter_length());
358            let mut previous = start;
359            let mut current: Option<StreamElement> = None;
360            for point in points {
361                let target = current.as_ref().unwrap_or(element);
362                let (left, right) = split_element(target, op_frac(point - previous))?;
363                match current.take() {
364                    None => *element = left,
365                    Some(_) => {
366                        if let Some(last) = added.last_mut() {
367                            *last = StreamEvent::new(last.offset(), left);
368                        }
369                    }
370                }
371                added.push(StreamEvent::new(point, right.clone()));
372                current = Some(right);
373                previous = point;
374            }
375            Ok(())
376        };
377        let is_voice = |element: &StreamElement| matches!(element, StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice);
378        let in_voices = each_nested(self, is_voice, &mut |voice, at| {
379            let local: Vec<FloatType> = offsets.iter().map(|offset| offset - at).collect();
380            voice.slice_offsets_in_place(&local)
381        })?;
382        let mut events: Vec<StreamEvent> = self.events().to_vec();
383        for event in events.iter_mut() {
384            if !is_voice(event.element()) {
385                let start = event.offset();
386                cut_one(event.element_mut(), start)?;
387            }
388        }
389        let count = events.len();
390        let mut held: Vec<(StreamEvent, Option<usize>, usize)> = events
391            .into_iter()
392            .enumerate()
393            .map(|(index, event)| (event, Some(index), index))
394            .collect();
395        held.extend(
396            added
397                .into_iter()
398                .enumerate()
399                .map(|(rank, event)| (event, None, count + rank)),
400        );
401        Ok(then(in_voices, &reorder(self, held)))
402    }
403
404    /// A copy of the stream with each note and rest cut at the beats of
405    /// its time signature: music21's `sliceByBeat`. A stream of measures is
406    /// cut measure by measure, each at the beats of the time signature in
407    /// force there, and a score part by part. A stream with no time
408    /// signature is cut at the beats of 4/4.
409    ///
410    /// # Errors
411    ///
412    /// A measure or stream holding more than one time signature, which
413    /// music21 does not cut.
414    pub fn slice_by_beat(&self) -> Result<Stream> {
415        let mut out = self.clone();
416        let mut meter = None;
417        out.slice_beat_in_place(&mut meter)?;
418        Ok(out)
419    }
420
421    fn slice_beat_in_place(&mut self, meter: &mut Option<TimeSignature>) -> Result<Moved> {
422        if self.has_measures() {
423            return each_nested(self, is_measure, &mut |measure, _| {
424                measure.slice_beat_in_place(meter)
425            });
426        }
427        if self.has_part_like_streams() {
428            return each_nested(self, is_part, &mut |part, _| {
429                let mut own = None;
430                part.slice_beat_in_place(&mut own)
431            });
432        }
433        let own: Vec<TimeSignature> = self
434            .events()
435            .iter()
436            .filter_map(|event| match event.element() {
437                StreamElement::TimeSignature(found) => Some(found.clone()),
438                _ => None,
439            })
440            .collect();
441        if own.len() > 1 {
442            return Err(stream_error(
443                "not yet implemented: slice by changing time signatures",
444            ));
445        }
446        if let Some(found) = own.into_iter().next() {
447            *meter = Some(found);
448        }
449        let offsets = match meter {
450            Some(found) => found.beat_offsets(),
451            None => TimeSignature::new(4, 4)?.beat_offsets(),
452        };
453        self.slice_offsets_in_place(&offsets)
454    }
455
456    /// The elements this stream holds itself in two streams of its kind:
457    /// those `keep` accepts and the rest, at the offsets they stand at:
458    /// music21's `splitByClass` with no class.
459    pub fn split_by(&self, keep: impl Fn(&StreamElement) -> bool) -> (Stream, Stream) {
460        let (mut kept, mut rest) = (
461            Stream::with_kind(self.kind()),
462            Stream::with_kind(self.kind()),
463        );
464        let (mut ours, mut theirs) = (Vec::new(), Vec::new());
465        for event in self.events() {
466            if keep(event.element()) {
467                ours.push(event.clone());
468            } else {
469                theirs.push(event.clone());
470            }
471        }
472        kept.insert_sorted(ours);
473        rest.insert_sorted(theirs);
474        (kept, rest)
475    }
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481
482    fn lengths(stream: &Stream) -> Vec<(FloatType, FloatType)> {
483        stream
484            .events()
485            .iter()
486            .filter(|event| event.element().is_instance("GeneralNote"))
487            .map(|event| (event.offset(), event.element().quarter_length()))
488            .collect()
489    }
490
491    #[test]
492    fn grace_notes_and_empty_measures_have_nothing_to_divide() -> Result<()> {
493        use crate::duration::Duration;
494        use crate::note::Note;
495        let mut measure = Stream::with_kind(StreamKind::Measure);
496        measure.insert(0.0, Note::from_name("C4")?.grace_note(false));
497        measure.insert(
498            0.0,
499            Note::from_name("D4")?.with_duration(Duration::new(1.5)?),
500        );
501        measure.insert(
502            1.5,
503            Note::from_name("E4")?.with_duration(Duration::new(0.5)?),
504        );
505        let sliced = measure.slice_by_greatest_divisor()?;
506        assert_eq!(
507            lengths(&sliced),
508            [(0.0, 0.0), (0.0, 0.5), (0.5, 0.5), (1.0, 0.5), (1.5, 0.5)]
509        );
510        let empty = Stream::with_kind(StreamKind::Measure);
511        assert!(empty.slice_by_greatest_divisor()?.is_empty());
512        Ok(())
513    }
514
515    #[test]
516    fn a_note_in_a_voice_is_cut_in_its_voice() -> Result<()> {
517        use crate::duration::Duration;
518        use crate::note::Note;
519        let mut upper = Stream::with_kind(StreamKind::Voice);
520        upper.insert(0.0, Note::from_name("C5")?.with_duration(Duration::half()));
521        upper.insert(2.0, Note::from_name("D5")?.with_duration(Duration::half()));
522        let mut measure = Stream::with_kind(StreamKind::Measure);
523        measure.insert(0.0, upper);
524        let sliced = measure.slice_at_offsets(&[1.0])?;
525        assert!(lengths(&sliced).is_empty());
526        assert_eq!(
527            lengths(sliced.voices()[0]),
528            [(0.0, 1.0), (1.0, 1.0), (2.0, 2.0)]
529        );
530        Ok(())
531    }
532
533    #[test]
534    fn the_greatest_divisor_is_music21s() -> Result<()> {
535        // Read off music21's common.approximateGCD.
536        assert_eq!(approximate_gcd(&[2.0, 0.5])?, 0.5);
537        assert_eq!(approximate_gcd(&[2.0, 1.5, 1.0])?, 0.5);
538        assert_eq!(approximate_gcd(&[2.0, 1.5, 1.0, 1.0 / 3.0])?, 1.0 / 6.0);
539        assert!(approximate_gcd(&[1.0, 0.0]).is_err());
540        assert!(approximate_gcd(&[]).is_err());
541        Ok(())
542    }
543}