Skip to main content

music21/
tree.rs

1//! A stream's elements as spans of time, and the moments where what sounds
2//! changes: music21's `tree` package, its timespan trees and
3//! verticalities.
4//!
5//! [`as_timespans`] reads every element of a stream, nested ones included,
6//! as an [`ElementTimespan`] from where it starts to where it ends, with the
7//! streams it stands in; a [`TimespanTree`] answers which start, stop or
8//! sound at an offset; and a [`Verticality`] is one such offset, with the
9//! pitches sounding there.
10//!
11//! ```
12//! use music21::tinynotation::from_tiny_notation;
13//! use music21::tree::{Flatten, as_timespans};
14//!
15//! let line = from_tiny_notation("4/4 c2 e4 g")?;
16//! let tree = as_timespans(&line, Flatten::Flat, None);
17//! let third = tree.verticality_at(3.0);
18//! assert_eq!(third.start_timespans().len(), 1);
19//! assert_eq!(tree.maximum_overlap(), Some(3));
20//! # Ok::<(), music21::Error>(())
21//! ```
22
23use crate::{
24    chord::Chord,
25    defaults::{FloatType, IntegerType},
26    error::{Error, Result},
27    makenotation::op_frac,
28    pitch::Pitch,
29    stream::{Stream, StreamElement, StreamKind},
30    voiceleading::VoiceLeadingQuartet,
31};
32
33/// How [`as_timespans`] reads the streams a stream holds.
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
35pub enum Flatten {
36    /// Their elements only: music21's `flatten=True`.
37    #[default]
38    Flat,
39    /// Their elements and the streams themselves: music21's
40    /// `flatten='semiFlat'`.
41    SemiFlat,
42}
43
44/// One element of a stream, from where it starts to where it ends:
45/// music21's `ElementTimespan`, and its `PitchedTimespan` where the element
46/// is a note-like one or a stream.
47#[derive(Clone, Debug)]
48pub struct ElementTimespan<'a> {
49    element: &'a StreamElement,
50    offset: FloatType,
51    end_time: FloatType,
52    parent_offset: FloatType,
53    parent_end_time: FloatType,
54    parentage: Vec<&'a Stream>,
55    measure: Option<IntegerType>,
56    pitched: bool,
57}
58
59impl<'a> ElementTimespan<'a> {
60    /// The element.
61    pub fn element(&self) -> &'a StreamElement {
62        self.element
63    }
64
65    /// Where it starts in the stream read.
66    pub fn offset(&self) -> FloatType {
67        self.offset
68    }
69
70    /// Where it ends: music21's `endTime`.
71    pub fn end_time(&self) -> FloatType {
72        self.end_time
73    }
74
75    /// How long it lasts.
76    pub fn quarter_length(&self) -> FloatType {
77        self.end_time - self.offset
78    }
79
80    /// Where the stream holding it starts: music21's `parentOffset`.
81    pub fn parent_offset(&self) -> FloatType {
82        self.parent_offset
83    }
84
85    /// Where the stream holding it ends: music21's `parentEndTime`.
86    pub fn parent_end_time(&self) -> FloatType {
87        self.parent_end_time
88    }
89
90    /// The streams it stands in, the one holding it first and the stream
91    /// read last: music21's `parentage`.
92    pub fn parentage(&self) -> &[&'a Stream] {
93        &self.parentage
94    }
95
96    /// The part it is in, if any: music21's `part`.
97    pub fn part(&self) -> Option<&'a Stream> {
98        self.parentage
99            .iter()
100            .copied()
101            .find(|stream| matches!(stream.kind(), StreamKind::Part | StreamKind::PartStaff))
102    }
103
104    /// The number of the measure it is in, if it is in one: music21's
105    /// `measureNumber`.
106    pub fn measure_number(&self) -> Option<IntegerType> {
107        self.measure
108    }
109
110    /// Whether it is music21's `PitchedTimespan`: the element a note, chord,
111    /// chord symbol, unpitched stroke, percussion chord or stream.
112    pub fn is_pitched(&self) -> bool {
113        self.pitched
114    }
115
116    /// The pitches the element sounds, none for one that is not pitched:
117    /// music21's `pitches`.
118    pub fn pitches(&self) -> Vec<Pitch> {
119        if !self.pitched {
120            return Vec::new();
121        }
122        match self.element {
123            StreamElement::ChordSymbol(symbol) => symbol.pitches().unwrap_or_default(),
124            element => element.pitches(),
125        }
126    }
127}
128
129impl ElementTimespan<'_> {
130    /// The same element over another span: music21's `new`.
131    fn spanning(&self, offset: FloatType, end_time: FloatType) -> Self {
132        Self {
133            offset,
134            end_time,
135            ..self.clone()
136        }
137    }
138}
139
140/// Whether music21 makes a `PitchedTimespan` of an element: a `NotRest` or
141/// a stream.
142fn is_pitched(element: &StreamElement) -> bool {
143    matches!(
144        element,
145        StreamElement::Note(_)
146            | StreamElement::Chord(_)
147            | StreamElement::ChordSymbol(_)
148            | StreamElement::Unpitched(_)
149            | StreamElement::PercussionChord(_)
150            | StreamElement::Stream(_)
151    )
152}
153
154/// The timespans of a stream in time, each answer in music21's order: by
155/// where they start, then where they end, then as the stream holds them:
156/// music21's `TimespanTree`.
157#[derive(Clone, Debug)]
158pub struct TimespanTree<'a> {
159    source: &'a Stream,
160    timespans: Vec<ElementTimespan<'a>>,
161}
162
163/// Every element of a stream, nested ones included, as a timespan:
164/// music21's `asTimespans`. `filter` keeps only the elements it accepts, as
165/// music21's `classList` does; the streams are walked into either way.
166pub fn as_timespans<'a>(
167    stream: &'a Stream,
168    flatten: Flatten,
169    filter: Option<fn(&StreamElement) -> bool>,
170) -> TimespanTree<'a> {
171    let mut timespans = Vec::new();
172    let measure = (stream.kind() == StreamKind::Measure).then(|| stream.number());
173    walk(
174        stream,
175        0.0,
176        &mut vec![stream],
177        measure,
178        flatten,
179        filter,
180        &mut timespans,
181    );
182    timespans.sort_by(|a: &ElementTimespan<'_>, b: &ElementTimespan<'_>| {
183        a.offset
184            .total_cmp(&b.offset)
185            .then_with(|| a.end_time.total_cmp(&b.end_time))
186    });
187    TimespanTree {
188        source: stream,
189        timespans,
190    }
191}
192
193fn walk<'a>(
194    stream: &'a Stream,
195    initial: FloatType,
196    parentage: &mut Vec<&'a Stream>,
197    measure: Option<IntegerType>,
198    flatten: Flatten,
199    filter: Option<fn(&StreamElement) -> bool>,
200    out: &mut Vec<ElementTimespan<'a>>,
201) {
202    let parent_end_time = op_frac(initial + stream.end_offset());
203    let reversed: Vec<&'a Stream> = parentage.iter().rev().copied().collect();
204    for event in stream.events() {
205        let offset = op_frac(event.offset() + initial);
206        let element = event.element();
207        // A measure's own number is its measure number, as music21 reads it.
208        let mut own_measure = measure;
209        if let StreamElement::Stream(inner) = element {
210            parentage.push(inner);
211            let inner_measure = if inner.kind() == StreamKind::Measure {
212                Some(inner.number())
213            } else {
214                measure
215            };
216            walk(
217                inner,
218                offset,
219                parentage,
220                inner_measure,
221                flatten,
222                filter,
223                out,
224            );
225            parentage.pop();
226            own_measure = inner_measure;
227            if flatten == Flatten::Flat {
228                continue;
229            }
230        }
231        if filter.is_some_and(|accepts| !accepts(element)) {
232            continue;
233        }
234        out.push(ElementTimespan {
235            element,
236            offset,
237            end_time: op_frac(offset + element.quarter_length()),
238            parent_offset: initial,
239            parent_end_time,
240            parentage: reversed.clone(),
241            measure: own_measure,
242            pitched: is_pitched(element),
243        });
244    }
245}
246
247/// music21's `Pitch.__le__`: lower in pitch space, or the same pitch
248/// spelled the same.
249fn at_most(a: &Pitch, b: &Pitch) -> bool {
250    a.ps() < b.ps() || a == b
251}
252
253impl<'a> TimespanTree<'a> {
254    /// Every timespan, in order.
255    pub fn timespans(&self) -> &[ElementTimespan<'a>] {
256        &self.timespans
257    }
258
259    /// How many timespans there are.
260    pub fn len(&self) -> usize {
261        self.timespans.len()
262    }
263
264    /// Whether there are none.
265    pub fn is_empty(&self) -> bool {
266        self.timespans.is_empty()
267    }
268
269    /// Where the earliest timespan starts: music21's `lowestPosition`.
270    pub fn lowest_position(&self) -> Option<FloatType> {
271        self.timespans.first().map(|span| span.offset)
272    }
273
274    /// Where the latest starts: music21's `highestPosition`.
275    pub fn highest_position(&self) -> Option<FloatType> {
276        self.timespans.last().map(|span| span.offset)
277    }
278
279    /// Where the last to end ends: music21's `endTime`.
280    pub fn end_time(&self) -> Option<FloatType> {
281        self.timespans
282            .iter()
283            .map(|span| span.end_time)
284            .reduce(FloatType::max)
285    }
286
287    /// Each offset a timespan starts at, once, in order: music21's
288    /// `allOffsets`.
289    pub fn all_offsets(&self) -> Vec<FloatType> {
290        let mut offsets: Vec<FloatType> = self.timespans.iter().map(|span| span.offset).collect();
291        offsets.dedup();
292        offsets
293    }
294
295    /// Each offset a timespan starts or ends at, once, in order: music21's
296    /// `allTimePoints`.
297    pub fn all_time_points(&self) -> Vec<FloatType> {
298        let mut points: Vec<FloatType> = self
299            .timespans
300            .iter()
301            .flat_map(|span| [span.offset, span.end_time])
302            .collect();
303        points.sort_by(FloatType::total_cmp);
304        points.dedup();
305        points
306    }
307
308    /// The first offset after this one a timespan starts at: music21's
309    /// `getPositionAfter`.
310    pub fn position_after(&self, offset: FloatType) -> Option<FloatType> {
311        self.timespans
312            .iter()
313            .map(|span| span.offset)
314            .find(|start| *start > offset)
315    }
316
317    /// The last offset before this one a timespan starts at: music21's
318    /// `getPositionBefore`.
319    pub fn position_before(&self, offset: FloatType) -> Option<FloatType> {
320        self.timespans
321            .iter()
322            .rev()
323            .map(|span| span.offset)
324            .find(|start| *start < offset)
325    }
326
327    /// The timespans starting at an offset: music21's `elementsStartingAt`.
328    pub fn starting_at(&self, offset: FloatType) -> Vec<&ElementTimespan<'a>> {
329        self.timespans
330            .iter()
331            .filter(|span| span.offset == offset)
332            .collect()
333    }
334
335    /// The timespans ending at an offset: music21's `elementsStoppingAt`.
336    pub fn stopping_at(&self, offset: FloatType) -> Vec<&ElementTimespan<'a>> {
337        self.timespans
338            .iter()
339            .filter(|span| span.end_time == offset)
340            .collect()
341    }
342
343    /// The timespans sounding across an offset, started before it and
344    /// ending after: music21's `elementsOverlappingOffset`.
345    pub fn overlapping(&self, offset: FloatType) -> Vec<&ElementTimespan<'a>> {
346        self.timespans
347            .iter()
348            .filter(|span| span.offset < offset && offset < span.end_time)
349            .collect()
350    }
351
352    /// The offsets a timespan starts at, or with `include_stop_points` also
353    /// ends at, where another sounds across: music21's
354    /// `overlapTimePoints`.
355    pub fn overlap_time_points(&self, include_stop_points: bool) -> Vec<FloatType> {
356        let points = if include_stop_points {
357            self.all_time_points()
358        } else {
359            self.all_offsets()
360        };
361        points
362            .into_iter()
363            .filter(|point| !self.overlapping(*point).is_empty())
364            .collect()
365    }
366
367    /// What starts, stops and sounds across an offset: music21's
368    /// `getVerticalityAt`.
369    pub fn verticality_at(&self, offset: FloatType) -> Verticality<'_, 'a> {
370        Verticality {
371            tree: self,
372            offset,
373            start_timespans: self.starting_at(offset),
374            overlap_timespans: self.overlapping(offset),
375            stop_timespans: self.stopping_at(offset),
376        }
377    }
378
379    /// The verticality at an offset where something starts there, or the
380    /// one before: music21's `getVerticalityAtOrBefore`.
381    pub fn verticality_at_or_before(&self, offset: FloatType) -> Option<Verticality<'_, 'a>> {
382        let verticality = self.verticality_at(offset);
383        if verticality.start_timespans.is_empty() {
384            verticality.previous()
385        } else {
386            Some(verticality)
387        }
388    }
389
390    /// The verticality at each offset a timespan starts at, in order:
391    /// music21's `iterateVerticalities`.
392    pub fn verticalities(&self) -> Vec<Verticality<'_, 'a>> {
393        self.all_offsets()
394            .into_iter()
395            .map(|offset| self.verticality_at(offset))
396            .collect()
397    }
398
399    /// The most timespans starting at or sounding across any one offset a
400    /// timespan starts at, nothing for an empty tree: music21's
401    /// `maximumOverlap`.
402    pub fn maximum_overlap(&self) -> Option<usize> {
403        self.verticalities()
404            .iter()
405            .map(|verticality| {
406                verticality.start_timespans.len() + verticality.overlap_timespans.len()
407            })
408            .max()
409    }
410
411    /// The stream the tree was read from: music21's `source`.
412    pub fn source(&self) -> &'a Stream {
413        self.source
414    }
415
416    /// Takes out the timespan at a place in [`timespans`](Self::timespans)
417    /// and hands it back: music21's `removeTimespan`. Nothing is taken
418    /// out past the last.
419    pub fn remove_timespan(&mut self, index: usize) -> Option<ElementTimespan<'a>> {
420        (index < self.timespans.len()).then(|| self.timespans.remove(index))
421    }
422
423    /// Takes out the timespans at these places, each read as the tree
424    /// stood before any was taken out: music21's `removeTimespanList`.
425    pub fn remove_timespan_list(&mut self, indices: &[usize]) {
426        let mut index = 0;
427        self.timespans.retain(|_| {
428            let kept = !indices.contains(&index);
429            index += 1;
430            kept
431        });
432    }
433
434    /// The parts the timespans are in, each once: music21's `allParts`.
435    /// Those of the stream the tree was read from come first, in its
436    /// order, and any other after them by id, those with none last. A
437    /// timespan in no part is passed over.
438    pub fn all_parts(&self) -> Vec<&'a Stream> {
439        let mut parts: Vec<&'a Stream> = Vec::new();
440        for part in self.timespans.iter().filter_map(ElementTimespan::part) {
441            if !parts.iter().any(|known| std::ptr::eq(*known, part)) {
442                parts.push(part);
443            }
444        }
445        let order = self.source.parts();
446        parts.sort_by_key(|part| {
447            let place = order
448                .iter()
449                .position(|known| std::ptr::eq(*known, *part))
450                .unwrap_or(order.len());
451            (
452                place,
453                part.id().is_none(),
454                part.id().unwrap_or_default().to_string(),
455            )
456        });
457        parts
458    }
459
460    /// A tree for each part the timespans are in, holding that part's
461    /// timespans and read from it, in the order of
462    /// [`all_parts`](Self::all_parts): music21's `toPartwiseTimespanTrees`.
463    pub fn to_partwise_timespan_trees(&self) -> Vec<TimespanTree<'a>> {
464        self.all_parts()
465            .into_iter()
466            .map(|part| TimespanTree {
467                source: part,
468                timespans: self
469                    .timespans
470                    .iter()
471                    .filter(|span| span.part().is_some_and(|own| std::ptr::eq(own, part)))
472                    .cloned()
473                    .collect(),
474            })
475            .collect()
476    }
477
478    /// Cuts every timespan sounding across one of these offsets in two
479    /// there: music21's `splitAt`. The pieces come after what already
480    /// starts and ends where each does.
481    pub fn split_at(&mut self, offsets: &[FloatType]) {
482        for &offset in offsets {
483            let (across, mut kept): (Vec<ElementTimespan<'a>>, Vec<ElementTimespan<'a>>) =
484                std::mem::take(&mut self.timespans)
485                    .into_iter()
486                    .partition(|span| span.offset < offset && offset < span.end_time);
487            for span in across {
488                kept.push(span.spanning(span.offset, offset));
489                kept.push(span.spanning(offset, span.end_time));
490            }
491            kept.sort_by(|a, b| {
492                a.offset
493                    .total_cmp(&b.offset)
494                    .then_with(|| a.end_time.total_cmp(&b.end_time))
495            });
496            self.timespans = kept;
497        }
498    }
499
500    /// Runs of `n` verticalities, each starting one after the last, in
501    /// order or `reverse`d: music21's `iterateVerticalitiesNwise`. Where
502    /// there are fewer than `n`, or with `pad_end` at the end, a run is
503    /// filled out with an empty verticality at the tree's end.
504    ///
505    /// # Errors
506    ///
507    /// A run of no verticalities.
508    pub fn verticalities_nwise(
509        &self,
510        n: usize,
511        reverse: bool,
512        pad_end: bool,
513    ) -> Result<Vec<Vec<Verticality<'_, 'a>>>> {
514        if n == 0 {
515            return Err(Error::Tree(format!(
516                "The number of verticalities in the group must be at least one. Got {n}"
517            )));
518        }
519        let sentinel = Verticality {
520            tree: self,
521            offset: self.end_time().unwrap_or(FloatType::INFINITY),
522            start_timespans: Vec::new(),
523            overlap_timespans: Vec::new(),
524            stop_timespans: Vec::new(),
525        };
526        let mut sequence = self.verticalities();
527        if reverse {
528            sequence.reverse();
529        }
530        if pad_end {
531            sequence.extend(std::iter::repeat_n(sentinel.clone(), n - 1));
532        }
533        let mut windows: Vec<Vec<Verticality<'_, 'a>>> = if sequence.len() < n {
534            let mut window = sequence;
535            let missing = n - window.len();
536            window.extend(std::iter::repeat_n(sentinel, missing));
537            vec![window]
538        } else {
539            sequence.windows(n).map(<[_]>::to_vec).collect()
540        };
541        if reverse {
542            for window in &mut windows {
543                window.reverse();
544            }
545        }
546        Ok(windows)
547    }
548
549    /// Each run of verticalities from one whose chord is consonant to the
550    /// next that is, with at least one between: music21's
551    /// `iterateConsonanceBoundedVerticalities`.
552    ///
553    /// # Errors
554    ///
555    /// A verticality whose pitches make no chord.
556    pub fn consonance_bounded_verticalities(&self) -> Result<Vec<Vec<Verticality<'_, 'a>>>> {
557        let mut runs = Vec::new();
558        let mut buffer: Vec<Verticality<'_, 'a>> = Vec::new();
559        for verticality in self.verticalities() {
560            let consonant = verticality.to_chord()?.is_consonant();
561            if buffer.is_empty() {
562                if consonant {
563                    buffer.push(verticality);
564                }
565                continue;
566            }
567            buffer.push(verticality);
568            if consonant {
569                if buffer.len() > 2 {
570                    runs.push(buffer.clone());
571                }
572                let last = buffer.pop().expect("the verticality just pushed");
573                buffer = vec![last];
574            }
575        }
576        Ok(runs)
577    }
578
579    /// The next timespan after this one, starting at a later offset, in the
580    /// same part: music21's `findNextPitchedTimespanInSameStreamByClass`.
581    pub fn next_in_same_part(
582        &self,
583        timespan: &ElementTimespan<'a>,
584    ) -> Option<&ElementTimespan<'a>> {
585        let part = timespan.part();
586        let mut offset = timespan.offset;
587        while let Some(next) = self.position_after(offset) {
588            if let Some(found) = self
589                .starting_at(next)
590                .into_iter()
591                .find(|span| same_stream(span.part(), part))
592            {
593                return Some(found);
594            }
595            offset = next;
596        }
597        None
598    }
599
600    /// The timespan before this one, starting at an earlier offset, in the
601    /// same part: music21's `findPreviousPitchedTimespanInSameStreamByClass`.
602    pub fn previous_in_same_part(
603        &self,
604        timespan: &ElementTimespan<'a>,
605    ) -> Option<&ElementTimespan<'a>> {
606        let part = timespan.part();
607        let mut offset = timespan.offset;
608        while let Some(previous) = self.position_before(offset) {
609            if let Some(found) = self
610                .starting_at(previous)
611                .into_iter()
612                .find(|span| same_stream(span.part(), part))
613            {
614                return Some(found);
615            }
616            offset = previous;
617        }
618        None
619    }
620}
621
622/// Whether two parts are the same stream, or both are none.
623fn same_stream(a: Option<&Stream>, b: Option<&Stream>) -> bool {
624    match (a, b) {
625        (Some(a), Some(b)) => std::ptr::eq(a, b),
626        (None, None) => true,
627        _ => false,
628    }
629}
630
631/// What starts, stops and sounds across one offset of a [`TimespanTree`]:
632/// music21's `Verticality`.
633#[derive(Clone, Debug)]
634pub struct Verticality<'t, 'a> {
635    tree: &'t TimespanTree<'a>,
636    offset: FloatType,
637    start_timespans: Vec<&'t ElementTimespan<'a>>,
638    overlap_timespans: Vec<&'t ElementTimespan<'a>>,
639    stop_timespans: Vec<&'t ElementTimespan<'a>>,
640}
641
642impl<'t, 'a> Verticality<'t, 'a> {
643    /// The offset.
644    pub fn offset(&self) -> FloatType {
645        self.offset
646    }
647
648    /// The timespans starting here: music21's `startTimespans`.
649    pub fn start_timespans(&self) -> &[&'t ElementTimespan<'a>] {
650        &self.start_timespans
651    }
652
653    /// The timespans sounding across: music21's `overlapTimespans`.
654    pub fn overlap_timespans(&self) -> &[&'t ElementTimespan<'a>] {
655        &self.overlap_timespans
656    }
657
658    /// The timespans ending here: music21's `stopTimespans`.
659    pub fn stop_timespans(&self) -> &[&'t ElementTimespan<'a>] {
660        &self.stop_timespans
661    }
662
663    /// Those starting, then those sounding across: music21's
664    /// `startAndOverlapTimespans`.
665    pub fn start_and_overlap_timespans(&self) -> Vec<&'t ElementTimespan<'a>> {
666        self.start_timespans
667            .iter()
668            .chain(&self.overlap_timespans)
669            .copied()
670            .collect()
671    }
672
673    /// Each pitch sounding, each name and octave once, lowest first:
674    /// music21's `pitchSet`, ordered as its `toChord` orders it.
675    pub fn pitch_set(&self) -> Vec<Pitch> {
676        let mut seen: Vec<String> = Vec::new();
677        let mut pitches: Vec<Pitch> = Vec::new();
678        for span in self.start_and_overlap_timespans() {
679            for pitch in span.pitches() {
680                let name = pitch.name_with_octave();
681                if !seen.contains(&name) {
682                    seen.push(name);
683                    pitches.push(pitch);
684                }
685            }
686        }
687        pitches.sort_by(|a, b| a.ps().total_cmp(&b.ps()));
688        pitches
689    }
690
691    /// Each pitch class sounding, once, in order: music21's
692    /// `pitchClassSet`, by its classes.
693    pub fn pitch_class_set(&self) -> Vec<u8> {
694        let mut classes: Vec<u8> = self
695            .pitch_set()
696            .iter()
697            .map(|pitch| (pitch.ps().round_ties_even() as i64).rem_euclid(12) as u8)
698            .collect();
699        classes.sort_unstable();
700        classes.dedup();
701        classes
702    }
703
704    /// A chord of the pitches sounding, lowest first: music21's `toChord`.
705    ///
706    /// # Errors
707    ///
708    /// None in practice; a chord of no pitches is an empty chord.
709    pub fn to_chord(&self) -> Result<Chord> {
710        Chord::new(self.pitch_set().as_slice())
711    }
712
713    /// The timespan sounding the lowest pitch, the last of those sounding it
714    /// alike: music21's `bassTimespan`.
715    pub fn bass_timespan(&self) -> Option<&'t ElementTimespan<'a>> {
716        let mut lowest: Option<(Pitch, &'t ElementTimespan<'a>)> = None;
717        for span in self.start_and_overlap_timespans() {
718            let mut pitches = span.pitches();
719            if pitches.is_empty() {
720                continue;
721            }
722            pitches.sort_by(|a, b| a.ps().total_cmp(&b.ps()));
723            let pitch = pitches.swap_remove(0);
724            match &lowest {
725                None => lowest = Some((pitch, span)),
726                Some((overall, _)) if at_most(&pitch, overall) => lowest = Some((pitch, span)),
727                Some(_) => {}
728            }
729        }
730        lowest.map(|(_, span)| span)
731    }
732
733    /// The measure of the first timespan starting here: music21's
734    /// `measureNumber`.
735    pub fn measure_number(&self) -> Option<IntegerType> {
736        self.start_timespans.first().and_then(|span| span.measure)
737    }
738
739    /// Each pitched timespan starting here paired with the one before it in
740    /// its part, and, with `include_oblique`, each pitched one sounding
741    /// across paired with itself: music21's `getPairedMotion`. Without
742    /// `include_rests`, a pair is kept only where the earlier one stops
743    /// here; without `include_oblique`, a pair of the same pitches is not
744    /// kept.
745    pub fn paired_motion(
746        &self,
747        include_rests: bool,
748        include_oblique: bool,
749    ) -> Vec<(&'t ElementTimespan<'a>, &'t ElementTimespan<'a>)> {
750        let mut pairs = Vec::new();
751        for &starting in &self.start_timespans {
752            if !starting.pitched {
753                continue;
754            }
755            let Some(previous) = self.tree.previous_in_same_part(starting) else {
756                continue;
757            };
758            if !previous.pitched {
759                continue;
760            }
761            if !include_rests
762                && !self
763                    .stop_timespans
764                    .iter()
765                    .any(|stopping| std::ptr::eq(*stopping, previous))
766            {
767                continue;
768            }
769            if !include_oblique && starting.pitches() == previous.pitches() {
770                continue;
771            }
772            pairs.push((previous, starting));
773        }
774        if include_oblique {
775            for &overlap in &self.overlap_timespans {
776                if overlap.pitched {
777                    pairs.push((overlap, overlap));
778                }
779            }
780        }
781        pairs
782    }
783
784    /// Each two pairs of [`Self::paired_motion`], the earlier pair first:
785    /// music21's `getAllVoiceLeadingQuartets(returnObjects=False)`. Without
786    /// `include_no_motion`, two pairs neither of which moves are left out;
787    /// with `part_pairs`, only those whose two parts are one of the pairs of
788    /// the source's parts given, by their places among them.
789    ///
790    /// # Errors
791    ///
792    /// A part pair naming a part the source has not got.
793    pub fn voice_leading_timespans(
794        &self,
795        include_rests: bool,
796        include_oblique: bool,
797        include_no_motion: bool,
798        part_pairs: Option<&[(usize, usize)]>,
799    ) -> Result<Vec<[&'t ElementTimespan<'a>; 4]>> {
800        let pairs = self.paired_motion(include_rests, include_oblique);
801        let parts = self.tree.source.parts();
802        let mut quartets = Vec::new();
803        for (index, upper) in pairs.iter().enumerate() {
804            for lower in &pairs[index + 1..] {
805                if !include_no_motion
806                    && upper.0.pitches() == upper.1.pitches()
807                    && lower.0.pitches() == lower.1.pitches()
808                {
809                    continue;
810                }
811                if let Some(part_pairs) = part_pairs {
812                    let (top, bottom) = (upper.0.part(), lower.0.part());
813                    let is_either = |index: usize| -> Result<bool> {
814                        let part = parts
815                            .get(index)
816                            .copied()
817                            .ok_or_else(|| Error::Tree(format!("the score has no part {index}")))?;
818                        Ok(same_stream(Some(part), top) || same_stream(Some(part), bottom))
819                    };
820                    let mut appropriate = false;
821                    for (first, second) in part_pairs {
822                        if is_either(*first)? && is_either(*second)? {
823                            appropriate = true;
824                            break;
825                        }
826                    }
827                    if !appropriate {
828                        continue;
829                    }
830                }
831                quartets.push([upper.0, upper.1, lower.0, lower.1]);
832            }
833        }
834        Ok(quartets)
835    }
836
837    /// The same quartets as voice-leading quartets, where all four are
838    /// notes: music21's `getAllVoiceLeadingQuartets`.
839    ///
840    /// # Errors
841    ///
842    /// As [`Self::voice_leading_timespans`], or two pitches no interval can
843    /// be spelled between.
844    pub fn voice_leading_quartets(
845        &self,
846        include_rests: bool,
847        include_oblique: bool,
848        include_no_motion: bool,
849        part_pairs: Option<&[(usize, usize)]>,
850    ) -> Result<Vec<VoiceLeadingQuartet>> {
851        let mut quartets = Vec::new();
852        for spans in self.voice_leading_timespans(
853            include_rests,
854            include_oblique,
855            include_no_motion,
856            part_pairs,
857        )? {
858            let pitches: Vec<Pitch> = spans
859                .iter()
860                .filter_map(|span| match span.element {
861                    StreamElement::Note(note) => Some(note.pitch().clone()),
862                    _ => None,
863                })
864                .collect();
865            if let [v1n1, v1n2, v2n1, v2n2] = pitches.as_slice() {
866                quartets.push(VoiceLeadingQuartet::new(
867                    v1n1.clone(),
868                    v1n2.clone(),
869                    v2n1.clone(),
870                    v2n2.clone(),
871                )?);
872            }
873        }
874        Ok(quartets)
875    }
876
877    /// A chord of every pitch sounding here, lasting `length` or, without
878    /// one, until the next start -- a quarter where nothing follows -- or a
879    /// rest where nothing sounds: music21's `makeElement`. With
880    /// `add_ties`, a note begun before or going on after is tied; with
881    /// `remove_redundant_pitches`, a pitch two timespans sound is one note.
882    ///
883    /// # Errors
884    ///
885    /// A length no duration can have, or a chord symbol whose pitches
886    /// cannot be worked out.
887    pub fn make_element(
888        &self,
889        length: Option<FloatType>,
890        add_ties: bool,
891        remove_redundant_pitches: bool,
892    ) -> Result<StreamElement> {
893        let length = match length {
894            Some(length) => op_frac(length),
895            None => self.time_to_next_event().unwrap_or(1.0),
896        };
897        let spans: Vec<crate::stream::chordify::Span<'a>> = self
898            .start_and_overlap_timespans()
899            .into_iter()
900            .filter(|span| span.pitched)
901            .map(|span| crate::stream::chordify::Span {
902                start: span.offset,
903                end: span.end_time,
904                element: span.element,
905                part: 0,
906            })
907            .collect();
908        let options = crate::stream::ChordifyOptions {
909            add_ties,
910            remove_redundant_pitches,
911            to_sounding_pitch: false,
912        };
913        crate::stream::chordify::make_element(&spans, self.offset, length, &options)
914    }
915
916    /// The next offset a timespan starts at: music21's `nextStartOffset`.
917    pub fn next_start_offset(&self) -> Option<FloatType> {
918        self.tree.position_after(self.offset)
919    }
920
921    /// The verticality there: music21's `nextVerticality`.
922    pub fn next(&self) -> Option<Verticality<'t, 'a>> {
923        self.next_start_offset()
924            .map(|offset| self.tree.verticality_at(offset))
925    }
926
927    /// The verticality at the last offset before this one a timespan starts
928    /// at: music21's `previousVerticality`.
929    pub fn previous(&self) -> Option<Verticality<'t, 'a>> {
930        self.tree
931            .position_before(self.offset)
932            .map(|offset| self.tree.verticality_at(offset))
933    }
934
935    /// How long until the next start, or the end of the tree after the
936    /// last: music21's `timeToNextEvent`.
937    pub fn time_to_next_event(&self) -> Option<FloatType> {
938        let next = self.next_start_offset().or_else(|| self.tree.end_time())?;
939        Some(op_frac(next - self.offset))
940    }
941}
942
943/// Each part's timespans across a run of verticalities -- those sounding
944/// across or starting at the first, then those starting at the rest --
945/// part by part in the order first met: music21's
946/// `VerticalitySequence.unwrap`.
947pub fn unwrap<'t, 'a>(
948    verticalities: &[Verticality<'t, 'a>],
949) -> Vec<(Option<&'a Stream>, Horizontality<'t, 'a>)> {
950    let Some((first, rest)) = verticalities.split_first() else {
951        return Vec::new();
952    };
953    let mut parts: Vec<(Option<&'a Stream>, Vec<&'t ElementTimespan<'a>>)> = Vec::new();
954    let spans = first
955        .overlap_timespans
956        .iter()
957        .chain(&first.start_timespans)
958        .chain(
959            rest.iter()
960                .flat_map(|verticality| &verticality.start_timespans),
961        );
962    for &span in spans {
963        let part = span.part();
964        match parts
965            .iter_mut()
966            .find(|(known, _)| same_stream(*known, part))
967        {
968            Some((_, spans)) => spans.push(span),
969            None => parts.push((part, vec![span])),
970        }
971    }
972    parts
973        .into_iter()
974        .map(|(part, timespans)| (part, Horizontality { timespans }))
975        .collect()
976}
977
978/// One part's timespans one after another: music21's `Horizontality`.
979#[derive(Clone, Debug)]
980pub struct Horizontality<'t, 'a> {
981    timespans: Vec<&'t ElementTimespan<'a>>,
982}
983
984impl<'t, 'a> Horizontality<'t, 'a> {
985    /// The timespans, in order.
986    pub fn timespans(&self) -> &[&'t ElementTimespan<'a>] {
987        &self.timespans
988    }
989
990    /// The first pitch of each of the first three, where there are three
991    /// and every timespan has a pitch, as music21 asks.
992    fn first_three(&self) -> Option<[Pitch; 3]> {
993        if self.timespans.len() < 3 || self.timespans.iter().any(|span| span.pitches().is_empty()) {
994            return None;
995        }
996        let firsts: Vec<Pitch> = self
997            .timespans
998            .iter()
999            .take(3)
1000            .map(|span| span.pitches().into_iter().next())
1001            .collect::<Option<_>>()?;
1002        Some([firsts[0].clone(), firsts[1].clone(), firsts[2].clone()])
1003    }
1004
1005    /// Whether the first pitches of the first three rise all the way or
1006    /// fall all the way: music21's `hasPassingTone`.
1007    pub fn has_passing_tone(&self) -> bool {
1008        self.first_three().is_some_and(|[a, b, c]| {
1009            (a.ps() < b.ps() && b.ps() < c.ps()) || (a.ps() > b.ps() && b.ps() > c.ps())
1010        })
1011    }
1012
1013    /// Whether the first and third of the first three are the same pitch and
1014    /// the second is less than three semitones from them: music21's
1015    /// `hasNeighborTone`.
1016    pub fn has_neighbor_tone(&self) -> bool {
1017        self.first_three()
1018            .is_some_and(|[a, b, c]| a == c && (b.ps() - a.ps()).abs() < 3.0)
1019    }
1020
1021    /// Whether every timespan sounds the same pitches, written alike:
1022    /// music21's `hasNoMotion`. music21 gathers the pitches in a set, which
1023    /// tells apart pitches equal but for how their accidentals are shown or
1024    /// whether their spelling was worked out, so this does too.
1025    pub fn has_no_motion(&self) -> bool {
1026        let mut seen: Vec<Vec<String>> = Vec::new();
1027        for span in &self.timespans {
1028            let keys: Vec<String> = span.pitches().iter().map(set_key).collect();
1029            if !seen.contains(&keys) {
1030                seen.push(keys);
1031            }
1032        }
1033        seen.len() == 1
1034    }
1035}
1036
1037/// What tells two pitches apart in music21's sets of them: what its
1038/// `Pitch.__hash__` reads -- the accidental and how it is shown, the
1039/// fundamental, whether the spelling was worked out, the microtone, octave
1040/// and step.
1041fn set_key(pitch: &Pitch) -> String {
1042    pitch_set_key(pitch)
1043}
1044
1045/// What music21 hashes a pitch by, as text: a set of pitches holds two
1046/// pitches once only where these agree.
1047pub(crate) fn pitch_set_key(pitch: &Pitch) -> String {
1048    let accidental = pitch.written_accidental().map(|accidental| {
1049        format!(
1050            "{}|{:?}|{}|{}|{}|{}|{}|{}",
1051            accidental.alter(),
1052            accidental.display_status(),
1053            accidental.display_type(),
1054            accidental.modifier(),
1055            accidental.name(),
1056            accidental.display_location(),
1057            accidental.display_size(),
1058            accidental.display_style()
1059        )
1060    });
1061    format!(
1062        "{accidental:?}|{:?}|{}|{:?}|{:?}|{}",
1063        pitch.fundamental().map(pitch_set_key),
1064        pitch.spelling_is_inferred(),
1065        pitch.microtone().map(|microtone| microtone.cents()),
1066        pitch.octave(),
1067        pitch.name_with_octave()
1068    )
1069}
1070
1071#[cfg(test)]
1072mod tests {
1073    use super::*;
1074    use crate::tinynotation::from_tiny_notation;
1075
1076    #[test]
1077    fn a_line_is_read_as_timespans_and_verticalities() -> Result<()> {
1078        let line = from_tiny_notation("4/4 c2 e4 g")?;
1079        let tree = as_timespans(&line, Flatten::Flat, None);
1080        let notes: Vec<(FloatType, FloatType)> = tree
1081            .timespans()
1082            .iter()
1083            .filter(|span| matches!(span.element(), StreamElement::Note(_)))
1084            .map(|span| (span.offset(), span.end_time()))
1085            .collect();
1086        assert_eq!(notes, [(0.0, 2.0), (2.0, 3.0), (3.0, 4.0)]);
1087        let at_one = tree.verticality_at(1.0);
1088        assert!(at_one.start_timespans().is_empty());
1089        assert_eq!(at_one.overlap_timespans().len(), 1);
1090        assert_eq!(at_one.pitch_class_set(), [0]);
1091        assert_eq!(
1092            tree.verticality_at(2.0).next().map(|next| next.offset()),
1093            Some(3.0)
1094        );
1095        assert_eq!(tree.verticality_at(3.0).time_to_next_event(), Some(1.0));
1096        Ok(())
1097    }
1098
1099    /// A score of two parts read from tiny notation.
1100    fn two_parts() -> Result<Stream> {
1101        let mut score = Stream::with_kind(StreamKind::Score);
1102        for (id, line) in [("upper", "4/4 c'2 e'2"), ("lower", "4/4 c1")] {
1103            let mut part = from_tiny_notation(line)?;
1104            part.set_kind(StreamKind::Part);
1105            part.set_id(Some(id.to_string()));
1106            score.insert(0.0, part);
1107        }
1108        Ok(score)
1109    }
1110
1111    #[test]
1112    fn the_parts_come_in_the_score_order() -> Result<()> {
1113        let score = two_parts()?;
1114        let notes = as_timespans(
1115            &score,
1116            Flatten::Flat,
1117            Some(|element| matches!(element, StreamElement::Note(_))),
1118        );
1119        let ids: Vec<Option<&str>> = notes.all_parts().iter().map(|part| part.id()).collect();
1120        assert_eq!(ids, [Some("upper"), Some("lower")]);
1121
1122        let partwise = notes.to_partwise_timespan_trees();
1123        let counts: Vec<usize> = partwise.iter().map(TimespanTree::len).collect();
1124        assert_eq!(counts, [2, 1]);
1125        assert_eq!(partwise[1].source().id(), Some("lower"));
1126        assert_eq!(partwise[1].timespans()[0].end_time(), 4.0);
1127        Ok(())
1128    }
1129
1130    #[test]
1131    fn timespans_are_taken_out_by_place() -> Result<()> {
1132        let line = from_tiny_notation("4/4 c4 d e f")?;
1133        let mut tree = as_timespans(
1134            &line,
1135            Flatten::Flat,
1136            Some(|element| matches!(element, StreamElement::Note(_))),
1137        );
1138        let taken = tree.remove_timespan(1).map(|span| span.offset());
1139        assert_eq!(taken, Some(1.0));
1140        assert!(tree.remove_timespan(7).is_none());
1141        tree.remove_timespan_list(&[0, 2]);
1142        let left: Vec<FloatType> = tree
1143            .timespans()
1144            .iter()
1145            .map(ElementTimespan::offset)
1146            .collect();
1147        assert_eq!(left, [2.0]);
1148        Ok(())
1149    }
1150}