Skip to main content

music21/stream/
lookups.rs

1//! Finding elements by where they stand: music21's `getElementAtOrBefore`,
2//! `getElementBeforeOffset`, `getElementAfterElement`,
3//! `beatAndMeasureFromOffset`, `getTimeSignatures` and `getInstruments`.
4
5use super::exact::Exact;
6use super::{Stream, StreamElement, StreamEvent};
7use crate::defaults::FloatType;
8use crate::error::{Error, Result};
9use crate::instrument::Instrument;
10use crate::makenotation::{event_order, op_frac};
11use crate::meter::TimeSignature;
12
13fn stream_error(message: impl Into<String>) -> Error {
14    Error::Stream(message.into())
15}
16
17/// Whether an element is of one of `classes`, or of any where none are
18/// named.
19fn of_classes(element: &StreamElement, classes: &[&str]) -> bool {
20    classes.is_empty() || classes.iter().any(|class| element.is_instance(class))
21}
22
23/// The time signatures a stream holds directly, with where each stands.
24fn time_signatures_in(stream: &Stream) -> impl Iterator<Item = (FloatType, TimeSignature)> + '_ {
25    stream
26        .events
27        .iter()
28        .filter_map(|event| match event.element() {
29            StreamElement::TimeSignature(meter) => Some((event.offset(), meter.clone())),
30            _ => None,
31        })
32}
33
34/// The measure an event is, with where it stands.
35fn measure_of(event: &StreamEvent) -> Option<(FloatType, &Stream)> {
36    match event.element() {
37        StreamElement::Stream(inner) => Some((event.offset(), &**inner)),
38        _ => None,
39    }
40}
41
42impl Stream {
43    /// The element this stream holds itself that starts at `offset`, or
44    /// nearest before it, of one of `classes` (any where none are named):
45    /// music21's `getElementAtOrBefore`. Of several starting at the same
46    /// place, the last in the stream's order.
47    ///
48    /// music21 also looks among a stream's elements stored at its end, which
49    /// is where it keeps a measure's right barline; the crate keeps barlines
50    /// beside the elements ([`Stream::right_barline`]), so they are not
51    /// found here.
52    ///
53    /// ```
54    /// use music21::tinynotation::from_tiny_notation;
55    ///
56    /// let line = from_tiny_notation("4/4 c4 d4 e2")?.flatten();
57    /// let found = line.element_at_or_before(1.5, &["Note"]).unwrap();
58    /// assert_eq!(found.offset(), 1.0);
59    /// # Ok::<(), music21::Error>(())
60    /// ```
61    pub fn element_at_or_before(
62        &self,
63        offset: FloatType,
64        classes: &[&str],
65    ) -> Option<&StreamEvent> {
66        self.nearest_before(offset, classes, false)
67    }
68
69    /// The element nearest before `offset`, not at it: music21's
70    /// `getElementBeforeOffset`. As [`Stream::element_at_or_before`]
71    /// otherwise.
72    pub fn element_before_offset(
73        &self,
74        offset: FloatType,
75        classes: &[&str],
76    ) -> Option<&StreamEvent> {
77        self.nearest_before(offset, classes, true)
78    }
79
80    fn nearest_before(
81        &self,
82        offset: FloatType,
83        classes: &[&str],
84        before_not_at: bool,
85    ) -> Option<&StreamEvent> {
86        let offset = op_frac(offset);
87        let mut best: Option<(FloatType, &StreamEvent)> = None;
88        for event in &self.events {
89            if !of_classes(event.element(), classes) {
90                continue;
91            }
92            let span = op_frac(offset - event.offset());
93            if span < 0.0 || (span == 0.0 && before_not_at) {
94                continue;
95            }
96            // music21 starts the nearest span at the offset itself, so an
97            // element standing before nought is never found.
98            let better = match best {
99                None => span <= offset,
100                Some((nearest, held)) => {
101                    span < nearest
102                        || (span == nearest && event_order(event, held) != std::cmp::Ordering::Less)
103                }
104            };
105            if better {
106                best = Some((span, event));
107            }
108        }
109        best.map(|(_, event)| event)
110    }
111
112    /// The element after the one at `index` among this stream's own, of one
113    /// of `classes` (any where none are named): music21's
114    /// `getElementAfterElement`.
115    ///
116    /// # Errors
117    ///
118    /// An index this stream has no element at.
119    pub fn element_after(&self, index: usize, classes: &[&str]) -> Result<Option<&StreamEvent>> {
120        if index >= self.events.len() {
121            return Err(stream_error("cannot find object in this Stream"));
122        }
123        Ok(self.events[index + 1..]
124            .iter()
125            .find(|event| of_classes(event.element(), classes)))
126    }
127
128    /// Which beat of which measure `offset` falls on: music21's
129    /// `beatAndMeasureFromOffset`. The beat counts from one, as a
130    /// proportion of the time signature in force at the measure.
131    ///
132    /// A measure with a number suffix, or numbered nought where
133    /// `fix_zeros`, is taken as the second half of the measure before it,
134    /// which is answered instead, with the beat counted on from that
135    /// measure's last; a measure so numbered that opens the piece is taken
136    /// as a pickup, its beats counted from the end of a full bar. A score
137    /// answers for its first part with measures.
138    ///
139    /// ```
140    /// use music21::tinynotation::from_tiny_notation;
141    ///
142    /// let line = from_tiny_notation("3/4 c4 d4 e4 f2.")?;
143    /// let (beat, measure) = line.beat_and_measure_from_offset(4.0, true)?;
144    /// assert_eq!((beat, measure.number()), (2.0, 2));
145    /// # Ok::<(), music21::Error>(())
146    /// ```
147    ///
148    /// # Errors
149    ///
150    /// A stream with no measures, or none in any part; no measure at or
151    /// before `offset`; no time signature in force there; or an offset past
152    /// the bar it falls in.
153    pub fn beat_and_measure_from_offset(
154        &self,
155        offset: FloatType,
156        fix_zeros: bool,
157    ) -> Result<(FloatType, &Stream)> {
158        let mut holder = self;
159        if !self.has_measures() {
160            if !self.has_part_like_streams() {
161                return Err(stream_error(
162                    "beatAndMeasureFromOffset: could not find any measures!",
163                ));
164            }
165            holder = self
166                .events
167                .iter()
168                .filter_map(|event| event.element().as_stream())
169                .find(|inner| inner.has_measures())
170                .ok_or_else(|| {
171                    stream_error("beatAndMeasureFromOffset: could not find any parts!")
172                })?;
173        }
174        let (measure_offset, measure) = holder
175            .element_at_or_before(offset, &["Measure"])
176            .and_then(measure_of)
177            .ok_or_else(|| stream_error("beatAndMeasureFromOffset: no measure at that offset."))?;
178        let meter = holder.time_signature_at(measure_offset).ok_or_else(|| {
179            stream_error(
180                "beatAndMeasureFromOffset: could not find a time signature for that place.",
181            )
182        })?;
183        let beat = meter
184            .beat_proportion(op_frac(offset - measure_offset))
185            .map_err(|_| {
186                stream_error("beatAndMeasureFromOffset: offset is beyond the end of the piece")
187            })?;
188
189        let suffixed = measure
190            .number_suffix()
191            .is_some_and(|suffix| !suffix.is_empty());
192        if !suffixed && !(fix_zeros && measure.number() == 0) {
193            return Ok((beat, measure));
194        }
195        // music21 asks whether the measure before is truthy: whether it
196        // holds anything.
197        let previous = holder
198            .element_before_offset(measure_offset, &["Measure"])
199            .and_then(measure_of)
200            .filter(|(_, previous)| {
201                !previous.events.is_empty() || previous.right_barline().is_some()
202            });
203        match previous {
204            Some((previous_offset, previous)) => {
205                let opening = holder.time_signature_at(previous_offset).ok_or_else(|| {
206                    stream_error(
207                        "beatAndMeasureFromOffset: partial measure found, but could not find a time signature for the preceding measure",
208                    )
209                })?;
210                // Each time signature in the first half counts the stretch up
211                // to the next one; one standing where another does replaces it.
212                let mut segments = vec![(opening, 0.0)];
213                for (start, meter) in time_signatures_in(previous) {
214                    if segments.last().is_some_and(|(_, last)| *last == start) {
215                        segments.pop();
216                    }
217                    segments.push((meter, start));
218                }
219                let filled = op_frac(previous.end_offset());
220                let mut pad = Exact::integer(0);
221                for (index, (meter, start)) in segments.iter().enumerate() {
222                    let end = segments.get(index + 1).map_or(filled, |(_, next)| *next);
223                    let length = op_frac(end - start);
224                    pad = pad.plus(if length == meter.bar_quarter_length() {
225                        Exact::integer(i64::from(meter.beat_count()))
226                    } else {
227                        Exact::held(meter.beat_proportion(length)?).plus(Exact::integer(-1))
228                    });
229                }
230                let beat = match segments.last() {
231                    Some((closing, _))
232                        if !time_signatures_in(measure).any(|(start, _)| start == 0.0) =>
233                    {
234                        closing.beat_proportion(op_frac(offset - measure_offset))?
235                    }
236                    _ => beat,
237                };
238                Ok((Exact::held(beat).plus(pad).to_float(), previous))
239            }
240            None => {
241                let pad =
242                    Exact::held(meter.beat_proportion(
243                        meter.bar_quarter_length() - op_frac(measure.end_offset()),
244                    )?)
245                    .plus(Exact::integer(-1));
246                Ok((Exact::held(beat).plus(pad).to_float(), measure))
247            }
248        }
249    }
250
251    /// The time signatures this stream holds, however deep, each where it
252    /// stands from the stream's start: music21's `getTimeSignatures`. With
253    /// `return_default`, 4/4 is put first at nought where none stands there.
254    ///
255    /// music21 also looks outside a stream holding none, through the
256    /// streams that hold it; a crate stream knows nothing outside itself.
257    ///
258    /// # Errors
259    ///
260    /// None in practice: the default 4/4 is always made.
261    pub fn time_signatures(&self, return_default: bool) -> Result<Vec<(FloatType, TimeSignature)>> {
262        let mut found: Vec<(FloatType, TimeSignature)> = self
263            .flatten()
264            .events
265            .into_iter()
266            .filter_map(|event| match event.element {
267                StreamElement::TimeSignature(meter) => Some((event.offset, meter)),
268                _ => None,
269            })
270            .collect();
271        if return_default && found.first().is_none_or(|(offset, _)| *offset > 0.0) {
272            found.insert(0, (0.0, TimeSignature::new(4, 4)?));
273        }
274        Ok(found)
275    }
276
277    /// The instruments this stream holds, however deep where `recurse`, in
278    /// order: music21's `getInstruments`. With `return_default`, an
279    /// instrument with an empty part name stands for none found.
280    ///
281    /// music21 also asks the stream holding this one where it holds none; a
282    /// crate stream knows nothing outside itself.
283    pub fn instruments(&self, recurse: bool, return_default: bool) -> Vec<Instrument> {
284        let held = if recurse {
285            self.flatten()
286        } else {
287            self.clone()
288        };
289        let mut found: Vec<Instrument> = held
290            .events
291            .into_iter()
292            .filter_map(|event| match event.element {
293                StreamElement::Instrument(instrument) => Some(*instrument),
294                _ => None,
295            })
296            .collect();
297        if return_default && found.is_empty() {
298            let mut instrument = Instrument::new();
299            instrument.set_part_name(Some(String::new()));
300            found.push(instrument);
301        }
302        found
303    }
304
305    /// The first instrument this stream holds itself, or with `recurse`
306    /// however deep: music21's `getInstrument`. With `return_default`, an
307    /// instrument with an empty part name where none is.
308    pub fn instrument(&self, recurse: bool, return_default: bool) -> Option<Instrument> {
309        self.instruments(recurse, return_default).into_iter().next()
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::duration::Duration;
317    use crate::note::Note;
318    use crate::rest::Rest;
319    use crate::stream::StreamKind;
320
321    #[test]
322    fn the_nearest_before_is_the_last_of_its_offset() -> Result<()> {
323        let mut stream = Stream::new();
324        stream.insert(0.0, Note::from_name("C4")?);
325        stream.insert(1.0, Rest::new(Duration::quarter()));
326        stream.insert(1.0, Note::from_name("D4")?);
327        let at = stream.element_at_or_before(1.0, &[]).unwrap();
328        assert_eq!(at.offset(), 1.0);
329        assert!(matches!(at.element(), StreamElement::Note(_)));
330        assert!(matches!(
331            stream
332                .element_at_or_before(1.0, &["Rest"])
333                .unwrap()
334                .element(),
335            StreamElement::Rest(_)
336        ));
337        assert_eq!(
338            stream.element_before_offset(1.0, &[]).unwrap().offset(),
339            0.0
340        );
341        assert!(stream.element_before_offset(0.0, &[]).is_none());
342        assert!(stream.element_at_or_before(-1.0, &[]).is_none());
343        assert!(matches!(
344            stream.element_after(0, &["Note"])?.unwrap().element(),
345            StreamElement::Note(_)
346        ));
347        assert!(stream.element_after(2, &[])?.is_none());
348        assert!(stream.element_after(3, &[]).is_err());
349        Ok(())
350    }
351
352    #[test]
353    fn a_pickup_counts_its_beats_from_the_end_of_a_bar() -> Result<()> {
354        let mut part = Stream::with_kind(StreamKind::Part);
355        let mut pickup = Stream::with_kind(StreamKind::Measure);
356        pickup.insert(0.0, TimeSignature::new(4, 4)?);
357        pickup.insert(0.0, Note::from_name("G4")?);
358        let mut first = Stream::with_kind(StreamKind::Measure);
359        first.set_number(1);
360        first.insert(0.0, Note::from_name("C5")?.with_duration(Duration::whole()));
361        part.insert(0.0, pickup);
362        part.insert(1.0, first);
363        let (beat, measure) = part.beat_and_measure_from_offset(0.0, true)?;
364        assert_eq!((beat, measure.number()), (4.0, 0));
365        let (beat, measure) = part.beat_and_measure_from_offset(3.0, true)?;
366        assert_eq!((beat, measure.number()), (3.0, 1));
367        Ok(())
368    }
369
370    #[test]
371    fn a_meter_changing_at_a_split_counts_each_half_in_its_own() -> Result<()> {
372        let mut part = Stream::with_kind(StreamKind::Part);
373        let mut whole = Stream::with_kind(StreamKind::Measure);
374        whole.set_number(1);
375        whole.insert(0.0, TimeSignature::new(4, 4)?);
376        whole.insert(0.0, Note::from_name("C4")?.with_duration(Duration::whole()));
377        let mut first_half = Stream::with_kind(StreamKind::Measure);
378        first_half.set_number(2);
379        first_half.insert(0.0, Note::from_name("C4")?.with_duration(Duration::half()));
380        first_half.insert(2.0, TimeSignature::new(12, 16)?);
381        let mut second_half = Stream::with_kind(StreamKind::Measure);
382        second_half.set_number(2);
383        second_half.set_number_suffix(Some("X1".to_string()));
384        second_half.insert(
385            0.0,
386            Note::from_name("C4")?.with_duration(Duration::new(0.75)?),
387        );
388        second_half.insert(
389            0.75,
390            Note::from_name("C4")?.with_duration(Duration::new(0.75)?),
391        );
392        part.insert(0.0, whole);
393        part.insert(4.0, first_half);
394        part.insert(6.0, second_half);
395        let beats = [5.0, 6.0, 6.75]
396            .into_iter()
397            .map(|offset| Ok(part.beat_and_measure_from_offset(offset, true)?.0))
398            .collect::<Result<Vec<_>>>()?;
399        assert_eq!(beats, [2.0, 3.0, 4.0]);
400        Ok(())
401    }
402
403    #[test]
404    fn a_default_meter_and_instrument_stand_in_for_none() -> Result<()> {
405        let stream = Stream::new();
406        let meters = stream.time_signatures(true)?;
407        assert_eq!(meters.len(), 1);
408        assert_eq!(meters[0].1.ratio_string(), "4/4");
409        assert!(stream.time_signatures(false)?.is_empty());
410        assert_eq!(stream.instrument(true, true).unwrap().part_name(), Some(""));
411        assert!(stream.instrument(true, false).is_none());
412        Ok(())
413    }
414}