Skip to main content

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