music21_rs/stream/ends.rs
1//! The barline a stream ends with, and how many lines its staff has:
2//! music21's `Stream.finalBarline` and `staffLines`.
3
4use super::{Stream, StreamElement, StreamKind};
5use crate::bar::Barline;
6use crate::layout::Layout;
7
8impl Stream {
9 /// The barline this stream ends with: music21's `finalBarline` for a
10 /// stream that is not part-like.
11 ///
12 /// It is the right barline of the last measure the stream holds, or a
13 /// measure's own right barline, and nothing for a stream holding no
14 /// measures at all. music21 answers a *list* for a score or anything
15 /// else holding part-like streams, which is [`Self::final_barlines`].
16 ///
17 /// ```
18 /// use music21_rs::bar::{Barline, BarlineType};
19 /// use music21_rs::{Stream, StreamKind};
20 ///
21 /// let mut part = Stream::with_kind(StreamKind::Part);
22 /// for bar_type in [BarlineType::Double, BarlineType::Final] {
23 /// let mut measure = Stream::with_kind(StreamKind::Measure);
24 /// measure.set_right_barline(Some(Barline::new(bar_type)));
25 /// part.push(measure);
26 /// }
27 /// assert_eq!(
28 /// part.final_barline().map(Barline::bar_type),
29 /// Some(BarlineType::Final)
30 /// );
31 ///
32 /// // A stream holding no measures has none, as music21 answers.
33 /// assert!(Stream::new().final_barline().is_none());
34 /// ```
35 pub fn final_barline(&self) -> Option<&Barline> {
36 if self.has_measures() {
37 return self.measures().last().and_then(|last| last.right_barline());
38 }
39 // music21 reads a stream's own right barline only where it has one
40 // to read, which is a measure.
41 if self.kind() == StreamKind::Measure {
42 return self.right_barline();
43 }
44 None
45 }
46
47 /// The barline each part-like stream ends with: music21's
48 /// `finalBarline` for a score, which answers one barline per part.
49 ///
50 /// A stream holding no part-like streams answers nothing, where
51 /// [`Self::final_barline`] answers for the stream itself.
52 pub fn final_barlines(&self) -> Vec<Option<&Barline>> {
53 if !self.has_part_like_streams() {
54 return Vec::new();
55 }
56 // music21 asks every nested stream, not only the parts.
57 self.events
58 .iter()
59 .filter_map(|event| event.element().as_stream())
60 .map(Stream::final_barline)
61 .collect()
62 }
63
64 /// Writes the barline the stream ends with: music21's `finalBarline`
65 /// setter.
66 ///
67 /// On a stream holding part-like streams every part takes it, as
68 /// music21's setter gives each part the one barline it was handed. On
69 /// any other stream it goes on the last measure, or on a measure
70 /// itself; a stream with no measures to carry it is left alone, as
71 /// music21 leaves it.
72 pub fn set_final_barline(&mut self, barline: Option<Barline>) {
73 if self.has_part_like_streams() {
74 for event in &mut self.events {
75 if let StreamElement::Stream(part) = event.element_mut() {
76 part.set_final_barline(barline.clone());
77 }
78 }
79 return;
80 }
81 if self.has_measures() {
82 let last = self
83 .events
84 .iter_mut()
85 .rev()
86 .find_map(|event| match event.element_mut() {
87 StreamElement::Stream(stream) if stream.kind() == StreamKind::Measure => {
88 Some(stream)
89 }
90 _ => None,
91 });
92 if let Some(last) = last {
93 last.set_right_barline(barline);
94 }
95 return;
96 }
97 if self.kind() == StreamKind::Measure {
98 self.set_right_barline(barline);
99 }
100 }
101
102 /// How many lines this stream's staff has: music21's `staffLines`.
103 ///
104 /// It is read from the first staff layout standing at the start of the
105 /// stream, however deep, that says a number of lines; five where none
106 /// does, which is the ordinary staff. A layout standing anywhere after
107 /// the start says nothing about it, as in music21.
108 ///
109 /// ```
110 /// use music21_rs::layout::{Layout, StaffLayout};
111 /// use music21_rs::{Stream, StreamElement, StreamKind};
112 ///
113 /// let mut measure = Stream::with_kind(StreamKind::Measure);
114 /// assert_eq!(measure.staff_lines(), 5);
115 ///
116 /// let staff = StaffLayout {
117 /// staff_lines: Some(1),
118 /// ..StaffLayout::default()
119 /// };
120 /// measure.insert(0.0, StreamElement::Layout(Layout::Staff(staff)));
121 /// assert_eq!(measure.staff_lines(), 1);
122 ///
123 /// // A part reads the layout of the measure it starts with.
124 /// let mut part = Stream::with_kind(StreamKind::Part);
125 /// part.push(measure);
126 /// assert_eq!(part.staff_lines(), 1);
127 /// ```
128 pub fn staff_lines(&self) -> i64 {
129 // music21 reads the staff layouts alone, so anything else
130 // standing later does not stop the search; the first layout that
131 // does stops it.
132 let layouts = self
133 .recurse()
134 .into_iter()
135 .filter_map(|(offset, element)| match element {
136 StreamElement::Layout(Layout::Staff(staff)) => Some((offset, staff)),
137 _ => None,
138 });
139 for (offset, staff) in layouts {
140 if offset > 0.0 {
141 break;
142 }
143 if let Some(lines) = staff.staff_lines {
144 return lines;
145 }
146 }
147 DEFAULT_STAFF_LINES
148 }
149}
150
151/// The lines an ordinary staff has, which music21 answers for a stream
152/// whose layout says nothing.
153const DEFAULT_STAFF_LINES: i64 = 5;
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::bar::BarlineType;
159 use crate::duration::Duration;
160 use crate::layout::StaffLayout;
161 use crate::note::Note;
162
163 fn part_ending(bar_types: &[BarlineType]) -> Stream {
164 let mut part = Stream::with_kind(StreamKind::Part);
165 for bar_type in bar_types {
166 let mut measure = Stream::with_kind(StreamKind::Measure);
167 measure.set_right_barline(Some(Barline::new(*bar_type)));
168 part.push(measure);
169 }
170 part
171 }
172
173 #[test]
174 fn the_last_measures_right_barline_is_the_final_one() {
175 let part = part_ending(&[BarlineType::Double, BarlineType::Final]);
176 assert_eq!(
177 part.final_barline().map(Barline::bar_type),
178 Some(BarlineType::Final)
179 );
180 // A measure answers its own.
181 let measure = part.measures()[0];
182 assert_eq!(
183 measure.final_barline().map(Barline::bar_type),
184 Some(BarlineType::Double)
185 );
186 // A stream holding neither measures nor a barline of its own has
187 // none, where music21 refused before v6.3.
188 let mut loose = Stream::with_kind(StreamKind::Part);
189 loose.insert(0.0, Note::from_name("C4").expect("a note name"));
190 assert!(loose.final_barline().is_none());
191 }
192
193 #[test]
194 fn a_score_answers_one_barline_for_each_part() {
195 let mut score = Stream::with_kind(StreamKind::Score);
196 score.push(part_ending(&[BarlineType::Final]));
197 score.push(part_ending(&[BarlineType::Double, BarlineType::Regular]));
198 let types: Vec<Option<BarlineType>> = score
199 .final_barlines()
200 .into_iter()
201 .map(|barline| barline.map(Barline::bar_type))
202 .collect();
203 assert_eq!(
204 types,
205 [Some(BarlineType::Final), Some(BarlineType::Regular)]
206 );
207 // The score itself holds no measures, so it has no one barline.
208 assert!(score.final_barline().is_none());
209 // A part answers no list, having no part-like streams.
210 assert!(score.final_barlines()[0].is_some());
211 assert!(
212 part_ending(&[BarlineType::Final])
213 .final_barlines()
214 .is_empty()
215 );
216 }
217
218 #[test]
219 fn writing_the_final_barline_reaches_every_part() {
220 let mut score = Stream::with_kind(StreamKind::Score);
221 score.push(part_ending(&[BarlineType::Regular, BarlineType::Regular]));
222 score.push(part_ending(&[BarlineType::Regular]));
223 score.set_final_barline(Some(Barline::new(BarlineType::None)));
224 let types: Vec<Option<BarlineType>> = score
225 .final_barlines()
226 .into_iter()
227 .map(|barline| barline.map(Barline::bar_type))
228 .collect();
229 assert_eq!(types, [Some(BarlineType::None), Some(BarlineType::None)]);
230 // The first part's earlier measure is untouched.
231 assert_eq!(
232 score.parts()[0].measures()[0]
233 .right_barline()
234 .map(Barline::bar_type),
235 Some(BarlineType::Regular)
236 );
237 }
238
239 /// Each answer here is music21's own for the same arrangement.
240 #[test]
241 fn a_staff_says_how_many_lines_it_has_from_its_start() {
242 let mut measure = Stream::with_kind(StreamKind::Measure);
243 assert_eq!(measure.staff_lines(), 5);
244
245 let staff = StaffLayout {
246 staff_lines: Some(1),
247 ..StaffLayout::default()
248 };
249 measure.insert(0.0, StreamElement::Layout(Layout::Staff(staff.clone())));
250 assert_eq!(measure.staff_lines(), 1);
251
252 // A part reads the layout of the measure it starts with.
253 let mut part = Stream::with_kind(StreamKind::Part);
254 part.push(measure.clone());
255 assert_eq!(part.staff_lines(), 1);
256
257 // A layout in a measure that starts after the stream does says
258 // nothing about it.
259 let mut later = Stream::with_kind(StreamKind::Part);
260 let mut first = Stream::with_kind(StreamKind::Measure);
261 first.insert(
262 0.0,
263 Note::from_name("C4")
264 .expect("a note name")
265 .with_duration(Duration::whole()),
266 );
267 later.push(first);
268 later.push(measure.clone());
269 assert_eq!(later.staff_lines(), 5);
270
271 // A measure holding nothing takes no time, so the one after it
272 // starts where it does and is read after all.
273 let mut empty_first = Stream::with_kind(StreamKind::Part);
274 empty_first.push(Stream::with_kind(StreamKind::Measure));
275 empty_first.push(measure);
276 assert_eq!(empty_first.staff_lines(), 1);
277 }
278}