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}