Skip to main content

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