Skip to main content

music21_rs/
stream.rs

1//! Music on a timeline: music21's `stream.Stream` and the subclasses it
2//! nests inside one another.
3//!
4//! A [`Stream`] is a list of things at quarter-length offsets, and one of
5//! those things may be another stream: a score holds parts, a part holds
6//! measures, a measure holds voices and notes. [`Stream::flatten`] dissolves
7//! that nesting into one timeline the way music21's does, adding each
8//! stream's own offset to its contents'.
9//!
10//! **What is deliberately not here is music21's *sites*.** There an object
11//! can sit in several streams at once and carry a different offset in each,
12//! which is why it needs a back-reference to every stream holding it and why
13//! `getContextByClass` has a graph to walk. A stream here *owns* what it
14//! holds, so the nesting is a tree; asking what key or metre is in force at
15//! an offset is then a backwards look through the flattened timeline rather
16//! than a search through a graph. That is the same answer for music that is
17//! written once, which is all a stream built here can be.
18
19use crate::{
20    chord::Chord, defaults::FloatType, duration::Duration, error::Result, interval::Interval,
21    key::KeySignature, meter::TimeSignature, note::Note, pitch::Pitch, rest::Rest,
22    tempo::MetronomeMark,
23};
24
25/// Which of music21's `Stream` subclasses a stream stands for.
26///
27/// music21 makes each of these a class of its own; they carry no behaviour
28/// that a tag does not, and a tag is what the filters here need.
29#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub enum StreamKind {
32    /// A stream with no particular role: music21's `Stream`.
33    #[default]
34    Stream,
35    /// One independent line inside a measure.
36    Voice,
37    /// One bar.
38    Measure,
39    /// One instrument's line through the piece.
40    Part,
41    /// One staff of a part written on several.
42    PartStaff,
43    /// A whole piece, holding parts.
44    Score,
45    /// A collection of scores.
46    Opus,
47}
48
49impl StreamKind {
50    /// Every kind, in music21's order of containment.
51    pub const ALL: [StreamKind; 7] = [
52        StreamKind::Stream,
53        StreamKind::Voice,
54        StreamKind::Measure,
55        StreamKind::Part,
56        StreamKind::PartStaff,
57        StreamKind::Score,
58        StreamKind::Opus,
59    ];
60
61    /// music21's class name for this kind.
62    pub fn as_str(self) -> &'static str {
63        match self {
64            StreamKind::Stream => "Stream",
65            StreamKind::Voice => "Voice",
66            StreamKind::Measure => "Measure",
67            StreamKind::Part => "Part",
68            StreamKind::PartStaff => "PartStaff",
69            StreamKind::Score => "Score",
70            StreamKind::Opus => "Opus",
71        }
72    }
73}
74
75impl std::fmt::Display for StreamKind {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        f.write_str(self.as_str())
78    }
79}
80
81/// A musical object that can live on a timeline.
82#[derive(Clone, Debug)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub enum StreamElement {
85    /// A single pitched note.
86    Note(Note),
87    /// A chord containing one or more notes.
88    Chord(Chord),
89    /// A silent rest.
90    Rest(Rest),
91    /// A stream nested inside this one: a part in a score, a measure in a
92    /// part. Boxed, since a stream holds these by value.
93    Stream(Box<Stream>),
94    /// The key signature in force from here on.
95    KeySignature(KeySignature),
96    /// The metre in force from here on.
97    TimeSignature(TimeSignature),
98    /// The tempo in force from here on.
99    MetronomeMark(MetronomeMark),
100}
101
102impl StreamElement {
103    /// Returns the assigned duration, if present.
104    ///
105    /// The marks that only say what is in force from a point — a key, a
106    /// metre, a tempo — have none, as they take no time in music21 either.
107    pub fn duration(&self) -> Option<&Duration> {
108        match self {
109            Self::Note(note) => note.duration(),
110            Self::Chord(chord) => chord.duration(),
111            Self::Rest(rest) => Some(rest.duration()),
112            Self::Stream(_)
113            | Self::KeySignature(_)
114            | Self::TimeSignature(_)
115            | Self::MetronomeMark(_) => None,
116        }
117    }
118
119    /// Returns the duration in quarter lengths.
120    ///
121    /// A nested stream is as long as its own contents; a mark takes no time;
122    /// anything else with no duration of its own defaults to a quarter, as
123    /// music21's does.
124    pub fn quarter_length(&self) -> FloatType {
125        match self {
126            Self::Stream(stream) => stream.end_offset(),
127            Self::KeySignature(_) | Self::TimeSignature(_) | Self::MetronomeMark(_) => 0.0,
128            _ => self
129                .duration()
130                .map(Duration::quarter_length)
131                .unwrap_or_else(|| Duration::default().quarter_length()),
132        }
133    }
134
135    /// Returns all pitches contained by this element, a nested stream's
136    /// included.
137    pub fn pitches(&self) -> Vec<Pitch> {
138        match self {
139            Self::Note(note) => vec![note.pitch().clone()],
140            Self::Chord(chord) => chord.pitches(),
141            Self::Stream(stream) => stream.pitches(),
142            Self::Rest(_)
143            | Self::KeySignature(_)
144            | Self::TimeSignature(_)
145            | Self::MetronomeMark(_) => Vec::new(),
146        }
147    }
148
149    /// The stream this element is, when it is one.
150    pub fn as_stream(&self) -> Option<&Stream> {
151        match self {
152            Self::Stream(stream) => Some(stream),
153            _ => None,
154        }
155    }
156
157    /// Whether this element sounds — music21's `.notes`, which is notes and
158    /// chords but not rests.
159    pub fn is_note_or_chord(&self) -> bool {
160        matches!(self, Self::Note(_) | Self::Chord(_))
161    }
162
163    /// Returns a transposed copy.
164    ///
165    /// A key signature moves with the music, as music21's does; a metre and
166    /// a tempo do not depend on pitch and are left alone.
167    pub fn transpose(&self, interval: &Interval) -> Result<Self> {
168        match self {
169            Self::Note(note) => {
170                let mut out = note.clone();
171                out.set_pitch(interval.transpose_pitch(note.pitch())?);
172                Ok(Self::Note(out))
173            }
174            Self::Chord(chord) => Ok(Self::Chord(chord.transpose(interval)?)),
175            Self::Rest(rest) => Ok(Self::Rest(rest.clone())),
176            Self::Stream(stream) => Ok(Self::Stream(Box::new(stream.transpose(interval)?))),
177            Self::KeySignature(key) => Ok(Self::KeySignature(key.transpose(interval)?)),
178            Self::TimeSignature(meter) => Ok(Self::TimeSignature(meter.clone())),
179            Self::MetronomeMark(mark) => Ok(Self::MetronomeMark(mark.clone())),
180        }
181    }
182}
183
184impl From<Note> for StreamElement {
185    fn from(value: Note) -> Self {
186        Self::Note(value)
187    }
188}
189
190impl From<Chord> for StreamElement {
191    fn from(value: Chord) -> Self {
192        Self::Chord(value)
193    }
194}
195
196impl From<Rest> for StreamElement {
197    fn from(value: Rest) -> Self {
198        Self::Rest(value)
199    }
200}
201
202impl From<Stream> for StreamElement {
203    fn from(value: Stream) -> Self {
204        Self::Stream(Box::new(value))
205    }
206}
207
208impl From<KeySignature> for StreamElement {
209    fn from(value: KeySignature) -> Self {
210        Self::KeySignature(value)
211    }
212}
213
214impl From<TimeSignature> for StreamElement {
215    fn from(value: TimeSignature) -> Self {
216        Self::TimeSignature(value)
217    }
218}
219
220impl From<MetronomeMark> for StreamElement {
221    fn from(value: MetronomeMark) -> Self {
222        Self::MetronomeMark(value)
223    }
224}
225
226/// A timestamped stream item.
227#[derive(Clone, Debug)]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229pub struct StreamEvent {
230    offset: FloatType,
231    element: StreamElement,
232}
233
234impl StreamEvent {
235    /// Creates an event at an offset measured in quarter lengths.
236    pub fn new(offset: FloatType, element: impl Into<StreamElement>) -> Self {
237        Self {
238            offset,
239            element: element.into(),
240        }
241    }
242
243    /// Returns the offset in quarter lengths.
244    pub fn offset(&self) -> FloatType {
245        self.offset
246    }
247
248    /// Returns the stream element.
249    pub fn element_mut(&mut self) -> &mut StreamElement {
250        &mut self.element
251    }
252
253    /// The element itself.
254    pub fn element(&self) -> &StreamElement {
255        &self.element
256    }
257
258    /// The offset just past this element.
259    pub fn end_offset(&self) -> FloatType {
260        self.offset + self.element.quarter_length()
261    }
262}
263
264/// An ordered stream of music, which may hold other streams.
265#[derive(Clone, Debug, Default)]
266#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
267#[must_use]
268pub struct Stream {
269    #[cfg_attr(feature = "serde", serde(default))]
270    kind: StreamKind,
271    events: Vec<StreamEvent>,
272}
273
274impl Stream {
275    /// Creates an empty stream.
276    pub fn new() -> Self {
277        Self::default()
278    }
279
280    /// Creates an empty stream standing for one of music21's subclasses.
281    pub fn with_kind(kind: StreamKind) -> Self {
282        Self {
283            kind,
284            events: Vec::new(),
285        }
286    }
287
288    /// Which of music21's `Stream` subclasses this stands for.
289    pub fn kind(&self) -> StreamKind {
290        self.kind
291    }
292
293    /// Sets which subclass this stands for.
294    pub fn set_kind(&mut self, kind: StreamKind) {
295        self.kind = kind;
296    }
297
298    /// Creates a stream from events, sorted by offset.
299    pub fn from_events(events: impl IntoIterator<Item = StreamEvent>) -> Self {
300        let mut stream = Self {
301            kind: StreamKind::Stream,
302            events: events.into_iter().collect(),
303        };
304        stream.sort_events();
305        stream
306    }
307
308    /// Inserts an element at a quarter-length offset.
309    pub fn insert(&mut self, offset: FloatType, element: impl Into<StreamElement>) {
310        self.events.push(StreamEvent::new(offset, element));
311        self.sort_events();
312    }
313
314    /// Appends an element after the current end of the stream.
315    pub fn push(&mut self, element: impl Into<StreamElement>) {
316        let element = element.into();
317        let offset = self.end_offset();
318        self.events.push(StreamEvent::new(offset, element));
319    }
320
321    /// Returns immutable events in offset order.
322    pub fn events_mut(&mut self) -> &mut [StreamEvent] {
323        &mut self.events
324    }
325
326    /// The events in offset order.
327    pub fn events(&self) -> &[StreamEvent] {
328        &self.events
329    }
330
331    /// Iterates over events in offset order.
332    pub fn iter(&self) -> impl Iterator<Item = &StreamEvent> {
333        self.events.iter()
334    }
335
336    /// How many events this stream holds directly, not counting what any
337    /// nested stream holds.
338    pub fn len(&self) -> usize {
339        self.events.len()
340    }
341
342    /// Whether this stream holds nothing at all.
343    pub fn is_empty(&self) -> bool {
344        self.events.is_empty()
345    }
346
347    /// One timeline with the nesting dissolved: music21's `flatten`.
348    ///
349    /// Each nested stream's offset is added to its contents', and the
350    /// streams themselves are gone; everything else comes through in offset
351    /// order.
352    pub fn flatten(&self) -> Self {
353        let mut flattened = Self::with_kind(self.kind);
354        self.flatten_into(0.0, &mut flattened.events);
355        flattened.sort_events();
356        flattened
357    }
358
359    fn flatten_into(&self, base: FloatType, out: &mut Vec<StreamEvent>) {
360        for event in &self.events {
361            let offset = base + event.offset;
362            match &event.element {
363                StreamElement::Stream(stream) => stream.flatten_into(offset, out),
364                element => out.push(StreamEvent::new(offset, element.clone())),
365            }
366        }
367    }
368
369    /// Every element at its offset from this stream's start, nested streams
370    /// themselves included: music21's `recurse`.
371    pub fn recurse(&self) -> Vec<(FloatType, &StreamElement)> {
372        let mut out = Vec::new();
373        self.recurse_into(0.0, &mut out);
374        out
375    }
376
377    fn recurse_into<'a>(&'a self, base: FloatType, out: &mut Vec<(FloatType, &'a StreamElement)>) {
378        for event in &self.events {
379            let offset = base + event.offset;
380            out.push((offset, &event.element));
381            if let StreamElement::Stream(stream) = &event.element {
382                stream.recurse_into(offset, out);
383            }
384        }
385    }
386
387    /// The streams of one kind held directly by this one: music21's `.parts`
388    /// on a score, `.measures` on a part, `.voices` on a measure.
389    pub fn streams_of_kind(&self, kind: StreamKind) -> Vec<&Stream> {
390        self.events
391            .iter()
392            .filter_map(|event| event.element.as_stream())
393            .filter(|stream| stream.kind == kind)
394            .collect()
395    }
396
397    /// The parts this stream holds.
398    pub fn parts(&self) -> Vec<&Stream> {
399        self.streams_of_kind(StreamKind::Part)
400    }
401
402    /// The measures this stream holds.
403    pub fn measures(&self) -> Vec<&Stream> {
404        self.streams_of_kind(StreamKind::Measure)
405    }
406
407    /// The voices this stream holds.
408    pub fn voices(&self) -> Vec<&Stream> {
409        self.streams_of_kind(StreamKind::Voice)
410    }
411
412    /// Every note and chord on the flattened timeline: music21's
413    /// `flatten().notes`, which leaves rests out.
414    pub fn notes(&self) -> Vec<(FloatType, StreamElement)> {
415        self.flatten()
416            .events
417            .into_iter()
418            .filter(|event| event.element.is_note_or_chord())
419            .map(|event| (event.offset, event.element))
420            .collect()
421    }
422
423    /// Returns the maximum event end offset, a nested stream's own length
424    /// included.
425    pub fn end_offset(&self) -> FloatType {
426        self.events
427            .iter()
428            .map(StreamEvent::end_offset)
429            .fold(0.0, FloatType::max)
430    }
431
432    /// Returns all pitches in timeline order, from nested streams too.
433    pub fn pitches(&self) -> Vec<Pitch> {
434        self.flatten()
435            .events
436            .iter()
437            .flat_map(|event| event.element.pitches())
438            .collect()
439    }
440
441    /// The key signature in force at an offset: the last one written at or
442    /// before it, anywhere in the nesting.
443    ///
444    /// This is what music21 answers with `getContextByClass(KeySignature)`,
445    /// found by looking back along the flattened timeline rather than by
446    /// walking the sites an object belongs to.
447    pub fn key_signature_at(&self, offset: FloatType) -> Option<KeySignature> {
448        self.in_force_at(offset, |element| match element {
449            StreamElement::KeySignature(key) => Some(key.clone()),
450            _ => None,
451        })
452    }
453
454    /// The metre in force at an offset, found the same way.
455    pub fn time_signature_at(&self, offset: FloatType) -> Option<TimeSignature> {
456        self.in_force_at(offset, |element| match element {
457            StreamElement::TimeSignature(meter) => Some(meter.clone()),
458            _ => None,
459        })
460    }
461
462    /// The tempo in force at an offset, found the same way.
463    pub fn metronome_mark_at(&self, offset: FloatType) -> Option<MetronomeMark> {
464        self.in_force_at(offset, |element| match element {
465            StreamElement::MetronomeMark(mark) => Some(mark.clone()),
466            _ => None,
467        })
468    }
469
470    /// The last thing of one sort written at or before an offset.
471    fn in_force_at<T>(
472        &self,
473        offset: FloatType,
474        read: impl Fn(&StreamElement) -> Option<T>,
475    ) -> Option<T> {
476        self.flatten()
477            .events
478            .iter()
479            .take_while(|event| event.offset <= offset)
480            .filter_map(|event| read(&event.element))
481            .last()
482    }
483
484    /// Returns a transposed copy, nesting and all.
485    pub fn transpose(&self, interval: &Interval) -> Result<Self> {
486        let events = self
487            .events
488            .iter()
489            .map(|event| {
490                Ok(StreamEvent::new(
491                    event.offset,
492                    event.element.transpose(interval)?,
493                ))
494            })
495            .collect::<Result<Vec<_>>>()?;
496        let mut out = Self::with_kind(self.kind);
497        out.events = events;
498        out.sort_events();
499        Ok(out)
500    }
501
502    fn sort_events(&mut self) {
503        self.events.sort_by(|left, right| {
504            left.offset
505                .partial_cmp(&right.offset)
506                .unwrap_or(std::cmp::Ordering::Equal)
507        });
508    }
509}
510
511impl<'a> IntoIterator for &'a Stream {
512    type Item = &'a StreamEvent;
513    type IntoIter = std::slice::Iter<'a, StreamEvent>;
514
515    fn into_iter(self) -> Self::IntoIter {
516        self.events.iter()
517    }
518}
519
520#[cfg(test)]
521mod tests {
522    #[test]
523    fn a_stream_says_what_kind_it_is_and_walks_its_events() {
524        use super::{Stream, StreamElement, StreamEvent, StreamKind};
525        use crate::note::Note;
526        use crate::pitch::Pitch;
527        use crate::tempo::MetronomeMark;
528
529        assert_eq!(StreamKind::Voice.as_str(), "Voice");
530        assert_eq!(StreamKind::Voice.to_string(), "Voice");
531        let mut stream = Stream::new();
532        assert!(stream.is_empty());
533        stream.set_kind(StreamKind::Voice);
534        assert_eq!(stream.kind(), StreamKind::Voice);
535        assert!(matches!(
536            StreamElement::from(MetronomeMark::new(120.0)),
537            StreamElement::MetronomeMark(_)
538        ));
539
540        let note = Note::from_pitch(Pitch::from_name("C4").unwrap());
541        let rebuilt = Stream::from_events([StreamEvent::new(0.0, note)]);
542        assert_eq!(rebuilt.iter().count(), 1);
543        assert!(!rebuilt.is_empty());
544        assert!(rebuilt.voices().is_empty());
545        let mut outer = Stream::new();
546        outer.push(stream);
547        assert_eq!(outer.voices().len(), 1);
548    }
549
550    use super::*;
551
552    #[test]
553    fn a_stream_iterates_by_reference() {
554        let mut stream = Stream::new();
555        stream.push(Note::from_name("C4").unwrap());
556        stream.push(Note::from_name("E4").unwrap());
557
558        let offsets: Vec<FloatType> = (&stream).into_iter().map(StreamEvent::offset).collect();
559        assert_eq!(offsets, vec![0.0, 1.0]);
560        assert_eq!((&stream).into_iter().count(), stream.len());
561    }
562
563    #[test]
564    fn stream_push_uses_durations() {
565        let mut stream = Stream::new();
566        stream.push(
567            Note::from_name("C4")
568                .unwrap()
569                .with_duration(Duration::half()),
570        );
571        stream.push(Rest::from_quarter_length(0.5).unwrap());
572        assert_eq!(stream.events()[0].offset(), 0.0);
573        assert_eq!(stream.events()[1].offset(), 2.0);
574        assert_eq!(stream.end_offset(), 2.5);
575    }
576
577    #[test]
578    fn stream_transposes_notes_and_chords() {
579        let mut stream = Stream::new();
580        stream.push(Note::from_name("C4").unwrap());
581        stream.push(Chord::new("E4 G4").unwrap());
582        let out = stream
583            .transpose(&Interval::from_name("M2").unwrap())
584            .unwrap();
585        let names = out
586            .pitches()
587            .iter()
588            .map(Pitch::name_with_octave)
589            .collect::<Vec<_>>();
590        assert_eq!(names, vec!["D4", "F#4", "A4"]);
591    }
592
593    /// A score of one part of two measures, which is the shape music21 puts
594    /// almost everything in.
595    fn two_measure_score() -> Stream {
596        let mut first = Stream::with_kind(StreamKind::Measure);
597        first.insert(0.0, KeySignature::new(2));
598        first.insert(0.0, TimeSignature::new(4, 4).unwrap());
599        first.push(Note::from_name("D4").unwrap());
600        first.push(Note::from_name("E4").unwrap());
601        first.push(Note::from_name("F#4").unwrap());
602        first.push(Note::from_name("G4").unwrap());
603
604        let mut second = Stream::with_kind(StreamKind::Measure);
605        second.insert(0.0, KeySignature::new(-1));
606        second.push(Note::from_name("A4").unwrap());
607        second.push(Note::from_name("B-4").unwrap());
608
609        let mut part = Stream::with_kind(StreamKind::Part);
610        part.push(first);
611        part.push(second);
612
613        let mut score = Stream::with_kind(StreamKind::Score);
614        score.push(part);
615        score
616    }
617
618    #[test]
619    fn a_nested_stream_flattens_onto_one_timeline() {
620        let score = two_measure_score();
621        assert_eq!(score.kind(), StreamKind::Score);
622        assert_eq!(score.len(), 1, "a score holds its one part, not its notes");
623        assert_eq!(score.parts().len(), 1);
624        assert_eq!(score.parts()[0].measures().len(), 2);
625
626        // the second measure starts a bar in, so its notes do too
627        let notes = score.notes();
628        let offsets: Vec<FloatType> = notes.iter().map(|(offset, _)| *offset).collect();
629        assert_eq!(offsets, vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
630        assert_eq!(score.end_offset(), 6.0);
631
632        let names: Vec<String> = score
633            .pitches()
634            .iter()
635            .map(Pitch::name_with_octave)
636            .collect();
637        assert_eq!(names, ["D4", "E4", "F#4", "G4", "A4", "B-4"]);
638
639        // recurse sees the streams themselves; flatten does not
640        assert_eq!(score.recurse().len(), 1 + 2 + 6 + 3);
641        assert_eq!(score.flatten().len(), 6 + 3);
642    }
643
644    #[test]
645    fn the_key_in_force_is_the_last_one_written_before_it() {
646        let score = two_measure_score();
647        assert_eq!(score.key_signature_at(0.0).unwrap().sharps(), Some(2));
648        assert_eq!(score.key_signature_at(3.5).unwrap().sharps(), Some(2));
649        // the second measure changes it
650        assert_eq!(score.key_signature_at(4.0).unwrap().sharps(), Some(-1));
651        assert_eq!(score.key_signature_at(100.0).unwrap().sharps(), Some(-1));
652        assert_eq!(
653            score.time_signature_at(5.0).unwrap().ratio_string(),
654            "4/4",
655            "a metre stays in force across the bar that follows it"
656        );
657        assert!(score.metronome_mark_at(0.0).is_none());
658
659        // nothing is in force before the first mark
660        let mut late = Stream::new();
661        late.insert(2.0, KeySignature::new(3));
662        assert!(late.key_signature_at(1.0).is_none());
663        assert_eq!(late.key_signature_at(2.0).unwrap().sharps(), Some(3));
664    }
665
666    #[test]
667    fn transposing_a_score_moves_its_key_signatures_too() {
668        let score = two_measure_score();
669        let up = score
670            .transpose(&Interval::from_name("M2").unwrap())
671            .unwrap();
672        assert_eq!(up.key_signature_at(0.0).unwrap().sharps(), Some(4));
673        assert_eq!(up.key_signature_at(4.0).unwrap().sharps(), Some(1));
674        let names: Vec<String> = up.pitches().iter().map(Pitch::name_with_octave).collect();
675        assert_eq!(names, ["E4", "F#4", "G#4", "A4", "B4", "C5"]);
676        assert_eq!(up.kind(), StreamKind::Score);
677    }
678}