Skip to main content

music21_rs/analysis/
segment_by_rests.rs

1//! A part cut into the runs of notes between its rests: music21's
2//! `analysis.segmentByRests`.
3
4use crate::{
5    error::Result,
6    interval::Interval,
7    stream::{Stream, StreamElement},
8};
9
10/// The runs of notes between rests and clefs, through every stream nested
11/// in this one, each note as its position in [`Stream::leaves`]: music21's
12/// `Segmenter.getSegmentsList`.
13///
14/// Only single notes count: a chord or an unpitched stroke neither joins a
15/// run nor ends one. Parts are read one after another with nothing between
16/// them, so a part that opens without a clef or a rest carries on the last
17/// run of the part before it. With `remove_empty` a rest or clef following
18/// another gives no empty run.
19///
20/// ```
21/// use music21_rs::analysis::segment_by_rests::segments_list;
22/// use music21_rs::stream::StreamElement;
23/// use music21_rs::tinynotation::from_tiny_notation;
24///
25/// let part = from_tiny_notation("C4 r D E r r F r G r A B r c")?;
26/// let leaves = part.leaves();
27/// let names: Vec<Vec<String>> = segments_list(&part, true)
28///     .into_iter()
29///     .map(|run| {
30///         run.into_iter()
31///             .map(|position| match leaves[position].1 {
32///                 StreamElement::Note(note) => note.pitch().name(),
33///                 _ => unreachable!(),
34///             })
35///             .collect()
36///     })
37///     .collect();
38/// assert_eq!(names, [vec!["C"], vec!["D", "E"], vec!["F"], vec!["G"], vec!["A", "B"], vec!["C"]]);
39/// # Ok::<(), music21_rs::Error>(())
40/// ```
41pub fn segments_list(stream: &Stream, remove_empty: bool) -> Vec<Vec<usize>> {
42    let marks = marks(stream);
43    let mut segments: Vec<Vec<usize>> = Vec::new();
44    let mut current = Vec::new();
45    for (index, &(position, is_note)) in marks.iter().enumerate() {
46        if is_note {
47            current.push(position);
48            if index == marks.len() - 1 {
49                segments.push(std::mem::take(&mut current));
50            }
51        } else {
52            segments.push(std::mem::take(&mut current));
53        }
54    }
55    if remove_empty {
56        segments.retain(|segment| !segment.is_empty());
57    }
58    segments
59}
60
61/// The interval between each two notes standing next to each other with no
62/// rest or clef between them, through every stream nested in this one:
63/// music21's `Segmenter.getIntervalList`.
64///
65/// # Errors
66///
67/// Two notes no interval can be spelled between.
68pub fn interval_list(stream: &Stream) -> Result<Vec<Interval>> {
69    let leaves = stream.leaves();
70    let marks = marks(stream);
71    let mut intervals = Vec::new();
72    for pair in marks.windows(2) {
73        let ((first, true), (second, true)) = (pair[0], pair[1]) else {
74            continue;
75        };
76        if let (StreamElement::Note(start), StreamElement::Note(end)) =
77            (leaves[first].1, leaves[second].1)
78        {
79            intervals.push(Interval::between_notes(start, end)?);
80        }
81    }
82    Ok(intervals)
83}
84
85/// Every note, rest and clef in order, as its position among the leaves and
86/// whether it is a note.
87fn marks(stream: &Stream) -> Vec<(usize, bool)> {
88    stream
89        .leaves()
90        .into_iter()
91        .enumerate()
92        .filter_map(|(position, (_, element))| match element {
93            StreamElement::Note(_) => Some((position, true)),
94            StreamElement::Rest(_) | StreamElement::Clef(_) => Some((position, false)),
95            _ => None,
96        })
97        .collect()
98}