Skip to main content

music21_rs/alpha/analysis/
fixer.rs

1//! A score read by optical music recognition corrected by a performance of
2//! it: music21's `alpha.analysis.fixer`.
3//!
4//! The two are aligned by [`StreamAligner`], its target the performance
5//! (MIDI) and its source the reading (OMR), and each fixer reads the
6//! changes the alignment found: [`fix_enharmonics`] respells a note read
7//! with the wrong accidental, [`delete_measures`] takes out a measure
8//! holding a note read wrong, and an [`OrnamentFixer`] writes the trill or
9//! turn a performance plays where the reading has a single note.
10
11use crate::alpha::analysis::aligner::{Change, ChangeOp, StreamAligner};
12use crate::alpha::analysis::hasher::HashReference;
13use crate::alpha::analysis::ornament_recognizer::{TrillRecognizer, TurnRecognizer};
14use crate::braille::equality::equal;
15use crate::defaults::FloatType;
16use crate::error::{Error, Result};
17use crate::expressions::{Expression, Ornament};
18use crate::interval::notes_to_chromatic;
19use crate::note::Note;
20use crate::pitch::Accidental;
21use crate::stream::{Stream, StreamElement, StreamKind};
22
23/// What a reference names: an element, or a note of a chord hashed note by
24/// note.
25enum Named<'a> {
26    Element(&'a StreamElement),
27    InChord(&'a Note),
28}
29
30impl Named<'_> {
31    fn note(&self) -> Option<&Note> {
32        match self {
33            Self::Element(StreamElement::Note(note)) => Some(note),
34            Self::InChord(note) => Some(*note),
35            Self::Element(_) => None,
36        }
37    }
38
39    fn quarter_length(&self) -> FloatType {
40        match self {
41            Self::Element(element) => element.quarter_length(),
42            Self::InChord(note) => StreamElement::Note((*note).clone()).quarter_length(),
43        }
44    }
45
46    fn element(&self) -> StreamElement {
47        match self {
48            Self::Element(element) => (*element).clone(),
49            Self::InChord(note) => StreamElement::Note((*note).clone()),
50        }
51    }
52}
53
54/// The element at a place in [`Stream::recurse`], walking the stream as it
55/// does: each element, then what it holds.
56fn element_at(stream: &Stream, place: usize) -> Option<&StreamElement> {
57    stream.recurse().get(place).map(|(_, element)| *element)
58}
59
60/// The same, to be changed.
61fn element_at_mut(stream: &mut Stream, place: usize) -> Option<&mut StreamElement> {
62    fn walk<'a>(
63        stream: &'a mut Stream,
64        place: usize,
65        seen: &mut usize,
66    ) -> Option<&'a mut StreamElement> {
67        for event in stream.events_mut() {
68            if *seen == place {
69                return Some(event.element_mut());
70            }
71            *seen += 1;
72            if let StreamElement::Stream(inner) = event.element_mut()
73                && let Some(found) = walk(inner, place, seen)
74            {
75                return Some(found);
76            }
77        }
78        None
79    }
80    walk(stream, place, &mut 0)
81}
82
83/// The stream directly holding the element at a place in
84/// [`Stream::recurse`], and the element's place among its events.
85fn container_of(stream: &Stream, place: usize) -> Option<(&Stream, usize)> {
86    fn walk<'a>(stream: &'a Stream, place: usize, seen: &mut usize) -> Option<(&'a Stream, usize)> {
87        for (index, event) in stream.events().iter().enumerate() {
88            if *seen == place {
89                return Some((stream, index));
90            }
91            *seen += 1;
92            if let StreamElement::Stream(inner) = event.element()
93                && let Some(found) = walk(inner, place, seen)
94            {
95                return Some(found);
96            }
97        }
98        None
99    }
100    walk(stream, place, &mut 0)
101}
102
103fn named(stream: &Stream, reference: HashReference) -> Result<Named<'_>> {
104    let element = element_at(stream, reference.element).ok_or_else(|| {
105        Error::Analysis(format!(
106            "no element at place {} of the stream",
107            reference.element
108        ))
109    })?;
110    match (reference.component, element) {
111        (None, element) => Ok(Named::Element(element)),
112        (Some(component), StreamElement::Chord(chord)) => chord
113            .notes()
114            .get(component)
115            .map(Named::InChord)
116            .ok_or_else(|| Error::Analysis(format!("the chord has no note {component}"))),
117        (Some(_), _) => Err(Error::Analysis(
118            "a reference to a note of something that is not a chord".to_string(),
119        )),
120    }
121}
122
123/// The note a reference names, to be changed, where it names a note.
124fn note_mut(stream: &mut Stream, reference: HashReference) -> Option<&mut Note> {
125    match (
126        reference.component,
127        element_at_mut(stream, reference.element)?,
128    ) {
129        (None, StreamElement::Note(note)) => Some(note),
130        (Some(component), StreamElement::Chord(chord)) => chord.notes_mut().get_mut(component),
131        _ => None,
132    }
133}
134
135/// The references a change makes, which music21 always has: a hash made
136/// with no reference has nothing to fix.
137fn references(change: &Change) -> Result<(HashReference, HashReference)> {
138    match (change.target, change.source) {
139        (Some(midi), Some(omr)) => Ok((midi, omr)),
140        _ => Err(Error::Analysis(
141            "'NoteHash' object has no attribute 'reference'".to_string(),
142        )),
143    }
144}
145
146/// Colours what a reference names, where it can be coloured: a note, a
147/// note of a chord, or a chord.
148fn colour(stream: &mut Stream, reference: HashReference, colour: &str) {
149    let colour = Some(colour.to_string());
150    if reference.component.is_some() {
151        if let Some(note) = note_mut(stream, reference) {
152            note.set_color(colour);
153        }
154        return;
155    }
156    match element_at_mut(stream, reference.element) {
157        Some(StreamElement::Note(note)) => note.set_color(colour),
158        Some(StreamElement::Chord(chord)) => chord.set_color(colour),
159        _ => {}
160    }
161}
162
163/// Respells each note of the reading the performance plays enharmonically
164/// to it: music21's `EnharmonicFixer`.
165///
166/// Every element the changes name in the reading is coloured black. Of
167/// two notes changed into each other no more than a fifth apart, by
168/// interval class:
169///
170/// 1. a reading's natural is dropped where the notes sound alike, and
171///    where the reading is a semitone off it becomes the flat above or the
172///    sharp below the note played;
173/// 2. a reading's sharp or flat on the letter played takes the played
174///    note's accidental;
175/// 3. a sharp read a whole tone above the note played becomes a flat, and
176///    a flat read a whole tone below a sharp;
177/// 4. a note played sharp or flat on the letter read, but read otherwise,
178///    takes the played pitch.
179///
180/// # Errors
181///
182/// A change with no references, or one naming nothing in its stream.
183pub fn fix_enharmonics(changes: &[Change], midi: &Stream, omr: &mut Stream) -> Result<()> {
184    for change in changes {
185        let (midi_reference, omr_reference) = references(change)?;
186        colour(omr, omr_reference, "black");
187        let Some(played) = named(midi, midi_reference)?.note().cloned() else {
188            continue;
189        };
190        let Some(read) = named(omr, omr_reference)?.note().cloned() else {
191            continue;
192        };
193        if change.op == ChangeOp::NoChange {
194            continue;
195        }
196        if notes_to_chromatic(played.pitch(), read.pitch())?.interval_class() > 5 {
197            continue;
198        }
199        let Some(fixed) = note_mut(omr, omr_reference) else {
200            continue;
201        };
202        let (played, read) = (played.pitch(), read.pitch());
203        let accidental = read.written_accidental().map(Accidental::name);
204        let has_sharp_or_flat = accidental.is_some_and(|name| name != "natural");
205        let same_step = played.step() == read.step();
206        let mut pitch = read.clone();
207        if accidental == Some("natural") {
208            if played.ps() == read.ps() {
209                pitch.set_written_accidental(None);
210            } else if read.ps() > played.ps() {
211                if read.ps() - 1.0 == played.ps() {
212                    pitch.set_written_accidental(Some(Accidental::new("flat")?));
213                }
214            } else if read.ps() < played.ps() && read.ps() + 1.0 == played.ps() {
215                pitch.set_written_accidental(Some(Accidental::new("sharp")?));
216            }
217        } else if has_sharp_or_flat && same_step {
218            pitch.set_written_accidental(played.written_accidental().cloned());
219        } else if has_sharp_or_flat {
220            if read.ps() > played.ps() {
221                if accidental == Some("sharp") && read.ps() - 2.0 == played.ps() {
222                    pitch.set_written_accidental(Some(Accidental::new("flat")?));
223                }
224            } else if read.ps() < played.ps()
225                && accidental == Some("flat")
226                && read.ps() + 2.0 == played.ps()
227            {
228                pitch.set_written_accidental(Some(Accidental::new("sharp")?));
229            }
230        } else if read != played
231            && played
232                .written_accidental()
233                .is_some_and(|accidental| accidental.name() != "natural")
234            && same_step
235        {
236            pitch = played.clone();
237        }
238        fixed.set_pitch(pitch);
239    }
240    Ok(())
241}
242
243/// Takes out of the reading each measure holding a note changed from the
244/// performance's: music21's `DeleteFixer`, which "does really weird things
245/// still".
246///
247/// Only a measure standing in the reading itself is taken out, as
248/// music21's `remove` finds nothing deeper, so the measures of a score's
249/// parts stay where they are.
250///
251/// # Errors
252///
253/// A changed note in no measure, which music21 asks to remove nothing, or
254/// a change with no references.
255pub fn delete_measures(changes: &[Change], midi: &Stream, omr: &mut Stream) -> Result<()> {
256    let mut removed: Vec<usize> = Vec::new();
257    let mut result = Ok(());
258    for change in changes {
259        let (midi_reference, omr_reference) = references(change)?;
260        if named(midi, midi_reference)?.note().is_none()
261            || named(omr, omr_reference)?.note().is_none()
262            || change.op == ChangeOp::NoChange
263        {
264            continue;
265        }
266        match measure_holding(omr, omr_reference.element) {
267            Some(Some(top)) => {
268                if !removed.contains(&top) {
269                    removed.push(top);
270                }
271            }
272            Some(None) => {}
273            None => {
274                result = Err(Error::Analysis(
275                    "None is not a Music21Object; got <class 'NoneType'>".to_string(),
276                ));
277                break;
278            }
279        }
280    }
281    removed.sort_unstable();
282    for top in removed.into_iter().rev() {
283        omr.remove_event(top);
284    }
285    result
286}
287
288/// The measure holding the element at a place in [`Stream::recurse`], as
289/// its place among the stream's own events where it stands there, and
290/// nothing where it stands deeper; `None` where no measure holds it.
291fn measure_holding(stream: &Stream, place: usize) -> Option<Option<usize>> {
292    /// The places among their streams' events of the element and of each
293    /// stream holding it, outermost first.
294    fn path(stream: &Stream, place: usize, seen: &mut usize, out: &mut Vec<usize>) -> bool {
295        for (index, event) in stream.events().iter().enumerate() {
296            out.push(index);
297            if *seen == place {
298                return true;
299            }
300            *seen += 1;
301            if let StreamElement::Stream(inner) = event.element()
302                && path(inner, place, seen, out)
303            {
304                return true;
305            }
306            out.pop();
307        }
308        false
309    }
310    let mut places = Vec::new();
311    if !path(stream, place, &mut 0, &mut places) {
312        return None;
313    }
314    // The streams holding the element, each with its depth.
315    let mut holders = Vec::new();
316    let mut current = stream;
317    for (depth, &index) in places[..places.len() - 1].iter().enumerate() {
318        let StreamElement::Stream(inner) = current.events()[index].element() else {
319            return None;
320        };
321        holders.push((depth, inner.kind()));
322        current = inner;
323    }
324    let (depth, _) = holders
325        .into_iter()
326        .rev()
327        .find(|(_, kind)| *kind == StreamKind::Measure)?;
328    Some((depth == 0).then_some(places[0]))
329}
330
331/// The notes, rests and chords from one on, as many as fit in a length,
332/// each a copy: music21's `getNotesWithinDuration`, read from the stream
333/// directly holding the first.
334///
335/// Nothing where the first is longer than the length; else it and each
336/// that follows it in its stream while the length left holds it.
337pub fn notes_within_duration(
338    container: &Stream,
339    index: usize,
340    total: FloatType,
341) -> Vec<StreamElement> {
342    let events = container.events();
343    let Some(first) = events.get(index) else {
344        return Vec::new();
345    };
346    let first = first.element();
347    if first.quarter_length() > total {
348        return Vec::new();
349    }
350    let mut left = total - first.quarter_length();
351    let mut out = vec![first.clone()];
352    for event in &events[index + 1..] {
353        let element = event.element();
354        if !is_general_note(element) {
355            continue;
356        }
357        if left < element.quarter_length() {
358            break;
359        }
360        left -= element.quarter_length();
361        out.push(element.clone());
362    }
363    out
364}
365
366/// music21's `GeneralNote`: anything written that sounds or rests.
367fn is_general_note(element: &StreamElement) -> bool {
368    matches!(
369        element,
370        StreamElement::Note(_)
371            | StreamElement::Chord(_)
372            | StreamElement::Rest(_)
373            | StreamElement::Unpitched(_)
374            | StreamElement::PercussionChord(_)
375            | StreamElement::ChordSymbol(_)
376    )
377}
378
379/// The busy notes a reference names the start of, as many as last no
380/// longer than `total`.
381fn busy_notes(
382    stream: &Stream,
383    reference: HashReference,
384    total: FloatType,
385) -> Result<Vec<StreamElement>> {
386    if reference.component.is_some() {
387        // A note of a chord stands in no stream, so nothing follows it.
388        let note = named(stream, reference)?;
389        return Ok(if note.quarter_length() > total {
390            Vec::new()
391        } else {
392            vec![note.element()]
393        });
394    }
395    let (container, index) = container_of(stream, reference.element).ok_or_else(|| {
396        Error::Analysis(format!(
397            "no element at place {} of the stream",
398            reference.element
399        ))
400    })?;
401    Ok(notes_within_duration(container, index, total))
402}
403
404/// A recognizer an [`OrnamentFixer`] tries.
405#[derive(Clone, Debug, PartialEq)]
406pub enum Recognizer {
407    /// A trill.
408    Trill(TrillRecognizer),
409    /// A turn.
410    Turn(TurnRecognizer),
411}
412
413impl Recognizer {
414    fn recognize(
415        &self,
416        busy: &[StreamElement],
417        simple: &[StreamElement],
418    ) -> Result<Option<Ornament>> {
419        match self {
420            Self::Trill(recognizer) => recognizer.recognize(busy, simple),
421            Self::Turn(recognizer) => recognizer.recognize(busy, simple),
422        }
423    }
424}
425
426/// Writes the ornaments a performance plays on the notes of a reading
427/// that has none: music21's `OrnamentFixer`.
428#[derive(Clone, Debug, PartialEq)]
429pub struct OrnamentFixer {
430    /// The recognizers tried, in order.
431    pub recognizers: Vec<Recognizer>,
432    /// The colour a note given an ornament is marked in: music21's
433    /// `markChangeColor`.
434    pub mark_change_color: String,
435}
436
437impl OrnamentFixer {
438    /// Recognizes trills, plain and then with a nachschlag: music21's
439    /// `TrillFixer`.
440    pub fn trills() -> Self {
441        Self {
442            recognizers: vec![
443                Recognizer::Trill(TrillRecognizer::default()),
444                Recognizer::Trill(TrillRecognizer {
445                    check_nachschlag: true,
446                    ..TrillRecognizer::default()
447                }),
448            ],
449            mark_change_color: "blue".to_string(),
450        }
451    }
452
453    /// Recognizes turns: music21's `TurnFixer`.
454    pub fn turns() -> Self {
455        Self {
456            recognizers: vec![Recognizer::Turn(TurnRecognizer::default())],
457            mark_change_color: "blue".to_string(),
458        }
459    }
460
461    /// The ornament the first recognizer to find one finds: music21's
462    /// `findOrnament`.
463    ///
464    /// # Errors
465    ///
466    /// As the recognizers.
467    pub fn find_ornament(
468        &self,
469        busy: &[StreamElement],
470        simple: &[StreamElement],
471    ) -> Result<Option<Ornament>> {
472        for recognizer in &self.recognizers {
473            if let Some(found) = recognizer.recognize(busy, simple)? {
474                return Ok(Some(found));
475            }
476        }
477        Ok(None)
478    }
479
480    /// Gives each note of the reading an inserted or substituted note
481    /// stands for the ornament the performance plays there, from the
482    /// performed note on for as long as the read note lasts: music21's
483    /// `fix` in place. A read note given an ornament is not tried again,
484    /// nor are performed notes already made into one -- each compared as
485    /// music21 compares notes, by what they are rather than which they are.
486    /// A note with an ornament already keeps it; with `mark`, one given an
487    /// ornament is coloured [`OrnamentFixer::mark_change_color`].
488    ///
489    /// # Errors
490    ///
491    /// A change with no references or naming nothing, or a recognizer's
492    /// error.
493    pub fn fix(
494        &self,
495        changes: &[Change],
496        midi: &Stream,
497        omr: &mut Stream,
498        mark: bool,
499    ) -> Result<()> {
500        let mut labelled: Vec<StreamElement> = Vec::new();
501        let mut used: Vec<StreamElement> = Vec::new();
502        for change in changes {
503            if matches!(change.op, ChangeOp::NoChange | ChangeOp::Deletion) {
504                continue;
505            }
506            let (midi_reference, omr_reference) = references(change)?;
507            let read = named(omr, omr_reference)?;
508            let read_element = read.element();
509            if labelled.iter().any(|done| equal(done, &read_element)) {
510                continue;
511            }
512            let busy = busy_notes(midi, midi_reference, read.quarter_length())?;
513            if busy
514                .iter()
515                .any(|note| used.iter().any(|done| equal(done, note)))
516            {
517                continue;
518            }
519            let Some(ornament) = self.find_ornament(&busy, std::slice::from_ref(&read_element))?
520            else {
521                continue;
522            };
523            used.extend(busy);
524            if let Some(note) = note_mut(omr, omr_reference) {
525                let already = note
526                    .expressions()
527                    .iter()
528                    .any(|expression| matches!(expression, Expression::Ornament(_)));
529                if !already {
530                    note.expressions_mut()
531                        .push(Expression::Ornament(Box::new(ornament)));
532                    if mark {
533                        note.set_color(Some(self.mark_change_color.clone()));
534                    }
535                }
536                labelled.push(StreamElement::Note(note.clone()));
537            }
538        }
539        Ok(())
540    }
541
542    /// The reading with its ornaments written, the two streams copied and
543    /// aligned afresh by a [`StreamAligner`]: music21's `fix` with
544    /// `inPlace=False`, whose new fixer holds this reading.
545    ///
546    /// # Errors
547    ///
548    /// As [`OrnamentFixer::fix`], or streams that cannot be aligned.
549    pub fn fixed(&self, midi: &Stream, omr: &Stream) -> Result<Stream> {
550        let mut aligner = StreamAligner::new();
551        aligner.align(midi, omr)?;
552        let mut fixed = omr.clone();
553        self.fix(&aligner.changes, midi, &mut fixed, false)?;
554        Ok(fixed)
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561    use crate::duration::Duration;
562    use crate::expressions::OrnamentKind;
563
564    fn note(name: &str, length: FloatType) -> Note {
565        Note::from_name(name)
566            .unwrap()
567            .with_duration(Duration::new(length).unwrap())
568    }
569
570    fn measure(notes: &[(&str, FloatType)]) -> Stream {
571        let mut measure = Stream::with_kind(StreamKind::Measure);
572        for (name, length) in notes {
573            measure.push(note(name, *length));
574        }
575        measure
576    }
577
578    fn reference(element: usize) -> Option<HashReference> {
579        Some(HashReference {
580            element,
581            component: None,
582        })
583    }
584
585    /// music21's EnharmonicFixer doctests: a played note and the note
586    /// read, and what the read note becomes.
587    #[test]
588    fn enharmonics_are_respelled_as_music21_respells_them() {
589        for (played, read, fixed) in [
590            ("B-4", "A#4", "A#4"),
591            ("A#4", "A#4", "A#4"),
592            ("A4", "An4", "A4"),
593            ("G#4", "An4", "A-4"),
594            ("G-4", "Fn4", "F#4"),
595            ("G#4", "Gn4", "G#4"),
596            ("G#4", "G-4", "G#4"),
597            ("G#4", "A#4", "A-4"),
598            ("A-4", "G-4", "G#4"),
599        ] {
600            let midi = measure(&[(played, 1.0)]);
601            let mut omr = measure(&[(read, 1.0)]);
602            let op = if played == read {
603                ChangeOp::NoChange
604            } else {
605                ChangeOp::Substitution
606            };
607            let changes = [Change {
608                target: reference(0),
609                source: reference(0),
610                op,
611            }];
612            fix_enharmonics(&changes, &midi, &mut omr).unwrap();
613            let StreamElement::Note(result) = omr.events()[0].element() else {
614                unreachable!("the measure holds a note")
615            };
616            let named = result.pitch().name_with_octave();
617            let expected = Note::from_name(fixed).unwrap().pitch().name_with_octave();
618            assert_eq!(named, expected, "{played} played, {read} read");
619            assert_eq!(result.color(), Some("black"));
620            if read == "An4" && played == "A4" {
621                assert!(result.pitch().written_accidental().is_none());
622            }
623        }
624    }
625
626    /// music21's testGetNotesWithinDuration, its measure m2.
627    #[test]
628    fn notes_within_a_length_are_the_ones_that_fit() {
629        let m2 = measure(&[("C", 1.0), ("D", 0.5), ("E", 0.5)]);
630        let names = |total: FloatType| -> Vec<String> {
631            notes_within_duration(&m2, 0, total)
632                .iter()
633                .map(|element| match element {
634                    StreamElement::Note(note) => note.pitch().name(),
635                    _ => "other".to_string(),
636                })
637                .collect()
638        };
639        assert_eq!(names(1.0), ["C"]);
640        assert_eq!(names(2.0), ["C", "D", "E"]);
641        assert_eq!(names(4.0), ["C", "D", "E"]);
642        assert_eq!(names(1.5), ["C", "D"]);
643        assert_eq!(names(1.75), ["C", "D"]);
644        assert!(names(0.5).is_empty());
645    }
646
647    fn ornaments(stream: &Stream) -> Vec<Vec<(OrnamentKind, FloatType, bool)>> {
648        stream
649            .events()
650            .iter()
651            .map(|event| match event.element() {
652                StreamElement::Note(note) => note
653                    .expressions()
654                    .iter()
655                    .filter_map(|expression| match expression {
656                        Expression::Ornament(ornament) => Some((
657                            ornament.kind(),
658                            ornament.quarter_length(),
659                            ornament.nachschlag(),
660                        )),
661                        _ => None,
662                    })
663                    .collect(),
664                _ => Vec::new(),
665            })
666            .collect()
667    }
668
669    fn fixed(
670        fixer: &OrnamentFixer,
671        midi: &Stream,
672        omr: &Stream,
673    ) -> Vec<Vec<(OrnamentKind, FloatType, bool)>> {
674        // In place and on copies alike.
675        let copied = fixer.fixed(midi, omr).unwrap();
676        let mut aligner = StreamAligner::new();
677        aligner.align(midi, omr).unwrap();
678        let mut in_place = omr.clone();
679        fixer
680            .fix(&aligner.changes, midi, &mut in_place, false)
681            .unwrap();
682        assert_eq!(ornaments(&copied), ornaments(&in_place));
683        ornaments(&in_place)
684    }
685
686    /// music21's testTrillFixer.
687    #[test]
688    fn trills_are_written_as_music21_writes_them() {
689        use OrnamentKind::Trill;
690        let trills = OrnamentFixer::trills();
691        let mut played = measure(&[("G", 0.25), ("A", 0.25), ("G", 0.25), ("A", 0.25)]);
692        for name in ["C", "B3", "C", "B3", "C", "B3", "C", "B3"] {
693            played.push(note(name, 0.0625));
694        }
695        let read = measure(&[("G", 1.0), ("B3", 1.0)]);
696        assert_eq!(
697            fixed(&trills, &played, &read),
698            // Each trilled note lasts the read note's length shared among
699            // the eight played.
700            [vec![(Trill, 0.25, false)], vec![(Trill, 0.125, false)]]
701        );
702
703        let wrong = measure(&[("C", 0.25), ("A", 0.25), ("C", 0.25), ("A", 0.25)]);
704        assert_eq!(fixed(&trills, &wrong, &measure(&[("C", 1.0)])), [vec![]]);
705        let other = measure(&[("C", 0.25), ("D", 0.25), ("C", 0.25), ("D", 0.25)]);
706        assert_eq!(fixed(&trills, &other, &measure(&[("A", 1.0)])), [vec![]]);
707
708        let nachschlag = measure(&[
709            ("E", 0.125),
710            ("F", 0.125),
711            ("E", 0.125),
712            ("F", 0.125),
713            ("E", 0.125),
714            ("F", 0.125),
715            ("E", 0.125),
716            ("D", 0.125),
717        ]);
718        assert_eq!(
719            fixed(&trills, &nachschlag, &measure(&[("E", 1.0)])),
720            [vec![(Trill, 0.125, true)]]
721        );
722
723        let mut trilled = measure(&[("F", 1.0)]);
724        let mut written = Ornament::of_kind(Trill);
725        written.set_quarter_length(0.125);
726        if let StreamElement::Note(note) = trilled.events_mut()[0].element_mut() {
727            note.expressions_mut()
728                .push(Expression::Ornament(Box::new(written)));
729        }
730        let played = measure(&[("F", 0.125), ("G", 0.125), ("F", 0.125), ("G", 0.125)]);
731        assert_eq!(
732            fixed(&trills, &played, &trilled),
733            [vec![(Trill, 0.125, false)]]
734        );
735    }
736
737    /// music21's testTurnFixer.
738    #[test]
739    fn turns_are_written_as_music21_writes_them() {
740        use OrnamentKind::{InvertedTurn, Turn};
741        let turns = OrnamentFixer::turns();
742        let played = measure(&[("G", 1.0), ("F", 1.0), ("E", 1.0), ("F", 1.0)]);
743        assert_eq!(
744            fixed(&turns, &played, &measure(&[("F", 4.0)])),
745            [vec![(Turn, 1.0, false)]]
746        );
747
748        let read = measure(&[("B-", 1.0), ("G", 1.0), ("B-", 1.0)]);
749        let played = measure(&[
750            ("A", 0.25),
751            ("B-", 0.25),
752            ("C5", 0.25),
753            ("B-", 0.25),
754            ("G", 1.0),
755            ("G#", 0.25),
756            ("A#", 0.25),
757            ("B", 0.25),
758            ("A#", 0.25),
759        ]);
760        assert_eq!(
761            fixed(&turns, &played, &read),
762            [
763                vec![(InvertedTurn, 0.25, false)],
764                vec![],
765                vec![(InvertedTurn, 0.25, false)]
766            ]
767        );
768
769        let played = measure(&[("B", 1.0), ("A", 1.0), ("G", 1.0), ("F", 1.0)]);
770        assert_eq!(fixed(&turns, &played, &measure(&[("A", 4.0)])), [vec![]]);
771    }
772}