Skip to main content

music21_rs/stream/
placed.rs

1//! An element together with where it stands: music21's sites, answered
2//! as a composite rather than stored on the element.
3//!
4//! music21 keeps on every object a back-reference to each stream holding
5//! it, so that `getContextByClass` can walk up to the key or metre in
6//! force; that is why its objects are deep-copied so often. Its author
7//! would do it differently: "store objects and sites separately and return
8//! them as a separate composite object" ("Music21's Mistakes", point 2).
9//! [`Stream::placed`] is that composite. The element stays a plain value in
10//! the tree; where it sits, and what is in force there, is worked out from
11//! the tree when asked.
12//!
13//! ```text
14//!   Score ─┬─ Part 1 ─┬─ Measure 1: KeySignature(-1), C4 …
15//!          │          └─ Measure 2: D4 …   ─► Placed { D4, offset 4.0,
16//!          │                                     measure offset 0.0,
17//!          │                                     key: one flat }
18//!          └─ Part 2 ─── Measure 1: E4 …   ─► Placed { E4, key: none }
19//! ```
20
21use super::{Stream, StreamElement, StreamKind};
22use crate::{defaults::FloatType, key::KeySignature, meter::TimeSignature, tempo::MetronomeMark};
23
24/// Which marks a leaf reads: those of its own part, and those held by the
25/// stream asked, which stand over every part.
26#[derive(Clone, Copy, PartialEq)]
27enum Scope {
28    Shared,
29    Part(usize),
30}
31
32impl Scope {
33    fn reaches(self, leaf: Scope) -> bool {
34        self == Scope::Shared || self == leaf
35    }
36}
37
38/// One element of a stream with its place in it: its offset, its offset in
39/// the measure holding it, and the key signature, metre and tempo in force
40/// where it stands.
41#[derive(Clone, Debug)]
42pub struct Placed<'a> {
43    element: &'a StreamElement,
44    offset: FloatType,
45    measure_offset: Option<FloatType>,
46    key_signature: Option<&'a KeySignature>,
47    time_signature: Option<&'a TimeSignature>,
48    metronome_mark: Option<&'a MetronomeMark>,
49}
50
51impl<'a> Placed<'a> {
52    /// The element itself.
53    pub fn element(&self) -> &'a StreamElement {
54        self.element
55    }
56
57    /// The offset from the start of the stream asked.
58    pub fn offset(&self) -> FloatType {
59        self.offset
60    }
61
62    /// The offset from the start of the measure holding the element, or
63    /// `None` outside any measure.
64    pub fn measure_offset(&self) -> Option<FloatType> {
65        self.measure_offset
66    }
67
68    /// The key signature in force where the element stands.
69    pub fn key_signature(&self) -> Option<&'a KeySignature> {
70        self.key_signature
71    }
72
73    /// The metre in force where the element stands.
74    pub fn time_signature(&self) -> Option<&'a TimeSignature> {
75        self.time_signature
76    }
77
78    /// The tempo in force where the element stands.
79    pub fn metronome_mark(&self) -> Option<&'a MetronomeMark> {
80        self.metronome_mark
81    }
82}
83
84/// A leaf as the walk finds it, before its context is looked up.
85struct Leaf<'a> {
86    element: &'a StreamElement,
87    offset: FloatType,
88    measure_start: Option<FloatType>,
89    scope: Scope,
90}
91
92impl Stream {
93    /// Every element that is not itself a stream, in [`Stream::leaves`]
94    /// order, each with where it stands and what is in force there.
95    ///
96    /// A mark is in force from its offset on, the last one at or before an
97    /// element winning. In a stream holding parts, each part reads its own
98    /// marks and those the stream holds directly, never another part's,
99    /// which is music21's `getContextByClass` for music written once.
100    ///
101    /// ```
102    /// use music21_rs::{KeySignature, Note, Stream, StreamElement, stream::StreamKind};
103    ///
104    /// let mut part = Stream::with_kind(StreamKind::Part);
105    /// part.insert(0.0, KeySignature::new(-1));
106    /// part.insert(0.0, Note::from_name("B-4")?);
107    /// let placed = part.placed();
108    /// let note = placed
109    ///     .iter()
110    ///     .find(|placed| matches!(placed.element(), StreamElement::Note(_)))
111    ///     .unwrap();
112    /// assert_eq!(note.key_signature().and_then(KeySignature::sharps), Some(-1));
113    /// # Ok::<(), music21_rs::Error>(())
114    /// ```
115    pub fn placed(&self) -> Vec<Placed<'_>> {
116        let parts = self.has_part_like_streams();
117        let mut leaves = Vec::new();
118        for (index, event) in self.events.iter().enumerate() {
119            let StreamElement::Stream(stream) = &event.element else {
120                leaves.push(Leaf {
121                    element: &event.element,
122                    offset: event.offset,
123                    measure_start: self.measure_start(0.0),
124                    scope: Scope::Shared,
125                });
126                continue;
127            };
128
129            let scope = if parts {
130                Scope::Part(index)
131            } else {
132                Scope::Shared
133            };
134            let measure_start = self.measure_start(0.0);
135            stream.gather(event.offset, measure_start, scope, &mut leaves);
136        }
137
138        leaves
139            .iter()
140            .map(|leaf| Placed {
141                element: leaf.element,
142                offset: leaf.offset,
143                measure_offset: leaf.measure_start.map(|start| leaf.offset - start),
144                key_signature: in_force(&leaves, leaf, |element| match element {
145                    StreamElement::KeySignature(key) => Some(key),
146                    _ => None,
147                }),
148                time_signature: in_force(&leaves, leaf, |element| match element {
149                    StreamElement::TimeSignature(meter) => Some(meter),
150                    _ => None,
151                }),
152                metronome_mark: in_force(&leaves, leaf, |element| match element {
153                    StreamElement::MetronomeMark(mark) => Some(mark),
154                    _ => None,
155                }),
156            })
157            .collect()
158    }
159
160    /// Where a measure starts, when this stream is one starting at `base`.
161    fn measure_start(&self, base: FloatType) -> Option<FloatType> {
162        (self.kind == StreamKind::Measure).then_some(base)
163    }
164
165    fn gather<'a>(
166        &'a self,
167        base: FloatType,
168        measure_start: Option<FloatType>,
169        scope: Scope,
170        out: &mut Vec<Leaf<'a>>,
171    ) {
172        let measure_start = self.measure_start(base).or(measure_start);
173        for event in &self.events {
174            let offset = base + event.offset;
175            if let StreamElement::Stream(stream) = &event.element {
176                stream.gather(offset, measure_start, scope, out);
177                continue;
178            }
179
180            out.push(Leaf {
181                element: &event.element,
182                offset,
183                measure_start,
184                scope,
185            });
186        }
187    }
188}
189
190/// The last mark of one sort at or before a leaf that the leaf's scope
191/// reaches; of two at one offset, the later-listed.
192fn in_force<'a, T>(
193    leaves: &[Leaf<'a>],
194    leaf: &Leaf<'a>,
195    read: impl Fn(&'a StreamElement) -> Option<&'a T>,
196) -> Option<&'a T> {
197    let mut found: Option<(FloatType, &'a T)> = None;
198    for mark in leaves {
199        if mark.offset > leaf.offset || !mark.scope.reaches(leaf.scope) {
200            continue;
201        }
202        let Some(value) = read(mark.element) else {
203            continue;
204        };
205        if found.is_none_or(|(offset, _)| mark.offset >= offset) {
206            found = Some((mark.offset, value));
207        }
208    }
209    found.map(|(_, value)| value)
210}
211
212#[cfg(test)]
213mod tests {
214    use crate::{
215        KeySignature, Note, Stream, StreamElement, meter::TimeSignature, stream::StreamKind,
216    };
217
218    fn measure(notes: &[&str]) -> Stream {
219        let mut measure = Stream::with_kind(StreamKind::Measure);
220        for name in notes {
221            measure.push(Note::from_name(name).unwrap());
222        }
223        measure
224    }
225
226    /// A part's key reaches its later measures but not the other part.
227    #[test]
228    fn each_part_reads_its_own_marks() {
229        let mut first = measure(&["C4", "D4", "E4", "F4"]);
230        first.insert(0.0, KeySignature::new(-1));
231        let mut flute = Stream::with_kind(StreamKind::Part);
232        flute.insert(0.0, first);
233        flute.insert(4.0, measure(&["G4"]));
234        let mut oboe = Stream::with_kind(StreamKind::Part);
235        oboe.insert(0.0, measure(&["A4"]));
236        let mut score = Stream::with_kind(StreamKind::Score);
237        score.insert(0.0, TimeSignature::new(4, 4).unwrap());
238        score.insert(0.0, flute);
239        score.insert(0.0, oboe);
240
241        let notes: Vec<_> = score
242            .placed()
243            .into_iter()
244            .filter(|placed| matches!(placed.element(), StreamElement::Note(_)))
245            .collect();
246        let sharps = |index: usize| notes[index].key_signature().and_then(KeySignature::sharps);
247
248        assert_eq!(notes.len(), 6);
249        assert_eq!(sharps(4), Some(-1));
250        assert_eq!(notes[4].offset(), 4.0);
251        assert_eq!(notes[4].measure_offset(), Some(0.0));
252        assert_eq!(notes[3].measure_offset(), Some(3.0));
253        assert_eq!(sharps(5), None);
254        assert!(notes.iter().all(|placed| placed.time_signature().is_some()));
255    }
256
257    /// The composite lists what `leaves` lists, in the same order.
258    #[test]
259    fn placed_follows_leaves() {
260        let mut part = Stream::with_kind(StreamKind::Part);
261        part.insert(0.0, measure(&["C4", "D4"]));
262        part.insert(2.0, measure(&["E4"]));
263        let offsets: Vec<_> = part.placed().iter().map(|placed| placed.offset()).collect();
264        let leaves: Vec<_> = part.leaves().iter().map(|(offset, _)| *offset).collect();
265        assert_eq!(offsets, leaves);
266    }
267}