Skip to main content

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}