Skip to main content

music21_rs/stream/
queries.rs

1//! What a stream holds and where: music21's questions of a stream's layout
2//! -- its gaps and overlaps, what sounds when, its measures by offset.
3
4use super::span::OffsetSpan;
5use super::{Stream, StreamElement, StreamEvent, StreamKind};
6use crate::defaults::FloatType;
7use crate::makenotation::op_frac;
8use crate::rest::Rest;
9
10/// One element of a stream with where it starts and ends and the voice it
11/// is in: music21's `OffsetMap`.
12#[derive(Clone, Debug)]
13pub struct OffsetMapEntry {
14    /// The element.
15    pub element: StreamElement,
16    /// Where it starts.
17    pub offset: FloatType,
18    /// Where it ends.
19    pub end_time: FloatType,
20    /// The voice it is in, by its place among the stream's voices, or
21    /// nothing for an element outside them.
22    pub voice: Option<usize>,
23}
24
25impl Stream {
26    /// Where the last element this stream holds itself starts, nought for
27    /// an empty stream: music21's `highestOffset`.
28    pub fn highest_offset(&self) -> FloatType {
29        self.events
30            .iter()
31            .map(StreamEvent::offset)
32            .fold(None, |most: Option<FloatType>, offset| {
33                Some(most.map_or(offset, |most| most.max(offset)))
34            })
35            .unwrap_or(0.0)
36    }
37
38    /// Where the first element this stream holds itself starts, nought for
39    /// an empty stream: music21's `lowestOffset`.
40    pub fn lowest_offset(&self) -> FloatType {
41        self.events
42            .iter()
43            .map(StreamEvent::offset)
44            .fold(None, |least: Option<FloatType>, offset| {
45                Some(least.map_or(offset, |least| least.min(offset)))
46            })
47            .unwrap_or(0.0)
48    }
49
50    /// Whether this stream holds a measure itself: music21's `hasMeasures`.
51    pub fn has_measures(&self) -> bool {
52        self.events
53            .iter()
54            .any(|event| matches!(event.element(), StreamElement::Stream(inner) if inner.kind() == StreamKind::Measure))
55    }
56
57    /// Whether this stream holds a voice itself: music21's `hasVoices`.
58    pub fn has_voices(&self) -> bool {
59        self.events
60            .iter()
61            .any(|event| matches!(event.element(), StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice))
62    }
63
64    /// Whether the stream is notation as music21 writes it out: a measure
65    /// or a voice; a part of measures; a score or other stream of parts, or
66    /// an opus of scores, whose every stream holds measures; and nothing
67    /// holding notes or rests loose: music21's `isWellFormedNotation`.
68    pub fn is_well_formed_notation(&self) -> bool {
69        let all_hold_measures = |stream: &Stream| {
70            stream
71                .events
72                .iter()
73                .filter_map(|event| event.element().as_stream())
74                .all(Stream::has_measures)
75        };
76        match self.kind() {
77            StreamKind::Measure | StreamKind::Voice => true,
78            _ if self
79                .events
80                .iter()
81                .any(|event| event.element().is_instance("GeneralNote")) =>
82            {
83                false
84            }
85            StreamKind::Part | StreamKind::PartStaff => self.has_measures(),
86            StreamKind::Opus => self
87                .streams_of_kind(StreamKind::Score)
88                .into_iter()
89                .all(all_hold_measures),
90            _ if self.has_part_like_streams() => all_hold_measures(self),
91            _ => false,
92        }
93    }
94
95    /// The stretches of this stream, between its own elements, where
96    /// nothing sounds, each as a rest, or nothing where there are none:
97    /// music21's `findGaps`.
98    ///
99    /// ```
100    /// use music21_rs::{Duration, Note, Stream};
101    ///
102    /// let mut line = Stream::new();
103    /// line.insert(0.0, Note::from_name("C4")?.with_duration(Duration::quarter()));
104    /// line.insert(2.0, Note::from_name("D4")?.with_duration(Duration::quarter()));
105    /// let gaps = line.find_gaps().expect("a gap");
106    /// assert_eq!(gaps.events()[0].offset(), 1.0);
107    /// assert_eq!(gaps.events()[0].element().quarter_length(), 1.0);
108    /// assert!(!line.is_gapless());
109    /// # Ok::<(), music21_rs::Error>(())
110    /// ```
111    pub fn find_gaps(&self) -> Option<Stream> {
112        let mut gaps = Stream::with_kind(self.kind());
113        let mut highest: FloatType = 0.0;
114        for event in &self.events {
115            let offset = event.offset();
116            if offset > highest {
117                let length = op_frac(offset - highest);
118                if let Ok(duration) = crate::duration::Duration::new(length) {
119                    gaps.insert_sorted(vec![StreamEvent::new(
120                        highest,
121                        StreamElement::Rest(Rest::new(duration)),
122                    )]);
123                }
124            }
125            highest = op_frac(highest.max(offset + event.element().quarter_length()));
126        }
127        (!gaps.is_empty()).then_some(gaps)
128    }
129
130    /// Whether this stream has no gaps: music21's `isGapless`.
131    pub fn is_gapless(&self) -> bool {
132        self.find_gaps().is_none()
133    }
134
135    /// The flattened stream, each element with where it starts and ends.
136    fn spans(&self) -> (Stream, Vec<(FloatType, FloatType)>) {
137        let flat = self.flatten();
138        let spans = flat
139            .events
140            .iter()
141            .map(|event| {
142                let offset = event.offset();
143                (offset, op_frac(offset + event.element().quarter_length()))
144            })
145            .collect();
146        (flat, spans)
147    }
148
149    /// music21's `_findLayering`: for each element of the flattened stream,
150    /// the others it overlaps, found looking on from it until the first that
151    /// does not.
152    fn layering(spans: &[(FloatType, FloatType)]) -> Vec<Vec<usize>> {
153        let overlap = |a: (FloatType, FloatType), b: (FloatType, FloatType)| {
154            let (first, second) = if (b.0, b.1) < (a.0, a.1) {
155                (b, a)
156            } else {
157                (a, b)
158            };
159            second.0 < first.1
160        };
161        let mut map = vec![Vec::new(); spans.len()];
162        for i in 0..spans.len() {
163            for j in (i + 1)..spans.len() {
164                if overlap(spans[i], spans[j]) {
165                    map[i].push(j);
166                    map[j].push(i);
167                } else {
168                    break;
169                }
170            }
171        }
172        for list in &mut map {
173            list.sort_unstable();
174        }
175        map
176    }
177
178    /// The elements of the flattened stream that sound over one another,
179    /// gathered by the offset where each overlapping group starts:
180    /// music21's `getOverlaps`. Only elements found overlapping looking on
181    /// from one until the first that does not are gathered, as music21
182    /// gathers them.
183    ///
184    /// ```
185    /// use music21_rs::{Duration, Note, Stream};
186    ///
187    /// let mut line = Stream::new();
188    /// line.insert(0.0, Note::from_name("C4")?.with_duration(Duration::half()));
189    /// line.insert(1.0, Note::from_name("E4")?.with_duration(Duration::quarter()));
190    /// line.insert(3.0, Note::from_name("G4")?.with_duration(Duration::quarter()));
191    /// let overlaps = line.overlaps();
192    /// assert_eq!(overlaps.len(), 1);
193    /// assert_eq!(overlaps[0].0, 0.0);
194    /// assert_eq!(overlaps[0].1.len(), 2);
195    /// assert!(!line.is_sequence());
196    /// # Ok::<(), music21_rs::Error>(())
197    /// ```
198    pub fn overlaps(&self) -> Vec<(FloatType, Vec<StreamEvent>)> {
199        let flat = self.flatten();
200        self.overlap_groups()
201            .into_iter()
202            .map(|(offset, members)| {
203                (
204                    offset,
205                    members
206                        .into_iter()
207                        .map(|index| flat.events[index].clone())
208                        .collect(),
209                )
210            })
211            .collect()
212    }
213
214    /// [`Stream::overlaps`], each group's members by their position among
215    /// [`Stream::leaves`] rather than as values.
216    ///
217    /// music21 hands back the elements themselves, so a caller keeping an
218    /// object per element -- a binding keeping what it read the stream
219    /// from -- finds each again by that position.
220    ///
221    /// ```
222    /// use music21_rs::{Duration, Note, Stream, StreamKind};
223    ///
224    /// let mut part = Stream::with_kind(StreamKind::Part);
225    /// let mut measure = Stream::with_kind(StreamKind::Measure);
226    /// measure.insert(0.0, Note::from_name("C4")?.with_duration(Duration::half()));
227    /// measure.insert(1.0, Note::from_name("E4")?);
228    /// part.insert(0.0, measure);
229    ///
230    /// // The two notes overlap, and are the part's first two leaves. The
231    /// // one overlapped is listed before the one looking on from itself,
232    /// // which is the order music21's own dictionary takes them in.
233    /// let found = part.overlap_positions();
234    /// assert_eq!(found.len(), 1);
235    /// assert_eq!(found[0], (0.0, vec![1, 0]));
236    /// # Ok::<(), music21_rs::Error>(())
237    /// ```
238    pub fn overlap_positions(&self) -> Vec<(FloatType, Vec<usize>)> {
239        let leaves = self.leaves();
240        // The flattened stream is the leaves in `flatten`'s order, so the
241        // nth element of it is the leaf that order puts nth.
242        let order = Stream::flatten_order(&leaves);
243        self.overlap_groups()
244            .into_iter()
245            .map(|(offset, members)| {
246                (
247                    offset,
248                    members.into_iter().map(|index| order[index]).collect(),
249                )
250            })
251            .collect()
252    }
253
254    /// The groups [`Stream::overlaps`] finds, each holding indices into the
255    /// flattened stream, in the order music21's dictionary takes them.
256    fn overlap_groups(&self) -> Vec<(FloatType, Vec<usize>)> {
257        let (flat, spans) = self.spans();
258        let map = Self::layering(&spans);
259        // Each group by its offset, holding indices of the flattened stream,
260        // in the order music21's dictionary takes them.
261        let mut groups: Vec<(FloatType, Vec<usize>)> = Vec::new();
262        let held_at = |groups: &[(FloatType, Vec<usize>)], index: usize| {
263            groups
264                .iter()
265                .find(|(_, members)| members.contains(&index))
266                .map(|(offset, _)| *offset)
267        };
268        let push = |groups: &mut Vec<(FloatType, Vec<usize>)>, offset: FloatType, index: usize| {
269            match groups.iter_mut().find(|(known, _)| *known == offset) {
270                Some((_, members)) => members.push(index),
271                None => groups.push((offset, vec![index])),
272            }
273        };
274        for (index, indices) in map.iter().enumerate() {
275            if indices.is_empty() {
276                continue;
277            }
278            let source = flat.events[index].offset();
279            let mut destination = None;
280            for &other in indices {
281                let mut store = true;
282                if let Some(offset) = held_at(&groups, other) {
283                    store = false;
284                    destination = Some(offset);
285                }
286                let at = *destination.get_or_insert(source);
287                if store {
288                    push(&mut groups, at, other);
289                }
290            }
291            if held_at(&groups, index).is_none() {
292                let at = destination.unwrap_or(source);
293                push(&mut groups, at, index);
294            }
295        }
296        groups
297    }
298
299    /// Whether no element of the flattened stream overlaps another, as
300    /// [`Stream::overlaps`] finds them: music21's `isSequence`.
301    pub fn is_sequence(&self) -> bool {
302        let (_, spans) = self.spans();
303        Self::layering(&spans).iter().all(Vec::is_empty)
304    }
305
306    /// The offsets where something in this stream and something in
307    /// `other` both start, each stream's own elements: music21's
308    /// `simultaneousAttacks`.
309    pub fn simultaneous_attacks(&self, other: &Stream) -> Vec<FloatType> {
310        let mut offsets: Vec<FloatType> = Vec::new();
311        for event in &self.events {
312            if !offsets.contains(&event.offset()) {
313                offsets.push(event.offset());
314            }
315        }
316        let mut shared: Vec<FloatType> = offsets
317            .into_iter()
318            .filter(|offset| other.events.iter().any(|event| event.offset() == *offset))
319            .collect();
320        shared.sort_by(FloatType::total_cmp);
321        shared
322    }
323
324    /// The element of this stream sounding where `element` starts, at
325    /// `offset`: the one there, or of those there the first of `element`'s
326    /// class, or else the first; nothing where none is: music21's
327    /// `playingWhenAttacked`.
328    pub fn playing_when_attacked(
329        &self,
330        element: &StreamElement,
331        offset: FloatType,
332    ) -> Option<&StreamEvent> {
333        let mut span = OffsetSpan::at(offset);
334        span.must_begin_in_span = false;
335        let there: Vec<&StreamEvent> = self
336            .events
337            .iter()
338            .filter(|event| span.takes_at(event.offset(), event.element().quarter_length()))
339            .collect();
340        match there.as_slice() {
341            [] => None,
342            [only] => Some(*only),
343            many => {
344                let class = element.music21_classes()[0];
345                many.iter()
346                    .find(|event| event.element().is_instance(class))
347                    .or_else(|| many.first())
348                    .copied()
349            }
350        }
351    }
352
353    /// Every element of this stream sounding while `element`, at `offset`,
354    /// sounds, each at its offset from `offset`: music21's
355    /// `allPlayingWhileSounding`.
356    pub fn all_playing_while_sounding(&self, element: &StreamElement, offset: FloatType) -> Stream {
357        let end = offset + element.quarter_length();
358        let span = if end != offset {
359            OffsetSpan {
360                must_begin_in_span: false,
361                include_end_boundary: false,
362                include_elements_that_end_at_start: false,
363                ..OffsetSpan::between(offset, end)
364            }
365        } else {
366            OffsetSpan {
367                must_begin_in_span: false,
368                ..OffsetSpan::at(offset)
369            }
370        };
371        let mut out = Stream::with_kind(self.kind());
372        out.events = self
373            .events
374            .iter()
375            .filter(|event| span.takes_at(event.offset(), event.element().quarter_length()))
376            .map(|event| {
377                StreamEvent::new(op_frac(event.offset() - offset), event.element().clone())
378            })
379            .collect();
380        out
381    }
382
383    /// Each element of this stream with where it starts and ends, those of
384    /// its voices flattened and labelled with the voice, barlines left out:
385    /// music21's `offsetMap`.
386    pub fn offset_map(&self) -> Vec<OffsetMapEntry> {
387        let entry = |event: &StreamEvent, voice: Option<usize>| OffsetMapEntry {
388            element: event.element().clone(),
389            offset: event.offset(),
390            end_time: op_frac(event.offset() + event.element().quarter_length()),
391            voice,
392        };
393        let keep = |event: &&StreamEvent| !matches!(event.element(), StreamElement::Barline(_));
394        if !self.has_voices() {
395            return self
396                .events
397                .iter()
398                .filter(keep)
399                .map(|event| entry(event, None))
400                .collect();
401        }
402        let mut out: Vec<OffsetMapEntry> = self
403            .events
404            .iter()
405            .filter(|event| !matches!(event.element(), StreamElement::Stream(inner) if inner.kind() == StreamKind::Voice))
406            .filter(keep)
407            .map(|event| entry(event, None))
408            .collect();
409        for (index, voice) in self.voices().into_iter().enumerate() {
410            let flat = voice.flatten();
411            out.extend(
412                flat.events
413                    .iter()
414                    .filter(keep)
415                    .map(|event| entry(event, Some(index))),
416            );
417        }
418        out
419    }
420
421    /// The measures by offset, in order: a score's those of every part at
422    /// each offset, and another stream's its own: music21's
423    /// `measureOffsetMap`.
424    pub fn measure_offset_map(&self) -> Vec<(FloatType, Vec<&Stream>)> {
425        let mut map: Vec<(FloatType, Vec<&Stream>)> = Vec::new();
426        let parts = self.parts();
427        let holders: Vec<&Stream> = if self.kind() == StreamKind::Score && !parts.is_empty() {
428            parts
429        } else {
430            vec![self]
431        };
432        for holder in holders {
433            for event in &holder.events {
434                let StreamElement::Stream(measure) = event.element() else {
435                    continue;
436                };
437                if measure.kind() != StreamKind::Measure {
438                    continue;
439                }
440                match map.iter_mut().find(|(offset, _)| *offset == event.offset()) {
441                    Some((_, measures)) => measures.push(measure),
442                    None => map.push((event.offset(), vec![measure])),
443                }
444            }
445        }
446        map.sort_by(|left, right| left.0.total_cmp(&right.0));
447        map
448    }
449}