Skip to main content

music21_rs/makenotation/
score.rs

1//! What music21's `makeNotation` does to a whole score, and the pieces of it
2//! the other functions of this module do not already cover: brackets over
3//! tuplets, tuplets cut to complete one another or joined back into one
4//! value, and lengths no single value writes cut into ones that do.
5
6use crate::defaults::FloatType;
7use crate::duration::{Duration, Tuplet, TupletBracket, TupletType};
8use crate::error::Result;
9use crate::spanner::Spanner;
10use crate::stream::{Stream, StreamElement, StreamEvent, StreamKind};
11
12use super::{
13    is_general_note, is_not_rest, is_voice, make_accidentals_by, make_beams, make_measures,
14    make_ties, op_frac, split_element,
15};
16
17/// Works out what a score leaves unsaid, as music21's `makeNotation` does,
18/// changing the stream in place.
19///
20/// A stream holding parts has each part done; any other stream is done as
21/// one part. A part with no measures is first cut into them, its
22/// overlapping notes put in voices. Then, in music21's order: accidentals are
23/// decided where no accidental in the part has been decided yet, notes
24/// running past a barline are cut and tied, tuplets left incomplete by a cut
25/// are completed and ones a cut left whole are joined back, the notes are
26/// beamed where no note in the part is beamed yet, and each measure's
27/// tuplets are bracketed where none of them is yet.
28///
29/// Spanners stay on the elements they joined; where one of those is cut, on
30/// its first piece.
31///
32/// # Errors
33///
34/// A part that cannot be cut into measures, as its meters say.
35///
36/// ```
37/// use music21_rs::makenotation::make_notation;
38/// use music21_rs::{Duration, Note, Stream, StreamKind, TimeSignature};
39///
40/// let mut part = Stream::with_kind(StreamKind::Part);
41/// part.insert(0.0, TimeSignature::new(2, 4)?);
42/// for (place, name) in ["C4", "D4", "E4", "F4"].iter().enumerate() {
43///     let note = Note::from_name(name)?.with_duration(Duration::eighth());
44///     part.insert(place as f64 * 0.5, note);
45/// }
46/// part.insert(2.0, Note::from_name("G4")?.with_duration(Duration::half()));
47/// make_notation(&mut part)?;
48/// let measures = part.measures();
49/// assert_eq!(measures.len(), 2);
50/// # Ok::<(), music21_rs::Error>(())
51/// ```
52pub fn make_notation(stream: &mut Stream) -> Result<()> {
53    keeping_spanners(stream, Kept::Retained, &mut |stream| {
54        if has_parts(stream) {
55            for event in stream.events_mut() {
56                if let StreamElement::Stream(inner) = event.element_mut() {
57                    make_part_notation(inner)?;
58                }
59            }
60            Ok(())
61        } else {
62            make_part_notation(stream).map(|_| ())
63        }
64    })
65}
66
67/// What music21's makeNotation leaves undone on one part, so that its
68/// exporter may try again: whether beaming failed.
69pub(crate) struct PartNotation {
70    #[cfg_attr(not(feature = "musicxml"), allow(dead_code))]
71    pub(crate) beams_failed: bool,
72}
73
74/// Whether a stream holds parts: music21's `hasPartLikeStreams`, as a score
75/// asks it.
76fn has_parts(stream: &Stream) -> bool {
77    stream.has_part_like_streams()
78}
79
80/// music21's `Stream.makeNotation` on one part.
81pub(crate) fn make_part_notation(part: &mut Stream) -> Result<PartNotation> {
82    make_part_notation_by(part, true)
83}
84
85/// music21's `Stream.makeNotation` on one part, given its
86/// `cautionaryNotImmediateRepeat`, the part's spanners carried back on the
87/// elements they joined.
88pub(crate) fn make_part_notation_keeping_spanners(
89    part: &mut Stream,
90    cautionary_not_immediate_repeat: bool,
91) -> Result<()> {
92    keeping_spanners(part, Kept::Retained, &mut |part| {
93        make_part_notation_by(part, cautionary_not_immediate_repeat).map(|_| ())
94    })
95}
96
97/// music21's `Stream.makeNotation` on one part, given its
98/// `cautionaryNotImmediateRepeat`.
99pub(crate) fn make_part_notation_by(
100    part: &mut Stream,
101    cautionary_not_immediate_repeat: bool,
102) -> Result<PartNotation> {
103    if part.measures().is_empty() {
104        make_voices_filling_gaps(part)?;
105        *part = make_measures(part)?;
106        if part.measures().is_empty() {
107            return Err(crate::error::Error::Meter(format!(
108                "no measures found in stream with {} elements",
109                part.len()
110            )));
111        }
112    }
113    if !accidentals_made(part) {
114        make_accidentals_by(part, cautionary_not_immediate_repeat);
115    }
116    make_ties(part)?;
117    // Which measures had tuplets joined in their own line, which music21
118    // marks as wanting their brackets made again.
119    let mut consolidated: Vec<bool> = Vec::new();
120    for_each_measure(part, &mut |measure| {
121        for_each_container(measure, &mut split_elements_to_complete_tuplets)?;
122        let own = consolidate_completed_tuplets(measure);
123        for event in measure.events_mut() {
124            if let StreamElement::Stream(inner) = event.element_mut() {
125                for_each_container(inner, &mut |container| {
126                    consolidate_completed_tuplets(container);
127                    Ok(())
128                })?;
129            }
130        }
131        consolidated.push(own);
132        Ok(())
133    })?;
134    let mut beams_failed = false;
135    if !beams_made(part) && make_beams(part).is_err() {
136        beams_failed = true;
137    }
138    let mut index = 0;
139    for_each_measure(part, &mut |measure| {
140        let redo = consolidated.get(index).copied().unwrap_or(false);
141        index += 1;
142        if redo || tuplet_brackets_made(measure) != Some(true) {
143            make_tuplet_brackets(measure);
144        }
145        Ok(())
146    })?;
147    Ok(PartNotation { beams_failed })
148}
149
150/// music21's `Measure.makeNotation`, on each measure of a part: accidentals
151/// decided, a meter where the measure states none (`best_time_signature`,
152/// or `4/4` where none fits), tuplets completed and the tied ones joined,
153/// the notes beamed and each line's tuplets bracketed.
154#[cfg(feature = "musicxml")]
155pub(crate) fn make_measure_notation(part: &mut Stream) -> Result<()> {
156    make_accidentals_by(part, true);
157    for_each_measure(part, &mut |measure| {
158        if super::meter_in(measure).is_none() {
159            let meter = crate::meter::best_time_signature(measure)
160                .or_else(|_| crate::meter::TimeSignature::new(4, 4))?;
161            super::insert_sorted(measure, 0.0, StreamElement::TimeSignature(meter));
162        }
163        for_each_container(measure, &mut split_elements_to_complete_tuplets)?;
164        for_each_container(measure, &mut |container| {
165            consolidate_completed_tuplets(container);
166            Ok(())
167        })
168    })?;
169    make_beams(part)?;
170    for_each_measure(part, &mut |measure| {
171        make_tuplet_brackets(measure);
172        for event in measure.events_mut() {
173            if let StreamElement::Stream(voice) = event.element_mut()
174                && voice.kind() == StreamKind::Voice
175            {
176                make_tuplet_brackets(voice);
177            }
178        }
179        Ok(())
180    })
181}
182
183/// music21's `makeVoices` with `fillGaps`, on a stream with no measures:
184/// overlapping notes are put in voices, which are filled out with rests.
185fn make_voices_filling_gaps(stream: &mut Stream) -> Result<()> {
186    let has_voices = |stream: &Stream| stream.events().iter().any(|e| is_voice(e.element()));
187    if has_voices(stream) {
188        return Ok(());
189    }
190    super::make_voices(stream);
191    if !has_voices(stream) {
192        return Ok(());
193    }
194    // music21 fills the voices out, then takes every rest standing in the
195    // stream itself away.
196    let end = stream.end_offset();
197    let mut events = stream.events().to_vec();
198    for event in &mut events {
199        if let StreamElement::Stream(voice) = event.element_mut()
200            && voice.kind() == StreamKind::Voice
201        {
202            super::fill_line(voice, 0.0, end, false)?;
203        }
204    }
205    events.retain(|event| !matches!(event.element(), StreamElement::Rest(_)));
206    *stream = stream.with_events(super::sorted_events(events));
207    Ok(())
208}
209
210/// Calls an edit on each measure a part holds.
211pub(crate) fn for_each_measure(
212    part: &mut Stream,
213    edit: &mut dyn FnMut(&mut Stream) -> Result<()>,
214) -> Result<()> {
215    for event in part.events_mut() {
216        if let StreamElement::Stream(measure) = event.element_mut()
217            && measure.kind() == StreamKind::Measure
218        {
219            edit(measure)?;
220        }
221    }
222    Ok(())
223}
224
225/// Calls an edit on a stream and on every stream inside it, the outer one
226/// first: music21's `recurse(streamsOnly=True, includeSelf=True)`.
227fn for_each_container(
228    stream: &mut Stream,
229    edit: &mut dyn FnMut(&mut Stream) -> Result<()>,
230) -> Result<()> {
231    edit(stream)?;
232    for event in stream.events_mut() {
233        if let StreamElement::Stream(inner) = event.element_mut() {
234            for_each_container(inner, edit)?;
235        }
236    }
237    Ok(())
238}
239
240/// Whether some accidental in a stream has had its showing decided:
241/// music21's `haveAccidentalsBeenMade`.
242pub(crate) fn accidentals_made(stream: &Stream) -> bool {
243    stream.pitches().iter().any(|pitch| {
244        pitch
245            .written_accidental()
246            .is_some_and(|accidental| accidental.display_status().is_some())
247    })
248}
249
250/// Whether some note or chord in a stream is beamed: music21's
251/// `haveBeamsBeenMade`.
252pub(crate) fn beams_made(stream: &Stream) -> bool {
253    stream.recurse().iter().any(|(_, element)| match element {
254        StreamElement::Note(note) => !note.beams().is_empty(),
255        StreamElement::Chord(chord) => !chord.beams().is_empty(),
256        StreamElement::Unpitched(stroke) => !stroke.written().beams().is_empty(),
257        StreamElement::PercussionChord(chord) => !chord.written().beams().is_empty(),
258        _ => false,
259    })
260}
261
262/// Whether a stream's tuplets are bracketed: music21's
263/// `haveTupletBracketsBeenMade`. Nothing where it has no tuplets, and
264/// otherwise whether any of them says where its bracket falls.
265pub(crate) fn tuplet_brackets_made(stream: &Stream) -> Option<bool> {
266    let mut found = false;
267    for (_, element) in stream.recurse() {
268        if !is_general_note(element) {
269            continue;
270        }
271        let Some(duration) = element.duration() else {
272            continue;
273        };
274        let tuplets = duration.tuplets();
275        if let Some(first) = tuplets.first() {
276            found = true;
277            if first.tuplet_type().is_some() {
278                return Some(true);
279            }
280        }
281    }
282    found.then_some(false)
283}
284
285/// The duration an element keeps, or a quarter where it keeps none.
286fn duration_of(element: &StreamElement) -> Duration {
287    element.duration().cloned().unwrap_or_default()
288}
289
290/// Gives an element a duration, keeping whatever else it says.
291pub(crate) fn set_duration(element: &mut StreamElement, duration: Duration) {
292    match element {
293        StreamElement::Note(note) => note.set_duration(duration),
294        StreamElement::Chord(chord) => chord.set_duration(duration),
295        StreamElement::Rest(rest) => rest.set_duration(duration),
296        StreamElement::Unpitched(stroke) => stroke.written_mut().set_duration(duration),
297        StreamElement::PercussionChord(chord) => chord.written_mut().set_duration(duration),
298        StreamElement::ChordSymbol(symbol) => symbol.set_duration(duration),
299        _ => {}
300    }
301}
302
303/// Marks the first and last of each run of tuplets in a line of notes as
304/// where its bracket starts and stops: music21's `makeTupletBrackets`.
305///
306/// A line is a measure's own notes, or one voice's; grace notes are passed
307/// over. A run ends when the notes of a tuplet have filled its length, or
308/// where the next note is in no tuplet. A tuplet with none either side of it
309/// is bracketed alone, and drawn with no bracket. A note written in more
310/// than one tuplet at once is taken as in none.
311pub fn make_tuplet_brackets(line: &mut Stream) {
312    let places: Vec<usize> = line
313        .events()
314        .iter()
315        .enumerate()
316        .filter(|(_, event)| is_general_note(event.element()))
317        .filter(|(_, event)| !event.element().duration().is_some_and(Duration::is_grace))
318        .map(|(place, _)| place)
319        .collect();
320    let tuplets: Vec<Option<Tuplet>> = places
321        .iter()
322        .map(|place| {
323            let tuplets = duration_of(line.events()[*place].element()).tuplets();
324            match tuplets.as_slice() {
325                [only] => Some(*only),
326                _ => None,
327            }
328        })
329        .collect();
330
331    let mut count: FloatType = 0.0;
332    let mut target: Option<FloatType> = None;
333    let mut previous_was_tuplet = false;
334    for (index, place) in places.iter().enumerate() {
335        let Some(tuplet) = tuplets[index] else {
336            previous_was_tuplet = false;
337            continue;
338        };
339        let next = tuplets.get(index + 1).copied().flatten();
340        let element = line.events_mut()[*place].element_mut();
341        let length = element.quarter_length();
342        count = op_frac(count + length);
343        let mut marked = tuplet;
344        if !previous_was_tuplet || target.is_none() {
345            if next.is_none() {
346                marked.set_tuplet_type(Some(TupletType::StartStop));
347                marked.set_bracket(TupletBracket::None);
348                count = 0.0;
349            } else {
350                marked.set_tuplet_type(Some(TupletType::Start));
351                target = Some(tuplet.total_tuplet_length());
352            }
353        } else if next.is_none() || target.is_some_and(|target| count >= target) {
354            marked.set_tuplet_type(Some(TupletType::Stop));
355            target = None;
356            count = 0.0;
357        } else {
358            // Inside a run: whatever an earlier pass said here is unsaid.
359            marked.set_tuplet_type(None);
360        }
361        let mut duration = duration_of(element);
362        duration.set_tuplets(vec![marked]);
363        set_duration(element, duration);
364        previous_was_tuplet = true;
365    }
366}
367
368/// Whether a duration was worked out from a length rather than written as
369/// values: music21's `expressionIsInferred`. An element given no duration
370/// has the quarter every note starts with, which nothing worked out.
371fn expression_is_inferred(element: &StreamElement) -> bool {
372    element
373        .duration()
374        .is_some_and(Duration::expression_is_inferred)
375}
376
377/// music21's `opFrac`-exact comparison of two offsets.
378fn same_offset(left: FloatType, right: FloatType) -> bool {
379    (left - right).abs() < 1e-9
380}
381
382/// Cuts the note or rest after an unfinished tuplet where that finishes it:
383/// music21's `splitElementsToCompleteTuplets`, on one line.
384///
385/// Both the notes of the tuplet and the one cut must have had their lengths
386/// worked out rather than written, and the one cut must follow without a
387/// gap. The pieces are tied.
388///
389/// # Errors
390///
391/// A note the cut leaves with a length no tie of written values reaches.
392pub fn split_elements_to_complete_tuplets(line: &mut Stream) -> Result<()> {
393    // Each event, and whether it was there before any cut: only those are
394    // walked, as music21 walks a list taken before it starts.
395    let mut events: Vec<(StreamEvent, bool)> = line
396        .events()
397        .iter()
398        .map(|event| (event.clone(), true))
399        .collect();
400    let mut last: Option<Tuplet> = None;
401    let mut sum: FloatType = 0.0;
402    let mut index = 0;
403    while index < events.len() {
404        let (event, original) = &events[index];
405        if !original || !is_general_note(event.element()) {
406            index += 1;
407            continue;
408        }
409        let element = event.element();
410        let tuplets = duration_of(element).tuplets();
411        match tuplets.first() {
412            Some(first)
413                if expression_is_inferred(element) && last.is_none_or(|last| last == *first) =>
414            {
415                last = Some(*first);
416                sum = op_frac(element.quarter_length() + sum);
417            }
418            _ => {
419                last = None;
420                sum = 0.0;
421                index += 1;
422                continue;
423            }
424        }
425        let first = tuplets[0];
426        let to_complete = op_frac(first.total_tuplet_length() - sum);
427        if to_complete == 0.0 {
428            last = None;
429            sum = 0.0;
430            index += 1;
431            continue;
432        }
433        let offset = event.offset();
434        let end = op_frac(offset + element.quarter_length());
435        // The next note after this one: music21's `next`, which looks on
436        // through the line and down into the voices it holds. A note in a
437        // voice comes after this one only if it starts later, and before a
438        // note of the line itself starting where it does.
439        let own = (index + 1..events.len()).find(|at| is_general_note(events[*at].0.element()));
440        let mut nested: Option<(usize, usize, FloatType)> = None;
441        for (at, (held, _)) in events.iter().enumerate() {
442            let StreamElement::Stream(inner) = held.element() else {
443                continue;
444            };
445            let found = inner.events().iter().enumerate().find(|(_, inner_event)| {
446                is_general_note(inner_event.element())
447                    && held.offset() + inner_event.offset() > offset + 1e-9
448            });
449            if let Some((place, inner_event)) = found {
450                let starts = held.offset() + inner_event.offset();
451                if nested.is_none_or(|(_, _, best)| starts < best - 1e-9) {
452                    nested = Some((at, place, starts));
453                }
454            }
455        }
456        let nested = nested.filter(|(_, _, starts)| {
457            own.is_none_or(|own| *starts <= events[own].0.offset() + 1e-9)
458        });
459        if let Some((voice_at, place, _)) = nested {
460            let StreamElement::Stream(inner) = events[voice_at].0.element() else {
461                index += 1;
462                continue;
463            };
464            let next_event = inner.events()[place].clone();
465            // music21 compares the note's offset in its own voice.
466            if same_offset(next_event.offset(), end)
467                && expression_is_inferred(next_event.element())
468                && 0.0 < to_complete
469                && to_complete < next_event.element().quarter_length()
470            {
471                let at = next_event.offset();
472                let (left, right) = split_element(next_event.element(), to_complete)?;
473                if let StreamElement::Stream(inner) = events[voice_at].0.element_mut() {
474                    inner.events_mut()[place] = StreamEvent::new(at, left);
475                }
476                // The piece cut off goes into the line the walk is in, not
477                // into the voice the note was in.
478                let piece = StreamEvent::new(op_frac(at + to_complete), right);
479                let place = events
480                    .iter()
481                    .position(|(held, _)| super::event_order(held, &piece).is_gt())
482                    .unwrap_or(events.len());
483                events.insert(place, (piece, false));
484            }
485            index += 1;
486            continue;
487        }
488        let Some(next) = own else {
489            index += 1;
490            continue;
491        };
492        let (next_event, _) = &events[next];
493        if !same_offset(next_event.offset(), end) {
494            index += 1;
495            continue;
496        }
497        if expression_is_inferred(next_event.element())
498            && 0.0 < to_complete
499            && to_complete < next_event.element().quarter_length()
500        {
501            let at = next_event.offset();
502            let (left, right) = split_element(next_event.element(), to_complete)?;
503            events[next].0 = StreamEvent::new(at, left);
504            // The cut piece goes where music21 inserts it: after everything
505            // that sorts no later, which is always after this note.
506            let piece = StreamEvent::new(op_frac(at + to_complete), right);
507            let place = events
508                .iter()
509                .position(|(held, _)| super::event_order(held, &piece).is_gt())
510                .unwrap_or(events.len());
511            events.insert(place, (piece, false));
512        }
513        index += 1;
514    }
515    let spanners = line.spanners().to_vec();
516    *line = line.with_events(events.into_iter().map(|(event, _)| event).collect());
517    for spanner in spanners {
518        line.add_spanner(spanner);
519    }
520    Ok(())
521}
522
523/// Joins a run of tied notes, or of rests, that fills one tuplet exactly
524/// into one note of the tuplet's whole length: music21's
525/// `consolidateCompletedTuplets` with `onlyIfTied`, on one line. Whether
526/// anything was joined.
527pub fn consolidate_completed_tuplets(line: &mut Stream) -> bool {
528    let mut events = line.events().to_vec();
529    let mut removed: Vec<usize> = Vec::new();
530    let mut consolidated = false;
531    // music21's `TupletSearchState`.
532    let mut group: Vec<Option<usize>> = Vec::new();
533    let mut partial: FloatType = 0.0;
534    let mut last: Option<Tuplet> = None;
535    let mut target: Option<FloatType> = None;
536    let notes: Vec<usize> = events
537        .iter()
538        .enumerate()
539        .filter(|(_, event)| is_general_note(event.element()))
540        .map(|(index, _)| index)
541        .collect();
542    let reexpressible = |event: &StreamEvent| {
543        let element = event.element();
544        let tied = match element {
545            StreamElement::Note(note) => note.tie().is_some(),
546            StreamElement::Chord(chord) => chord.notes().iter().any(|note| note.tie().is_some()),
547            StreamElement::Unpitched(stroke) => stroke.written().tie().is_some(),
548            _ => false,
549        };
550        expression_is_inferred(element)
551            && duration_of(element).tuplets().len() < 2
552            && (matches!(element, StreamElement::Rest(_)) || tied)
553    };
554    for (walked, &index) in notes.iter().enumerate() {
555        let event = &events[index];
556        let element = event.element();
557        partial = op_frac(partial + element.quarter_length());
558        let tested = if group.is_empty() {
559            true
560        } else {
561            let previous = &events[notes[walked - 1]];
562            let rests = matches!(element, StreamElement::Rest(_))
563                && matches!(previous.element(), StreamElement::Rest(_));
564            let same_notes = is_not_rest(element)
565                && is_not_rest(previous.element())
566                && same_pitches(element, previous.element());
567            let tuplets = duration_of(element).tuplets();
568            (rests || same_notes)
569                && same_offset(
570                    op_frac(previous.offset() + previous.element().quarter_length()),
571                    event.offset(),
572                )
573                && tuplets.len() == 1
574                && Some(tuplets[0]) == last
575        };
576        if tested {
577            if group.is_empty() {
578                partial = element.quarter_length();
579                if let Some(first) = duration_of(element).tuplets().first() {
580                    last = Some(*first);
581                    target = Some(first.total_tuplet_length());
582                    group.push(Some(index));
583                }
584            } else {
585                group.push(Some(index));
586            }
587        } else if !group.is_empty() {
588            group.push(None);
589        }
590        if target.is_some_and(|target| target == partial) {
591            let all = group
592                .iter()
593                .all(|held| held.is_some_and(|held| reexpressible(&events[held])));
594            if all && let Some(Some(first)) = group.first() {
595                consolidated = true;
596                removed.extend(group.iter().skip(1).flatten());
597                if let Ok(duration) = Duration::new(partial) {
598                    set_duration(events[*first].element_mut(), duration);
599                }
600            }
601            group.clear();
602            partial = 0.0;
603            last = None;
604            target = None;
605        }
606    }
607    if !consolidated {
608        return false;
609    }
610    let kept: Vec<StreamEvent> = events
611        .drain(..)
612        .enumerate()
613        .filter(|(index, _)| !removed.contains(index))
614        .map(|(_, event)| event)
615        .collect();
616    let spanners = line.spanners().to_vec();
617    *line = line.with_events(kept);
618    for spanner in spanners {
619        line.add_spanner(spanner);
620    }
621    true
622}
623
624/// Whether two notes or chords sound the same pitches, spelled alike:
625/// music21's `pitches ==`.
626fn same_pitches(left: &StreamElement, right: &StreamElement) -> bool {
627    let left = left.pitches();
628    let right = right.pitches();
629    left.len() == right.len() && left.iter().zip(&right).all(|(one, other)| one == other)
630}
631
632/// Cuts every note, chord and rest whose length no single written value
633/// writes into pieces that each are one, tied: music21's `splitAtDurations`
634/// with `recurse`.
635///
636/// A rest filling its measure's whole bar is left as it is, as a whole-bar
637/// rest is written as one whatever its length. Spanners joining a note that
638/// is cut start on its first piece and end on its last.
639///
640/// ```
641/// use music21_rs::makenotation::split_at_durations;
642/// use music21_rs::{Duration, Note, Stream};
643///
644/// let mut line = Stream::new();
645/// line.insert(0.0, Note::from_name("C4")?.with_duration(Duration::new(5.0)?));
646/// split_at_durations(&mut line)?;
647/// assert_eq!(line.len(), 2);
648/// assert_eq!(line.events()[1].offset(), 4.0);
649/// # Ok::<(), music21_rs::Error>(())
650/// ```
651///
652/// # Errors
653///
654/// A length no tie of written values reaches.
655pub fn split_at_durations(stream: &mut Stream) -> Result<()> {
656    keeping_spanners(stream, Kept::Split, &mut |stream| {
657        split_container(stream, None)
658    })
659}
660
661/// An element of a complex duration cut into one piece for each of its
662/// written values, tied: music21's `splitAtDurations` of one element. Each
663/// piece comes with its length.
664pub(crate) fn element_at_durations(
665    element: &StreamElement,
666    duration: &Duration,
667) -> Result<Vec<(FloatType, StreamElement)>> {
668    let multiplier = duration.aggregate_tuplet_multiplier();
669    let multiplier = *multiplier.numer().unwrap_or(&1) as FloatType
670        / *multiplier.denom().unwrap_or(&1) as FloatType;
671    let lengths: Vec<FloatType> = duration
672        .written_values()
673        .iter()
674        .map(|value| op_frac(value.quarter_length() * multiplier))
675        .collect();
676    let mut pieces = Vec::new();
677    let mut remain = element.clone();
678    for length in &lengths[..lengths.len().saturating_sub(1)] {
679        let (piece, rest) = split_element(&remain, *length)?;
680        pieces.push((*length, piece));
681        remain = rest;
682    }
683    pieces.push((lengths.last().copied().unwrap_or(0.0), remain));
684    Ok(pieces)
685}
686
687/// [`split_at_durations`] with no spanners to keep: the length of the bar
688/// in force where the stream is a measure or a voice in one.
689fn split_container(stream: &mut Stream, bar: Option<FloatType>) -> Result<()> {
690    let own_bar = match stream.kind() {
691        StreamKind::Measure | StreamKind::Voice => bar,
692        _ => None,
693    };
694    let mut events: Vec<StreamEvent> = Vec::new();
695    // The pieces of each element cut, in the order the elements stood.
696    let mut pieces: Vec<StreamEvent> = Vec::new();
697    for event in stream.events() {
698        let element = event.element();
699        let Some(duration) = element.duration() else {
700            events.push(event.clone());
701            continue;
702        };
703        if matches!(element, StreamElement::Stream(_)) || !duration.is_complex() {
704            events.push(event.clone());
705            continue;
706        }
707        if matches!(element, StreamElement::Rest(_))
708            && own_bar.is_some_and(|bar| {
709                duration.tuplets().is_empty() && duration.quarter_length() == bar
710            })
711        {
712            events.push(event.clone());
713            continue;
714        }
715        let mut offset = event.offset();
716        for (length, piece) in element_at_durations(element, duration)? {
717            pieces.push(StreamEvent::new(offset, piece));
718            offset = op_frac(offset + length);
719        }
720    }
721    // music21 puts each piece in as a new element, the first as much as the
722    // others: after everything already standing where it starts.
723    for piece in pieces {
724        let place = events
725            .iter()
726            .position(|held| super::event_order(held, &piece).is_gt())
727            .unwrap_or(events.len());
728        events.insert(place, piece);
729    }
730    let spanners = stream.spanners().to_vec();
731    *stream = stream.with_events(events);
732    for spanner in spanners {
733        stream.add_spanner(spanner);
734    }
735    // The meter in force, measure by measure, for the streams inside.
736    let mut meter_bar = bar;
737    for event in stream.events_mut() {
738        if let StreamElement::Stream(inner) = event.element_mut() {
739            if inner.kind() == StreamKind::Measure
740                && let Some(meter) = super::meter_in(inner)
741            {
742                meter_bar = Some(meter.bar_quarter_length());
743            }
744            let inner_bar = match inner.kind() {
745                StreamKind::Measure => Some(meter_bar.unwrap_or(4.0)),
746                StreamKind::Voice => own_bar,
747                _ => None,
748            };
749            split_container(inner, inner_bar)?;
750        }
751    }
752    Ok(())
753}
754
755// --------------------------------------------------------------- spanners
756
757/// Which piece of a cut element a spanner is left holding.
758#[derive(Clone, Copy, PartialEq, Eq)]
759pub(crate) enum Kept {
760    /// The first, which is the element itself cut short, whatever end of
761    /// the spanner it stands at: music21's `splitAtQuarterLength`.
762    Retained,
763    /// As `Retained`, for an edit that may move measures but leaves every
764    /// element where it stood in its own: the element is looked for by its
765    /// measure and its place in it.
766    InMeasure,
767    /// The first at the spanner's start and the last at its end, and none
768    /// in its middle: music21's `splitAtDurations`, which replaces the
769    /// element with copies.
770    Split,
771}
772
773/// One element as it stood before an edit, to be found again after it.
774struct Leaf {
775    /// The event of the holding stream it is inside.
776    top: usize,
777    /// The voice it is in, if any.
778    voice: Option<String>,
779    /// Which of the holder's measures it is in, counted from the first, and
780    /// where in it.
781    measure: Option<usize>,
782    local: FloatType,
783    offset: FloatType,
784    /// Where its last piece starts, if it is cut into the values it is
785    /// written as.
786    last_offset: FloatType,
787    what: String,
788}
789
790fn describe(element: &StreamElement) -> String {
791    let class = match element {
792        StreamElement::Note(_) => "Note",
793        StreamElement::Chord(_) => "Chord",
794        StreamElement::Rest(_) => "Rest",
795        StreamElement::Unpitched(_) => "Unpitched",
796        StreamElement::PercussionChord(_) => "PercussionChord",
797        StreamElement::ChordSymbol(_) => "ChordSymbol",
798        StreamElement::Dynamic(_) => "Dynamic",
799        StreamElement::Clef(_) => "Clef",
800        StreamElement::TextExpression(_) => "TextExpression",
801        StreamElement::RepeatExpression(_) => "RepeatExpression",
802        StreamElement::MetronomeMark(_) => "MetronomeMark",
803        StreamElement::TempoText(_) => "TempoText",
804        _ => "Other",
805    };
806    let pitches: Vec<String> = element
807        .pitches()
808        .iter()
809        .map(crate::pitch::Pitch::name_with_octave)
810        .collect();
811    let grace = element.duration().is_some_and(Duration::is_grace);
812    format!("{class} {} {grace}", pitches.join(" "))
813}
814
815fn leaves_of(stream: &Stream) -> Vec<Leaf> {
816    /// Where a walk has got to: the measure it is in and where that starts.
817    #[derive(Clone)]
818    struct Within {
819        top: usize,
820        voice: Option<String>,
821        measure: Option<(usize, FloatType)>,
822    }
823    fn leaf(element: &StreamElement, offset: FloatType, within: &Within) -> Leaf {
824        let last_offset = match element.duration() {
825            Some(duration) if duration.is_complex() => {
826                let lengths: Vec<FloatType> = duration
827                    .written_values()
828                    .iter()
829                    .map(|value| value.quarter_length())
830                    .collect();
831                let written: FloatType = lengths.iter().sum();
832                let scale = if written == 0.0 {
833                    1.0
834                } else {
835                    duration.quarter_length() / written
836                };
837                offset + (written - lengths.last().copied().unwrap_or(0.0)) * scale
838            }
839            _ => offset,
840        };
841        Leaf {
842            top: within.top,
843            voice: within.voice.clone(),
844            measure: within.measure.map(|(index, _)| index),
845            local: within.measure.map_or(offset, |(_, start)| offset - start),
846            offset,
847            last_offset,
848            what: describe(element),
849        }
850    }
851    fn enter(inner: &Stream, offset: FloatType, within: &Within, measures: &mut usize) -> Within {
852        let mut inside = within.clone();
853        match inner.kind() {
854            StreamKind::Voice => {
855                inside.voice = Some(inner.id().unwrap_or_default().to_string());
856            }
857            StreamKind::Measure => {
858                inside.measure = Some((*measures, offset));
859                *measures += 1;
860            }
861            _ => {}
862        }
863        inside
864    }
865    fn walk(
866        stream: &Stream,
867        base: FloatType,
868        within: &Within,
869        measures: &mut usize,
870        out: &mut Vec<Leaf>,
871    ) {
872        for event in stream.events() {
873            let offset = base + event.offset();
874            match event.element() {
875                StreamElement::Stream(inner) => {
876                    let inside = enter(inner, offset, within, measures);
877                    walk(inner, offset, &inside, measures, out);
878                }
879                element => out.push(leaf(element, offset, within)),
880            }
881        }
882    }
883    let mut out = Vec::new();
884    let mut measures = 0;
885    for (top, event) in stream.events().iter().enumerate() {
886        let within = Within {
887            top,
888            voice: None,
889            measure: None,
890        };
891        match event.element() {
892            StreamElement::Stream(inner) => {
893                let inside = enter(inner, event.offset(), &within, &mut measures);
894                walk(inner, event.offset(), &inside, &mut measures, &mut out);
895            }
896            // An element the holder holds itself is in no part, and where
897            // it stands among the holder's events moves as others are cut.
898            element => out.push(leaf(
899                element,
900                event.offset(),
901                &Within {
902                    top: usize::MAX,
903                    ..within
904                },
905            )),
906        }
907    }
908    out
909}
910
911/// Where each element standing before an edit stands after it: its first
912/// piece, and its last, by position; nothing for one that is gone.
913fn match_leaves(
914    before: &[Leaf],
915    after: &[Leaf],
916    by_measure: bool,
917) -> (Vec<Option<usize>>, Vec<Option<usize>>) {
918    let near = |one: FloatType, other: FloatType| (one - other).abs() < 1e-6;
919    let mut used = vec![false; after.len()];
920    let find = |leaf: &Leaf, offset: FloatType, used: &[bool], exact_voice: bool| {
921        after.iter().enumerate().position(|(index, candidate)| {
922            let same_place = if by_measure && offset == leaf.offset {
923                candidate.measure == leaf.measure && near(candidate.local, leaf.local)
924            } else {
925                near(candidate.offset, offset)
926            };
927            !used[index]
928                && candidate.what == leaf.what
929                && same_place
930                && (!exact_voice || candidate.voice == leaf.voice)
931                && (candidate.top == leaf.top || !exact_voice)
932        })
933    };
934    let mut first = Vec::with_capacity(before.len());
935    for leaf in before {
936        let found =
937            find(leaf, leaf.offset, &used, true).or_else(|| find(leaf, leaf.offset, &used, false));
938        if let Some(found) = found {
939            used[found] = true;
940        }
941        first.push(found);
942    }
943    let none = vec![false; after.len()];
944    let last = before
945        .iter()
946        .zip(&first)
947        .map(|(leaf, first)| {
948            if near(leaf.last_offset, leaf.offset) {
949                *first
950            } else {
951                find(leaf, leaf.last_offset, &none, true).or(*first)
952            }
953        })
954        .collect();
955    (first, last)
956}
957
958/// The spanners a stream and its parts hold, taken off them, with the
959/// elements as they stood.
960struct HeldSpanners {
961    /// For the stream itself and for each stream it holds directly, by
962    /// event index: its spanners and its elements.
963    holders: Vec<(Option<usize>, Vec<Spanner>, Vec<Leaf>)>,
964}
965
966fn take_spanners(stream: &mut Stream) -> HeldSpanners {
967    let mut holders = Vec::new();
968    let own = stream.spanners().to_vec();
969    if !own.is_empty() {
970        holders.push((None, own, leaves_of(stream)));
971        stream.clear_spanners();
972    }
973    for (index, event) in stream.events_mut().iter_mut().enumerate() {
974        if let StreamElement::Stream(inner) = event.element_mut() {
975            let spanners = inner.spanners().to_vec();
976            if !spanners.is_empty() {
977                holders.push((Some(index), spanners, leaves_of(inner)));
978                inner.clear_spanners();
979            }
980        }
981    }
982    HeldSpanners { holders }
983}
984
985fn restore_spanners(stream: &mut Stream, held: HeldSpanners, kept: Kept) {
986    for (holder, spanners, before) in held.holders {
987        let target: &mut Stream = match holder {
988            None => stream,
989            Some(index) => match stream
990                .events_mut()
991                .get_mut(index)
992                .map(StreamEvent::element_mut)
993            {
994                Some(StreamElement::Stream(inner)) => inner,
995                _ => continue,
996            },
997        };
998        let after = leaves_of(target);
999        let (first, last) = match_leaves(&before, &after, kept == Kept::InMeasure);
1000        for mut spanner in spanners {
1001            let count = spanner.spanned().len();
1002            for (at, position) in spanner.spanned_mut().iter_mut().enumerate() {
1003                let Some(old) = *position else {
1004                    continue;
1005                };
1006                let was_cut = before
1007                    .get(old)
1008                    .is_some_and(|leaf| (leaf.last_offset - leaf.offset).abs() > 1e-6);
1009                *position = match kept {
1010                    Kept::Retained | Kept::InMeasure => first.get(old).copied().flatten(),
1011                    Kept::Split if at == 0 => first.get(old).copied().flatten(),
1012                    Kept::Split if at + 1 == count => last.get(old).copied().flatten(),
1013                    Kept::Split if was_cut => None,
1014                    Kept::Split => first.get(old).copied().flatten(),
1015                };
1016            }
1017            target.add_spanner(spanner);
1018        }
1019    }
1020}
1021
1022/// Runs an edit over a stream, and puts the spanners it and the streams it
1023/// holds directly carried back on the elements they joined.
1024pub(crate) fn keeping_spanners<T>(
1025    stream: &mut Stream,
1026    kept: Kept,
1027    edit: &mut dyn FnMut(&mut Stream) -> Result<T>,
1028) -> Result<T> {
1029    let held = take_spanners(stream);
1030    let result = edit(stream);
1031    restore_spanners(stream, held, kept);
1032    result
1033}
1034
1035#[cfg(test)]
1036mod tests {
1037    use super::*;
1038    use crate::notation::{Tie, TieType};
1039    use crate::spanner::Spanner;
1040    use crate::{Note, Rest, TimeSignature};
1041
1042    fn note(name: &str, quarter_length: FloatType) -> Note {
1043        Note::from_name(name)
1044            .unwrap()
1045            .with_duration(Duration::new(quarter_length).unwrap())
1046    }
1047
1048    fn lengths(stream: &Stream) -> Vec<FloatType> {
1049        stream
1050            .leaves()
1051            .iter()
1052            .filter(|(_, element)| is_general_note(element))
1053            .map(|(_, element)| element.quarter_length())
1054            .collect()
1055    }
1056
1057    fn ties(stream: &Stream) -> Vec<Option<TieType>> {
1058        stream
1059            .leaves()
1060            .iter()
1061            .filter_map(|(_, element)| match element {
1062                StreamElement::Note(note) => Some(note.tie().map(Tie::tie_type)),
1063                _ => None,
1064            })
1065            .collect()
1066    }
1067
1068    fn close(left: &[FloatType], right: &[FloatType]) -> bool {
1069        left.len() == right.len()
1070            && left
1071                .iter()
1072                .zip(right)
1073                .all(|(one, other)| (one - other).abs() < 1e-9)
1074    }
1075
1076    #[test]
1077    fn tuplets_are_bracketed_a_group_at_a_time() {
1078        // music21: six triplet eighths in 2/4 are
1079        // ['start', None, 'stop', 'start', None, 'stop'].
1080        let mut line = Stream::new();
1081        line.insert(0.0, TimeSignature::new(2, 4).unwrap());
1082        for index in 0..6 {
1083            line.insert(FloatType::from(index) / 3.0, note("C4", 1.0 / 3.0));
1084        }
1085        make_tuplet_brackets(&mut line);
1086        let types: Vec<Option<TupletType>> = line
1087            .leaves()
1088            .iter()
1089            .filter(|(_, element)| is_general_note(element))
1090            .map(|(_, element)| duration_of(element).tuplets()[0].tuplet_type())
1091            .collect();
1092        assert_eq!(
1093            types,
1094            [
1095                Some(TupletType::Start),
1096                None,
1097                Some(TupletType::Stop),
1098                Some(TupletType::Start),
1099                None,
1100                Some(TupletType::Stop),
1101            ]
1102        );
1103    }
1104
1105    #[test]
1106    fn a_tuplet_alone_is_bracketed_on_its_own_with_no_bracket_drawn() {
1107        let mut line = Stream::new();
1108        line.insert(0.0, note("C4", 1.0));
1109        line.insert(1.0, note("D4", 1.0 / 3.0));
1110        line.insert(4.0 / 3.0, note("E4", 1.0));
1111        make_tuplet_brackets(&mut line);
1112        let tuplet = duration_of(line.events()[1].element()).tuplets()[0];
1113        assert_eq!(tuplet.tuplet_type(), Some(TupletType::StartStop));
1114        assert_eq!(tuplet.bracket(), TupletBracket::None);
1115    }
1116
1117    #[test]
1118    fn a_note_is_cut_to_finish_the_tuplet_before_it() {
1119        // music21's splitElementsToCompleteTuplets on lengths 1/3, 1, 2/3:
1120        // [1/3, 2/3, 1/3, 2/3], tied [None, start, stop, None].
1121        let mut line = Stream::new();
1122        line.insert(0.0, note("C4", 1.0 / 3.0));
1123        line.insert(1.0 / 3.0, note("C4", 1.0));
1124        line.insert(4.0 / 3.0, note("C4", 2.0 / 3.0));
1125        split_elements_to_complete_tuplets(&mut line).unwrap();
1126        assert!(close(
1127            &lengths(&line),
1128            &[1.0 / 3.0, 2.0 / 3.0, 1.0 / 3.0, 2.0 / 3.0]
1129        ));
1130        assert_eq!(
1131            ties(&line),
1132            [None, Some(TieType::Start), Some(TieType::Stop), None]
1133        );
1134    }
1135
1136    #[test]
1137    fn a_length_that_was_said_is_not_cut() {
1138        let mut line = Stream::new();
1139        line.insert(0.0, note("C4", 1.0 / 3.0));
1140        line.insert(
1141            1.0 / 3.0,
1142            Note::from_name("C4")
1143                .unwrap()
1144                .with_duration(Duration::quarter()),
1145        );
1146        split_elements_to_complete_tuplets(&mut line).unwrap();
1147        assert!(close(&lengths(&line), &[1.0 / 3.0, 1.0]));
1148    }
1149
1150    #[test]
1151    fn a_rest_between_two_sixths_is_cut_to_finish_the_first() {
1152        // music21: a measure of a sixth, a rest and a sixth at 5/6 is
1153        // [1/6, 1/3, 1/3, 1/6] once the rest is cut.
1154        let mut measure = Stream::with_kind(StreamKind::Measure);
1155        measure.insert(0.0, note("C4", 1.0 / 6.0));
1156        measure.insert(5.0 / 6.0, note("C4", 1.0 / 6.0));
1157        super::super::fill_line(&mut measure, 0.0, 1.0, false).unwrap();
1158        let mut part = Stream::with_kind(StreamKind::Part);
1159        part.insert(0.0, measure);
1160        for_each_container(&mut part, &mut split_elements_to_complete_tuplets).unwrap();
1161        assert!(close(
1162            &lengths(&part),
1163            &[1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0]
1164        ));
1165    }
1166
1167    #[test]
1168    fn rests_filling_a_tuplet_are_joined_into_one() {
1169        // music21's consolidateCompletedTuplets: five sixth rests and a
1170        // sixth note are [0.5, 1/6, 1/6, 1/6].
1171        let mut line = Stream::new();
1172        for index in 0..5 {
1173            line.insert(
1174                FloatType::from(index) / 6.0,
1175                Rest::new(Duration::new(1.0 / 6.0).unwrap()),
1176            );
1177        }
1178        line.insert(5.0 / 6.0, note("C4", 1.0 / 6.0));
1179        assert!(consolidate_completed_tuplets(&mut line));
1180        assert!(close(
1181            &lengths(&line),
1182            &[0.5, 1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0]
1183        ));
1184    }
1185
1186    #[test]
1187    fn untied_notes_filling_a_tuplet_are_left_apart() {
1188        let mut line = Stream::new();
1189        for index in 0..3 {
1190            line.insert(FloatType::from(index) / 3.0, note("C4", 1.0 / 3.0));
1191        }
1192        assert!(!consolidate_completed_tuplets(&mut line));
1193        assert_eq!(lengths(&line).len(), 3);
1194    }
1195
1196    #[test]
1197    fn a_length_no_one_value_writes_is_cut_into_values() {
1198        // music21: a note of five quarters is a whole tied to a quarter.
1199        let mut line = Stream::new();
1200        line.insert(0.0, note("C4", 5.0));
1201        split_at_durations(&mut line).unwrap();
1202        assert!(close(&lengths(&line), &[4.0, 1.0]));
1203        assert_eq!(ties(&line), [Some(TieType::Start), Some(TieType::Stop)]);
1204    }
1205
1206    #[test]
1207    fn a_rest_filling_its_bar_is_not_cut() {
1208        // music21 leaves a rest of five quarters whole in a 5/4 measure,
1209        // and cuts one that does not fill its measure.
1210        let mut measure = Stream::with_kind(StreamKind::Measure);
1211        measure.insert(0.0, TimeSignature::new(5, 4).unwrap());
1212        measure.insert(0.0, Rest::new(Duration::new(5.0).unwrap()));
1213        let mut part = Stream::with_kind(StreamKind::Part);
1214        part.insert(0.0, measure);
1215        split_at_durations(&mut part).unwrap();
1216        assert!(close(&lengths(&part), &[5.0]));
1217
1218        let mut measure = Stream::with_kind(StreamKind::Measure);
1219        measure.insert(0.0, TimeSignature::new(6, 4).unwrap());
1220        measure.insert(0.0, Rest::new(Duration::new(5.0).unwrap()));
1221        let mut part = Stream::with_kind(StreamKind::Part);
1222        part.insert(0.0, measure);
1223        split_at_durations(&mut part).unwrap();
1224        assert!(close(&lengths(&part), &[4.0, 1.0]));
1225    }
1226
1227    #[test]
1228    fn a_slur_stays_on_its_notes_and_ends_on_the_last_piece() {
1229        let mut line = Stream::new();
1230        line.insert(0.0, note("C4", 1.0));
1231        line.insert(1.0, note("D4", 5.0));
1232        line.add_spanner(Spanner::slur(vec![0, 1]));
1233        split_at_durations(&mut line).unwrap();
1234        // D4 is now a whole at 1 and a quarter at 5; the slur ends on the
1235        // quarter.
1236        assert_eq!(line.spanners()[0].spanned(), [Some(0), Some(2)]);
1237    }
1238
1239    #[test]
1240    fn loose_notes_are_measured_tied_and_beamed() {
1241        let mut part = Stream::with_kind(StreamKind::Part);
1242        part.insert(0.0, TimeSignature::new(2, 4).unwrap());
1243        for (index, name) in ["C4", "D4", "E4", "F4"].iter().enumerate() {
1244            part.insert(index as FloatType * 0.5, note(name, 0.5));
1245        }
1246        part.insert(2.0, note("G4", 1.0));
1247        part.insert(3.0, note("A4", 2.0));
1248        part.add_spanner(Spanner::slur(vec![1, 6]));
1249        make_notation(&mut part).unwrap();
1250        assert_eq!(part.measures().len(), 3);
1251        // The half note is cut at the second barline.
1252        assert!(close(&lengths(&part), &[0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0]));
1253        assert_eq!(
1254            ties(&part)[5..],
1255            [Some(TieType::Start), Some(TieType::Stop)]
1256        );
1257        assert!(beams_made(&part));
1258        // The slur still starts on the first note and ends on the first
1259        // piece of the last.
1260        let leaves = part.leaves();
1261        let ends: Vec<String> = part.spanners()[0]
1262            .spanned()
1263            .iter()
1264            .map(|position| describe(leaves[position.unwrap()].1))
1265            .collect();
1266        assert_eq!(ends, ["Note C4 false", "Note A4 false"]);
1267        let last = part.spanners()[0].spanned()[1].unwrap();
1268        assert!((leaves[last].0 - 3.0).abs() < 1e-9);
1269    }
1270
1271    #[test]
1272    fn a_duration_says_whether_its_writing_was_worked_out() {
1273        // music21: Duration(0.5).expressionIsInferred is True, and
1274        // Duration('eighth').expressionIsInferred False.
1275        assert!(Duration::new(0.5).unwrap().expression_is_inferred());
1276        assert!(!Duration::eighth().expression_is_inferred());
1277        let mut duration = Duration::eighth();
1278        duration.set_quarter_length(0.25).unwrap();
1279        assert!(duration.expression_is_inferred());
1280    }
1281}