Skip to main content

music21/musescore/
read.rs

1//! MuseScore's `.mscx`, read into a score.
2//!
3//! A MuseScore file keeps a score's parts apart from what is written on
4//! their staves: each `<Part>` names its instrument and how many staves it
5//! has, and after the parts comes one `<Staff>` for every staff of the
6//! score, holding its measures. A measure holds a `<voice>` for each of up
7//! to four voices, and a voice is its notes and rests in order, with the
8//! clefs, signatures, texts and marks standing between them. Nothing in the
9//! file says where a note starts; that is worked out by adding up the
10//! lengths of what comes before it, a `<location>` moving the place along
11//! where a mark stands between two notes.
12
13use std::collections::HashMap;
14
15use num::rational::Ratio;
16use num::{One, ToPrimitive, Zero};
17
18use super::beams::{self, BeamMode, Member};
19use crate::articulations::{Articulation, ArticulationKind, Finger};
20use crate::bar::{Barline, BarlineType, Ending, RepeatDirection};
21use crate::chord::Chord;
22use crate::chordsymbol::ChordSymbol;
23use crate::clef::Clef;
24use crate::defaults::{FloatType, IntegerType};
25use crate::duration::{
26    Duration, DurationType, Grace, Tuplet, TupletBracket, TupletShow, TupletType,
27};
28use crate::dynamics::Dynamic;
29use crate::error::{Error, FormatErrorKind, Result};
30use crate::expressions::{
31    ArpeggioType, Expression, Fermata, FermataType, Ornament, OrnamentDelay, OrnamentKind,
32    TextExpression,
33};
34use crate::instrument::{Instrument, SearchLanguage};
35use crate::interval::Interval;
36use crate::key::KeySignature;
37use crate::makenotation::op_frac;
38use crate::metadata::{Metadata, MetadataValue};
39use crate::meter::TimeSignature;
40use crate::notation::{
41    Beams, Lyric, NoteSize, Notehead, Placement, StemDirection, Syllabic, Tie, TieType,
42};
43use crate::note::Note;
44use crate::pitch::{Accidental, Pitch};
45use crate::repeat::{RepeatExpression, RepeatExpressionKind};
46use crate::rest::Rest;
47use crate::spanner::{Pedal, PedalForm, PedalType, Spanner, SpannerKind};
48use crate::stream::{BarTogether, StaffGroup, Stream, StreamElement, StreamEvent, StreamKind};
49use crate::tempo::MetronomeMark;
50use crate::volume::Volume;
51use crate::xml::Xml;
52
53/// A length or a place in time, in whole notes, as MuseScore counts.
54type Frac = Ratio<i64>;
55
56/// The most dots a note is read with: MuseScore writes four at most, and
57/// each halves the length the ticks count in.
58const MOST_DOTS: i64 = 16;
59
60fn mscx_error(message: impl Into<String>) -> Error {
61    Error::muse_score(message.into())
62}
63
64fn refused(what: impl std::fmt::Display) -> Error {
65    mscx_error(format!("{what} cannot be read yet")).of_kind(FormatErrorKind::Unsupported)
66}
67
68/// A fraction as MuseScore writes one: `3/4`, or a whole number. Its terms
69/// are read as `i32`, so that the ticks reckoned from them fit an `i64`.
70fn fraction(text: &str) -> Result<Frac> {
71    let text = text.trim();
72    let bad = || mscx_error(format!("{text:?} is not a fraction"));
73    match text.split_once('/') {
74        Some((numerator, denominator)) => {
75            let numerator: i32 = numerator.trim().parse().map_err(|_| bad())?;
76            let denominator: i32 = denominator.trim().parse().map_err(|_| bad())?;
77            if denominator == 0 {
78                return Err(bad());
79            }
80            Ok(Frac::new(numerator.into(), denominator.into()))
81        }
82        None => Ok(Frac::from_integer(
83            text.parse::<i32>().map_err(|_| bad())?.into(),
84        )),
85    }
86}
87
88fn integer(element: &Xml) -> Result<i64> {
89    let text = element.stripped();
90    text.parse()
91        .map_err(|_| mscx_error(format!("<{}> holds {text:?}, not a number", element.tag)))
92}
93
94fn child_integer(element: &Xml, tag: &str) -> Result<Option<i64>> {
95    element.find(tag).map(integer).transpose()
96}
97
98fn flag(element: &Xml, tag: &str) -> bool {
99    element
100        .find(tag)
101        .is_some_and(|found| found.stripped() != "0" && found.stripped() != "false")
102}
103
104/// A length in quarter notes, snapped as music21 snaps one.
105fn quarters(value: Frac) -> FloatType {
106    op_frac((value * 4).to_f64().unwrap_or(0.0))
107}
108
109/// A place in MuseScore's ticks, 480 to a quarter.
110fn ticks(value: Frac) -> i64 {
111    (value * beams::WHOLE).floor().to_integer()
112}
113
114/// Text as MuseScore writes it, its formatting taken off and each symbol
115/// read as the character that draws it.
116fn plain_text(element: &Xml) -> Result<String> {
117    let mut out = String::new();
118    plain_into(element, &mut out)?;
119    Ok(out)
120}
121
122fn plain_into(element: &Xml, out: &mut String) -> Result<()> {
123    if element.tag == "sym" {
124        out.push(symbol_named(element.text().unwrap_or("").trim())?);
125        return Ok(());
126    }
127    // An older file wrote a name as a small HTML page.
128    if matches!(element.tag.as_str(), "head" | "style" | "meta" | "title") {
129        return Ok(());
130    }
131    if element.tag == "br" {
132        out.push('\n');
133        return Ok(());
134    }
135    if let Some(text) = element.text() {
136        out.push_str(text);
137    }
138    for child in &element.children {
139        plain_into(child, out)?;
140        if let Some(tail) = child.tail() {
141            out.push_str(tail);
142        }
143    }
144    Ok(())
145}
146
147/// The character a symbol inside a text is drawn with: its place in the
148/// Standard Music Font Layout, which is what MuseScore names its symbols by.
149fn symbol_named(name: &str) -> Result<char> {
150    Ok(match name {
151        "segno" => '\u{E047}',
152        "coda" => '\u{E048}',
153        "codaSquare" => '\u{E049}',
154        "segnoSerpent1" => '\u{E04A}',
155        "segnoSerpent2" => '\u{E04B}',
156        "accidentalFlat" => '\u{E260}',
157        "accidentalNatural" => '\u{E261}',
158        "accidentalSharp" => '\u{E262}',
159        "accidentalDoubleSharp" => '\u{E263}',
160        "accidentalDoubleFlat" => '\u{E264}',
161        "dynamicPiano" => '\u{E520}',
162        "dynamicMezzo" => '\u{E521}',
163        "dynamicForte" => '\u{E522}',
164        "dynamicRinforzando" => '\u{E523}',
165        "dynamicSforzando" => '\u{E524}',
166        "dynamicZ" => '\u{E525}',
167        "dynamicNiente" => '\u{E526}',
168        "keyboardPedalPed" => '\u{E650}',
169        "keyboardPedalUp" => '\u{E655}',
170        "metNoteDoubleWhole" => '\u{ECA0}',
171        "metNoteWhole" => '\u{ECA2}',
172        "metNoteHalfUp" => '\u{ECA3}',
173        "metNoteHalfDown" => '\u{ECA4}',
174        "metNoteQuarterUp" => '\u{ECA5}',
175        "metNoteQuarterDown" => '\u{ECA6}',
176        "metNote8thUp" => '\u{ECA7}',
177        "metNote8thDown" => '\u{ECA8}',
178        "metNote16thUp" => '\u{ECA9}',
179        "metNote16thDown" => '\u{ECAA}',
180        "metAugmentationDot" => '\u{ECB7}',
181        "space" => ' ',
182        other => return Err(refused(format!("The symbol {other:?} in a text"))),
183    })
184}
185
186// ------------------------------------------------------------ vocabulary
187
188/// A written value by MuseScore's name for it, with its length in whole
189/// notes and its place in MuseScore's order of values.
190fn duration_named(name: &str) -> Result<(DurationType, Frac, u8)> {
191    Ok(match name {
192        "long" => (DurationType::Longa, Frac::from_integer(4), 0),
193        "breve" => (DurationType::Breve, Frac::from_integer(2), 1),
194        "whole" => (DurationType::Whole, Frac::one(), 2),
195        "half" => (DurationType::Half, Frac::new(1, 2), 3),
196        "quarter" => (DurationType::Quarter, Frac::new(1, 4), 4),
197        "eighth" => (DurationType::Eighth, Frac::new(1, 8), 5),
198        "16th" => (DurationType::Sixteenth, Frac::new(1, 16), 6),
199        "32nd" => (DurationType::ThirtySecond, Frac::new(1, 32), 7),
200        "64th" => (DurationType::SixtyFourth, Frac::new(1, 64), 8),
201        "128th" => (DurationType::HundredTwentyEighth, Frac::new(1, 128), 9),
202        "256th" => (DurationType::TwoHundredFiftySixth, Frac::new(1, 256), 10),
203        "512th" => (DurationType::FiveHundredTwelfth, Frac::new(1, 512), 11),
204        "1024th" => (DurationType::TenTwentyFourth, Frac::new(1, 1024), 12),
205        other => return Err(mscx_error(format!("a note of the value {other:?}"))),
206    })
207}
208
209fn dotted(length: Frac, dots: u32) -> Frac {
210    let mut total = length;
211    let mut added = length;
212    for _ in 0..dots {
213        added /= 2;
214        total += added;
215    }
216    total
217}
218
219/// A clef by MuseScore's name: the sign and line MusicXML writes it with,
220/// and how many octaves it sounds away.
221fn clef_named(name: &str) -> Result<Clef> {
222    let (sign, line, octaves) = match name {
223        "G" | "G8vbp" | "C_19C" => ("G", 2, 0),
224        "G15mb" => ("G", 2, -2),
225        "G8vb" | "G8vbo" | "G8vbc" => ("G", 2, -1),
226        "G8va" => ("G", 2, 1),
227        "G15ma" => ("G", 2, 2),
228        "G1" => ("G", 1, 0),
229        "C1" | "C1_F18C" | "C1_F20C" => ("C", 1, 0),
230        "C2" => ("C", 2, 0),
231        "C3" | "C3_F18C" | "C3_F20C" => ("C", 3, 0),
232        "C4" | "C4_F18C" | "C4_F20C" => ("C", 4, 0),
233        "C4_8VB" => ("C", 4, -1),
234        "C5" => ("C", 5, 0),
235        "F" | "F_F18C" | "F_19C" => ("F", 4, 0),
236        "F15mb" => ("F", 4, -2),
237        "F8vb" => ("F", 4, -1),
238        "F8va" => ("F", 4, 1),
239        "F15ma" => ("F", 4, 2),
240        "F3" => ("F", 3, 0),
241        "F5" => ("F", 5, 0),
242        "PERC" | "PERC2" => return Clef::from_string("percussion", 0),
243        "TAB" | "TAB4" | "TAB2" | "TAB4_SERIF" => return Err(refused("A tablature clef")),
244        other => return Err(mscx_error(format!("a clef of the kind {other:?}"))),
245    };
246    Clef::from_string(&format!("{sign}{line}"), octaves)
247}
248
249/// The accidental a MuseScore accidental draws, by music21's name.
250fn accidental_named(subtype: &str) -> Result<&'static str> {
251    Ok(match subtype {
252        "accidentalSharp" => "sharp",
253        "accidentalFlat" => "flat",
254        "accidentalNatural" => "natural",
255        "accidentalDoubleSharp" | "accidentalSharpSharp" => "double-sharp",
256        "accidentalDoubleFlat" => "double-flat",
257        "accidentalTripleSharp" => "triple-sharp",
258        "accidentalTripleFlat" => "triple-flat",
259        "accidentalNaturalSharp" => "natural-sharp",
260        "accidentalNaturalFlat" => "natural-flat",
261        other => return Err(refused(format!("The accidental {other:?}"))),
262    })
263}
264
265/// The spelling a tonal pitch class stands for: MuseScore's `tpc`, a place on
266/// the line of fifths where C is fourteen.
267fn spelling(tpc: i64) -> Result<(char, i64)> {
268    if !(-1..=33).contains(&tpc) {
269        return Err(mscx_error(format!("the tonal pitch class {tpc}")));
270    }
271    let step = b"FCGDAEB"[((tpc + 1) % 7) as usize] as char;
272    let alter = (tpc + 1) / 7 - 2;
273    Ok((step, alter))
274}
275
276fn natural_semitone(step: char) -> i64 {
277    match step {
278        'C' => 0,
279        'D' => 2,
280        'E' => 4,
281        'F' => 5,
282        'G' => 7,
283        'A' => 9,
284        _ => 11,
285    }
286}
287
288/// The letter and octave of a pitch, from its MIDI number and its spelling.
289fn pitch_of(midi: i64, tpc: i64) -> Result<Pitch> {
290    let (step, alter) = spelling(tpc)?;
291    let natural = midi - alter;
292    let octave = (natural - natural_semitone(step)).div_euclid(12) - 1;
293    Pitch::from_name(format!("{step}{octave}"))
294}
295
296// ------------------------------------------------------------ what is read
297
298/// A tuplet of one voice of one measure.
299#[derive(Clone, Debug)]
300struct TupletRead {
301    actual: u32,
302    normal: u32,
303    base: (DurationType, u32),
304    /// MuseScore's `bracketType`: 0 as the beams decide, 1 a bracket, 2 none.
305    bracket: i64,
306    /// MuseScore's `numberType`: 0 the number, 1 the ratio, 2 nothing.
307    number: i64,
308    placement: Option<Placement>,
309    parent: Option<usize>,
310    /// What it holds in order: notes and rests by their index among the
311    /// voice's notes, tuplets by their own.
312    elements: Vec<Held>,
313}
314
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316enum Held {
317    Note(usize),
318    Tuplet(usize),
319}
320
321#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322enum GraceKind {
323    Acciaccatura,
324    Appoggiatura,
325    Before,
326    After,
327}
328
329#[derive(Clone, Debug)]
330struct NoteRead {
331    pitch: Pitch,
332    tie_start: bool,
333    tie_stop: bool,
334    /// Which way the tie it starts curves, where the file says.
335    tie_placement: Option<Placement>,
336    /// How hard it is struck, of 127, where the file says.
337    velocity: Option<i64>,
338    hidden: bool,
339    small: bool,
340    /// The shape of its head, where it is not the usual one.
341    head: Option<Notehead>,
342    /// Whether its head is filled in, where the file fixes that.
343    head_filled: Option<bool>,
344    /// The fingerings written on it.
345    fingerings: Vec<Articulation>,
346    /// The colour it is drawn in, where that is not the usual one.
347    color: Option<String>,
348}
349
350#[derive(Clone, Debug)]
351struct LyricRead {
352    number: IntegerType,
353    syllabic: Option<Syllabic>,
354    text: String,
355}
356
357/// A note, chord or rest as it was read.
358#[derive(Clone, Debug)]
359struct ChordRest {
360    tick: Frac,
361    /// How long it lasts, in whole notes.
362    length: Frac,
363    value: Option<(DurationType, u32)>,
364    order: u8,
365    measure_rest: bool,
366    is_rest: bool,
367    tuplet: Option<usize>,
368    mode: BeamMode,
369    grace: Option<GraceKind>,
370    /// Its place among the grace notes of its note, as the file counts.
371    grace_index: usize,
372    graces_before: Vec<ChordRest>,
373    graces_after: Vec<ChordRest>,
374    notes: Vec<NoteRead>,
375    hidden: bool,
376    small: bool,
377    stem: StemDirection,
378    /// Whether it is drawn with no stem.
379    no_stem: bool,
380    /// The way the stems of the beam starting on it go, where a beam is
381    /// written out before it.
382    beam_stem: StemDirection,
383    lyrics: Vec<LyricRead>,
384    articulations: Vec<Articulation>,
385    /// Ornaments and an arpeggio, in the order written.
386    expressions: Vec<Expression>,
387    fermatas: Vec<Fermata>,
388    step_shift: IntegerType,
389    staff_move: i64,
390    /// What spanners know it by.
391    uid: usize,
392}
393
394/// A spanner as it was started: what it is, where it starts and where the
395/// file says it ends.
396#[derive(Clone, Debug)]
397struct SpannerRead {
398    kind: SpannerStart,
399    start: Position,
400    end: Position,
401}
402
403#[derive(Clone, Debug)]
404enum SpannerStart {
405    Slur(Option<Placement>, Option<&'static str>),
406    Wedge(SpannerKind),
407    Pedal(Pedal),
408    Volta { numbers: Vec<u32> },
409}
410
411/// A place in the score: a measure, a moment in it, a track.
412#[derive(Clone, Copy, Debug, PartialEq, Eq)]
413struct Position {
414    measure: i64,
415    tick: Frac,
416    track: usize,
417    /// Which of the grace notes leaning on the note there, where it is one
418    /// of them and not the note itself.
419    grace: Option<usize>,
420}
421
422#[derive(Clone, Debug)]
423enum What {
424    ChordRest(Box<ChordRest>),
425    Clef(Clef),
426    Key(StreamElement),
427    Meter(TimeSignature),
428    Element(StreamElement),
429    /// A barline, and whether a fermata stands on it.
430    Barline(Barline, bool),
431}
432
433#[derive(Clone, Debug)]
434struct Event {
435    tick: Frac,
436    what: What,
437}
438
439/// One voice of one measure of one staff.
440#[derive(Clone, Debug, Default)]
441struct VoiceRead {
442    events: Vec<Event>,
443    tuplets: Vec<TupletRead>,
444}
445
446/// One measure of one staff.
447#[derive(Clone, Debug, Default)]
448struct StaffMeasure {
449    voices: Vec<VoiceRead>,
450    len: Option<Frac>,
451    irregular: bool,
452    number_offset: i64,
453    start_repeat: bool,
454    end_repeat: Option<u32>,
455    /// Whether a section ends with it, the numbering starting again after.
456    section_break: bool,
457    /// Whether a section ended just before it.
458    starts_section: bool,
459    /// The signs and words saying where to go next: those standing at its
460    /// start, and those at its end.
461    marks_at_start: Vec<StreamElement>,
462    marks_at_end: Vec<StreamElement>,
463}
464
465/// A part, as the head of the file describes it.
466#[derive(Clone, Debug)]
467struct PartRead {
468    staves: usize,
469    /// The global index of its first staff.
470    first_staff: usize,
471    name: Option<String>,
472    abbreviation: Option<String>,
473    name_hidden: bool,
474    abbreviation_hidden: bool,
475    instrument: Instrument,
476    default_clefs: Vec<String>,
477    /// The brackets starting on its first staff, each its kind and how many
478    /// staves it joins, the innermost first.
479    brackets: Vec<(i64, usize)>,
480    /// Whether any of its staves starts a bracket.
481    bracket_found: bool,
482    /// How far the written notes stand from the sounding ones: diatonic
483    /// steps and semitones.
484    transposition: (i64, i64),
485}
486
487// ----------------------------------------------------------------- reading
488
489/// What reading a staff needs of the measures across the score.
490#[derive(Clone, Debug)]
491struct Grid {
492    /// Where each measure starts, in whole notes from the top.
493    starts: Vec<Frac>,
494    lengths: Vec<Frac>,
495    meters: Vec<(i64, i64)>,
496}
497
498struct Reader {
499    /// Whether the file is in a format older than 4.1.
500    old_format: bool,
501    /// Whether a marker is of the kind its label names. From format 4.6 on
502    /// it says its kind apart, and one that says none marks the end.
503    markers_by_label: bool,
504    next_uid: usize,
505    spanners: Vec<SpannerRead>,
506}
507
508impl Reader {
509    fn uid(&mut self) -> usize {
510        self.next_uid += 1;
511        self.next_uid
512    }
513
514    /// A staff's measures, each with its voices read.
515    fn staff(
516        &mut self,
517        staff: &Xml,
518        staff_index: usize,
519        transposition: (i64, i64),
520    ) -> Result<Vec<StaffMeasure>> {
521        let mut measures: Vec<StaffMeasure> = Vec::new();
522        let mut section_ended = false;
523        for element in &staff.children {
524            match element.tag.as_str() {
525                // The measure MuseScore draws for a run of empty ones stands
526                // in the file beside the measures it covers, which are the
527                // music.
528                "Measure" if element.find("multiMeasureRest").is_some() => {}
529                "Measure" => {
530                    let index = measures.len() as i64;
531                    let mut measure = self.measure(element, staff_index, index, transposition)?;
532                    measure.starts_section = std::mem::take(&mut section_ended);
533                    section_ended = measure.section_break;
534                    measures.push(measure);
535                }
536                // Frames hold titles and text for the page; a section may end
537                // on one.
538                "VBox" | "HBox" | "TBox" | "FBox" => {
539                    if element.find_all("LayoutBreak").any(ends_section) {
540                        section_ended = true;
541                    }
542                }
543                "eid" => {}
544                other => return Err(mscx_error(format!("<{other}> in a staff"))),
545            }
546        }
547        Ok(measures)
548    }
549
550    fn measure(
551        &mut self,
552        element: &Xml,
553        staff_index: usize,
554        index: i64,
555        transposition: (i64, i64),
556    ) -> Result<StaffMeasure> {
557        let mut measure = StaffMeasure {
558            len: element.get("len").map(fraction).transpose()?,
559            ..StaffMeasure::default()
560        };
561        let mut voice = 0usize;
562        for held in &element.children {
563            match held.tag.as_str() {
564                "voice" => {
565                    let track = staff_index * 4 + voice;
566                    let read = self.voice(held, index, track, transposition)?;
567                    measure.voices.push(read);
568                    voice += 1;
569                }
570                "irregular" => measure.irregular = held.stripped() != "0",
571                "noOffset" => measure.number_offset = integer(held)?,
572                "startRepeat" => measure.start_repeat = true,
573                "endRepeat" => measure.end_repeat = Some(integer(held)?.max(0) as u32),
574                "LayoutBreak" => {
575                    if ends_section(held) {
576                        measure.section_break = true;
577                    }
578                }
579                "measureRepeatCount" => {
580                    return Err(refused(format!("<{}>", held.tag)));
581                }
582                "Marker" if held.child_text("visible") != "0" => {
583                    match marker_of(held, self.markers_by_label)? {
584                        (false, mark) => measure.marks_at_start.push(mark),
585                        (true, mark) => measure.marks_at_end.push(mark),
586                    }
587                }
588                "Jump" if held.child_text("visible") != "0" => {
589                    measure.marks_at_end.push(jump_of(held)?);
590                }
591                "Marker" | "Jump" => {}
592                // What places the measure on the page.
593                "eid"
594                | "linkedMain"
595                | "stretch"
596                | "measureNumberMode"
597                | "breakMultiMeasureRest"
598                | "vspacer"
599                | "vspacerDown"
600                | "vspacerUp"
601                | "vspacerFixed"
602                | "visible"
603                | "stemless"
604                | "hideIfEmpty"
605                | "MeasureNumber"
606                | "MMRestRange"
607                | "SystemDivider"
608                | "Spacer"
609                | "StaffTypeChange" => {}
610                other => return Err(mscx_error(format!("<{other}> in a measure"))),
611            }
612        }
613        Ok(measure)
614    }
615
616    fn voice(
617        &mut self,
618        element: &Xml,
619        measure: i64,
620        track: usize,
621        transposition: (i64, i64),
622    ) -> Result<VoiceRead> {
623        let mut read = VoiceRead::default();
624        let mut tick = Frac::zero();
625        let mut tuplet: Option<usize> = None;
626        let mut graces: Vec<ChordRest> = Vec::new();
627        let mut fermatas: Vec<Fermata> = Vec::new();
628        let mut beam_stem = StemDirection::Unspecified;
629        let mut notes = 0usize;
630        for held in &element.children {
631            match held.tag.as_str() {
632                "location" => {
633                    if let Some(fractions) = held.find("fractions") {
634                        tick += fraction(fractions.stripped())?;
635                    }
636                    if held.find("measures").is_some() {
637                        return Err(mscx_error("a <location> in a voice that moves measures"));
638                    }
639                }
640                "Chord" | "Rest" => {
641                    let mut chord_rest =
642                        self.chord_rest(held, tick, tuplet, &read.tuplets, transposition)?;
643                    chord_rest.beam_stem =
644                        std::mem::replace(&mut beam_stem, StemDirection::Unspecified);
645                    let grace = chord_rest.grace.map(|_| graces.len());
646                    self.spanners_of(
647                        held,
648                        Position {
649                            measure,
650                            tick,
651                            track,
652                            grace,
653                        },
654                    )?;
655                    if let Some(index) = grace {
656                        chord_rest.grace_index = index;
657                        graces.push(*chord_rest);
658                        continue;
659                    }
660                    chord_rest.fermatas = std::mem::take(&mut fermatas);
661                    // Graces before the note come first, those after it
662                    // follow it.
663                    let (after, before): (Vec<ChordRest>, Vec<ChordRest>) =
664                        std::mem::take(&mut graces)
665                            .into_iter()
666                            .partition(|grace| grace.grace == Some(GraceKind::After));
667                    chord_rest.graces_before = before;
668                    // MuseScore keeps the graces after a note last first.
669                    chord_rest.graces_after = after.into_iter().rev().collect();
670                    if let Some(open) = tuplet {
671                        read.tuplets[open].elements.push(Held::Note(notes));
672                    }
673                    notes += 1;
674                    let length = chord_rest.length;
675                    read.events.push(Event {
676                        tick,
677                        what: What::ChordRest(chord_rest),
678                    });
679                    tick += length;
680                }
681                "Tuplet" => {
682                    let made = tuplet_of(held, tuplet)?;
683                    let index = read.tuplets.len();
684                    if let Some(open) = tuplet {
685                        read.tuplets[open].elements.push(Held::Tuplet(index));
686                    }
687                    read.tuplets.push(made);
688                    tuplet = Some(index);
689                }
690                "endTuplet" => {
691                    let open =
692                        tuplet.ok_or_else(|| mscx_error("an <endTuplet/> with no tuplet"))?;
693                    tuplet = read.tuplets[open].parent;
694                }
695                "Clef" => {
696                    let name = match held.find("concertClefType") {
697                        Some(clef) => clef.stripped(),
698                        None => held.child_text("subtype"),
699                    };
700                    let name = match held.find("transposingClefType") {
701                        Some(written) => written.stripped(),
702                        None => name,
703                    };
704                    read.events.push(Event {
705                        tick,
706                        what: What::Clef(clef_named(name)?),
707                    });
708                }
709                "KeySig" => {
710                    if !flag(held, "isCourtesy") {
711                        read.events.push(Event {
712                            tick,
713                            what: What::Key(key_of(held, transposition, self.old_format)?),
714                        });
715                    }
716                }
717                "TimeSig" => {
718                    if !flag(held, "isCourtesy") {
719                        read.events.push(Event {
720                            tick,
721                            what: What::Meter(meter_of(held)?),
722                        });
723                    }
724                }
725                "BarLine" => {
726                    // A fermata written before a barline stands on it.
727                    let fermata = !std::mem::take(&mut fermatas).is_empty();
728                    if let Some(barline) = barline_of(held)? {
729                        read.events.push(Event {
730                            tick,
731                            what: What::Barline(barline, fermata),
732                        });
733                    }
734                }
735                "Fermata" => fermatas.push(fermata_of(held)),
736                // A breath is taken after the note before it.
737                "Breath" => {
738                    let mark = breath_of(held)?;
739                    let before =
740                        read.events
741                            .iter_mut()
742                            .rev()
743                            .find_map(|event| match &mut event.what {
744                                What::ChordRest(before) => Some(before),
745                                _ => None,
746                            });
747                    match before {
748                        Some(before) => before.articulations.push(mark),
749                        None => return Err(mscx_error("a breath mark with no note before it")),
750                    }
751                }
752                "Sticking" | "PlayTechAnnotation" if held.child_text("visible") == "0" => {}
753                "Sticking" | "PlayTechAnnotation" => {
754                    let default = if held.tag == "Sticking" {
755                        Placement::Below
756                    } else {
757                        Placement::Above
758                    };
759                    if let Some(element) = words_of(held, default)? {
760                        read.events.push(Event {
761                            tick,
762                            what: What::Element(element),
763                        });
764                    }
765                }
766                // A mark that is hidden has nothing here to say so with, and
767                // is left out, as MuseScore leaves it out of what it
768                // exports.
769                "Dynamic" | "StaffText" | "SystemText" | "Expression" | "Text"
770                    if held.child_text("visible") == "0" => {}
771                "Dynamic" => {
772                    let mut dynamic = Dynamic::new(held.child_text("subtype"));
773                    dynamic.set_placement(Some(placement_of(held, Placement::Below)));
774                    read.events.push(Event {
775                        tick,
776                        what: What::Element(dynamic.into()),
777                    });
778                }
779                "Tempo" => {
780                    for element in tempo_of(held)? {
781                        read.events.push(Event {
782                            tick,
783                            what: What::Element(element),
784                        });
785                    }
786                }
787                "StaffText" | "SystemText" | "Expression" | "Text" => {
788                    let default = if held.tag == "Expression" {
789                        Placement::Below
790                    } else {
791                        Placement::Above
792                    };
793                    if let Some(element) = words_of(held, default)? {
794                        read.events.push(Event {
795                            tick,
796                            what: What::Element(element),
797                        });
798                    }
799                }
800                "Harmony" => {
801                    read.events.push(Event {
802                        tick,
803                        what: What::Element(harmony_of(held)?.into()),
804                    });
805                }
806                "Spanner" => self.spanner(
807                    held,
808                    Position {
809                        measure,
810                        tick,
811                        track,
812                        grace: None,
813                    },
814                )?,
815                // A beam written out says which way its stems go; which
816                // notes it joins is still the beam modes' to say.
817                "Beam" => {
818                    beam_stem = match held.child_text("StemDirection") {
819                        "up" => StemDirection::Up,
820                        "down" => StemDirection::Down,
821                        _ => StemDirection::Unspecified,
822                    };
823                }
824                // What is drawn and not played.
825                "Symbol" | "Image" | "RehearsalMark" | "StaffState" | "Ambitus" | "eid"
826                | "LayoutBreak" | "Segment" | "PlayCountText" => {}
827                other => return Err(refused(format!("A <{other}>"))),
828            }
829        }
830        if !graces.is_empty() {
831            return Err(mscx_error("grace notes with no note to lean on"));
832        }
833        Ok(read)
834    }
835
836    /// A `<Chord>` or `<Rest>`.
837    fn chord_rest(
838        &mut self,
839        element: &Xml,
840        tick: Frac,
841        tuplet: Option<usize>,
842        tuplets: &[TupletRead],
843        transposition: (i64, i64),
844    ) -> Result<Box<ChordRest>> {
845        let is_rest = element.tag == "Rest";
846        let written = element.child_text("durationType");
847        let dots = child_integer(element, "dots")?.unwrap_or(0).max(0);
848        if dots > MOST_DOTS {
849            return Err(
850                refused(format!("a note of {dots} dots")).of_kind(FormatErrorKind::TooLarge)
851            );
852        }
853        let dots = dots as u32;
854        let (value, base, order, measure_rest) = if written == "measure" {
855            let length = element
856                .find("duration")
857                .map(|duration| fraction(duration.stripped()))
858                .transpose()?
859                .ok_or_else(|| mscx_error("a measure rest that does not say how long it is"))?;
860            (None, length, 0, true)
861        } else {
862            let (kind, length, order) = duration_named(written)?;
863            (Some((kind, dots)), dotted(length, dots), order, false)
864        };
865        // A tuplet shortens what it holds by its ratio, and one inside
866        // another by both.
867        let mut length = base;
868        let mut open = tuplet;
869        while let Some(index) = open {
870            let held = &tuplets[index];
871            length = length * Frac::from_integer(i64::from(held.normal))
872                / Frac::from_integer(i64::from(held.actual));
873            open = held.parent;
874        }
875        let grace = [
876            ("acciaccatura", GraceKind::Acciaccatura),
877            ("appoggiatura", GraceKind::Appoggiatura),
878            ("grace4", GraceKind::Before),
879            ("grace16", GraceKind::Before),
880            ("grace32", GraceKind::Before),
881            ("grace8after", GraceKind::After),
882            ("grace16after", GraceKind::After),
883            ("grace32after", GraceKind::After),
884        ]
885        .into_iter()
886        .find(|(tag, _)| element.find(tag).is_some())
887        .map(|(_, kind)| kind);
888        let mut read = ChordRest {
889            tick,
890            length: if grace.is_some() {
891                Frac::zero()
892            } else {
893                length
894            },
895            value,
896            order,
897            measure_rest,
898            is_rest,
899            tuplet: if grace.is_some() { None } else { tuplet },
900            mode: BeamMode::Auto,
901            grace,
902            grace_index: 0,
903            graces_before: Vec::new(),
904            graces_after: Vec::new(),
905            notes: Vec::new(),
906            hidden: element.child_text("visible") == "0",
907            small: flag(element, "small"),
908            stem: StemDirection::Unspecified,
909            no_stem: false,
910            beam_stem: StemDirection::Unspecified,
911            lyrics: Vec::new(),
912            articulations: Vec::new(),
913            expressions: Vec::new(),
914            fermatas: Vec::new(),
915            step_shift: 0,
916            staff_move: child_integer(element, "staffMove")?.unwrap_or(0),
917            uid: self.uid(),
918        };
919        for held in &element.children {
920            match held.tag.as_str() {
921                "BeamMode" => {
922                    read.mode = BeamMode::from_name(held.stripped()).ok_or_else(|| {
923                        mscx_error(format!("the beam mode {:?}", held.stripped()))
924                    })?;
925                }
926                "StemDirection" => {
927                    read.stem = match held.stripped() {
928                        "up" => StemDirection::Up,
929                        "down" => StemDirection::Down,
930                        _ => StemDirection::Unspecified,
931                    };
932                }
933                "Stem" if held.child_text("visible") == "0" => read.no_stem = true,
934                "noStem" if held.stripped() != "0" => read.no_stem = true,
935                "Note" => read.notes.push(note_of(held, transposition)?),
936                "Lyrics" => read.lyrics.push(lyric_of(held)?),
937                "Articulation" => {
938                    if let Some(articulation) = articulation_of(held)? {
939                        read.articulations.push(articulation);
940                    }
941                }
942                "offset" if is_rest => {
943                    let y: FloatType = held.get("y").and_then(|y| y.parse().ok()).unwrap_or(0.0);
944                    let steps = -2.0 * y;
945                    read.step_shift = if steps > 0.0 {
946                        (steps + 0.5) as IntegerType
947                    } else {
948                        (steps - 0.5) as IntegerType
949                    };
950                }
951                "Ornament" => read.expressions.push(ornament_of(held)?),
952                "Arpeggio" => read.expressions.push(arpeggio_of(held)?),
953                "Tremolo" | "TremoloSingleChord" | "TremoloTwoChord" | "ChordLine" => {
954                    return Err(refused(format!("A <{}>", held.tag)));
955                }
956                // Read elsewhere, or only drawn.
957                "durationType" | "dots" | "duration" | "visible" | "small" | "staffMove"
958                | "acciaccatura" | "appoggiatura" | "grace4" | "grace16" | "grace32"
959                | "grace8after" | "grace16after" | "grace32after" | "Spanner" | "eid" | "Stem"
960                | "Hook" | "NoteDot" | "Beam" | "offset" | "linkedMain" | "stemDirection"
961                | "noStem" | "showStemSlash" | "combineVoice" | "ChordRest" | "StemSlash" => {}
962                other => return Err(refused(format!("A <{other}> in a chord"))),
963            }
964        }
965        if !is_rest && read.notes.is_empty() {
966            return Err(mscx_error("a chord with no notes"));
967        }
968        Ok(Box::new(read))
969    }
970
971    /// The spanners a note or rest starts: a slur written inside it.
972    fn spanners_of(&mut self, element: &Xml, here: Position) -> Result<()> {
973        for held in element.find_all("Spanner") {
974            self.spanner(held, here)?;
975        }
976        Ok(())
977    }
978
979    /// A `<Spanner>`: where one starts, with where it ends; the end itself
980    /// says nothing that the start did not.
981    fn spanner(&mut self, element: &Xml, here: Position) -> Result<()> {
982        let kind = element.get("type").unwrap_or("");
983        let Some(next) = element.find("next") else {
984            return Ok(());
985        };
986        let body = element.find(kind);
987        if matches!(kind, "HairPin" | "Pedal")
988            && body.is_some_and(|body| body.child_text("visible") == "0")
989        {
990            return Ok(());
991        }
992        let start = SpannerStart::from(kind, body)?;
993        let Some(start) = start else {
994            return Ok(());
995        };
996        let end = moved(here, next.find("location"))?;
997        self.spanners.push(SpannerRead {
998            kind: start,
999            start: here,
1000            end,
1001        });
1002        Ok(())
1003    }
1004}
1005
1006impl SpannerStart {
1007    fn from(kind: &str, body: Option<&Xml>) -> Result<Option<Self>> {
1008        Ok(Some(match kind {
1009            "Slur" => Self::Slur(
1010                match body.map_or("", |body| body.child_text("up")) {
1011                    "up" => Some(Placement::Above),
1012                    "down" => Some(Placement::Below),
1013                    _ => None,
1014                },
1015                match body.map_or("", |body| body.child_text("lineType")) {
1016                    "1" => Some("dotted"),
1017                    "2" | "3" => Some("dashed"),
1018                    _ => None,
1019                },
1020            ),
1021            "HairPin" => {
1022                let subtype = body.map_or("0", |body| body.child_text("subtype"));
1023                match subtype {
1024                    "0" | "" => Self::Wedge(SpannerKind::Crescendo),
1025                    "1" => Self::Wedge(SpannerKind::Diminuendo),
1026                    other => {
1027                        return Err(refused(format!("A hairpin of the kind {other}")));
1028                    }
1029                }
1030            }
1031            "Volta" => {
1032                let body = body.ok_or_else(|| mscx_error("a volta that says nothing"))?;
1033                let numbers = body
1034                    .child_text("endings")
1035                    .split(',')
1036                    .map(|number| number.trim().parse::<u32>())
1037                    .collect::<std::result::Result<Vec<u32>, _>>()
1038                    .map_err(|_| {
1039                        mscx_error(format!(
1040                            "the volta endings {:?}",
1041                            body.child_text("endings")
1042                        ))
1043                    })?;
1044                Self::Volta { numbers }
1045            }
1046            "Pedal" => {
1047                let body = body.ok_or_else(|| mscx_error("a pedal line that says nothing"))?;
1048                if body.find("beginHookType").is_some() {
1049                    return Err(refused("A pedal line that starts with a hook"));
1050                }
1051                let text = body.find("beginText").map(Xml::all_text);
1052                if text.as_deref() == Some("") {
1053                    return Err(refused("A pedal line carried on from another"));
1054                }
1055                let sostenuto = body
1056                    .find("beginText")
1057                    .and_then(|text| text.find("sym"))
1058                    .is_some_and(|sym| {
1059                        matches!(sym.stripped(), "keyboardPedalSost" | "keyboardPedalS")
1060                    });
1061                Self::Pedal(Pedal {
1062                    pedal_type: Some(if sostenuto {
1063                        PedalType::Sostenuto
1064                    } else {
1065                        PedalType::Sustain
1066                    }),
1067                    form: Some(if body.child_text("lineVisible") == "0" {
1068                        PedalForm::Symbol
1069                    } else {
1070                        PedalForm::Line
1071                    }),
1072                    abbreviated: false,
1073                })
1074            }
1075            // A tie is read on its note.
1076            "Tie" | "LaissezVib" | "PartialTie" => return Ok(None),
1077            other => return Err(refused(format!("A {other} spanner"))),
1078        }))
1079    }
1080}
1081
1082/// A position moved by a relative `<location>`.
1083fn moved(from: Position, location: Option<&Xml>) -> Result<Position> {
1084    // A place names a grace note outright, and the note itself by naming
1085    // none.
1086    let mut to = Position {
1087        grace: None,
1088        ..from
1089    };
1090    let Some(location) = location else {
1091        return Ok(to);
1092    };
1093    to.grace = child_integer(location, "grace")?.and_then(|index| usize::try_from(index).ok());
1094    if let Some(measures) = child_integer(location, "measures")? {
1095        to.measure += measures;
1096    }
1097    if let Some(fractions) = location.find("fractions") {
1098        to.tick += fraction(fractions.stripped())?;
1099    }
1100    let staves = child_integer(location, "staves")?.unwrap_or(0);
1101    let voices = child_integer(location, "voices")?.unwrap_or(0);
1102    let track = from.track as i64 + staves * 4 + voices;
1103    to.track = usize::try_from(track).map_err(|_| mscx_error("a spanner ending on no staff"))?;
1104    Ok(to)
1105}
1106
1107fn tuplet_of(element: &Xml, parent: Option<usize>) -> Result<TupletRead> {
1108    let count = |tag: &str| -> Result<u32> {
1109        let value = child_integer(element, tag)?
1110            .ok_or_else(|| mscx_error(format!("a tuplet with no <{tag}>")))?;
1111        u32::try_from(value)
1112            .ok()
1113            .filter(|value| *value > 0)
1114            .ok_or_else(|| mscx_error(format!("a tuplet of {value}")))
1115    };
1116    let base_name = element.child_text("baseNote");
1117    let (kind, _, _) = duration_named(base_name)?;
1118    let base_dots = child_integer(element, "baseDots")?.unwrap_or(0).max(0) as u32;
1119    let placement = match element.child_text("direction") {
1120        "up" => Some(Placement::Above),
1121        "down" => Some(Placement::Below),
1122        _ => None,
1123    };
1124    Ok(TupletRead {
1125        actual: count("actualNotes")?,
1126        normal: count("normalNotes")?,
1127        base: (kind, base_dots),
1128        bracket: child_integer(element, "bracketType")?.unwrap_or(0),
1129        number: child_integer(element, "numberType")?.unwrap_or(0),
1130        placement,
1131        parent,
1132        elements: Vec::new(),
1133    })
1134}
1135
1136fn note_of(element: &Xml, transposition: (i64, i64)) -> Result<NoteRead> {
1137    let midi =
1138        child_integer(element, "pitch")?.ok_or_else(|| mscx_error("a note with no pitch"))?;
1139    let concert = child_integer(element, "tpc")?.ok_or_else(|| mscx_error("a note with no tpc"))?;
1140    // A transposing instrument's notes are written as it reads them.
1141    let written_tpc = child_integer(element, "tpc2")?.unwrap_or(concert);
1142    let (tpc, midi) = if transposition == (0, 0) {
1143        (concert, midi)
1144    } else {
1145        (written_tpc, midi - transposition.1)
1146    };
1147    let mut pitch = pitch_of(midi, tpc)?;
1148    let (_, alter) = spelling(tpc)?;
1149    match element.find("Accidental") {
1150        Some(written) => {
1151            let name = accidental_named(written.child_text("subtype"))?;
1152            let mut accidental = Accidental::natural();
1153            accidental.set_allowing_non_standard_value(name)?;
1154            accidental.set_display_status(Some(true));
1155            match written.child_text("bracket") {
1156                "1" => accidental.set_display_style("parentheses")?,
1157                "2" => accidental.set_display_style("bracket")?,
1158                _ => {}
1159            }
1160            pitch.set_written_accidental(Some(accidental));
1161        }
1162        None if alter != 0 => {
1163            let mut accidental = Accidental::new(alter as FloatType)?;
1164            accidental.set_display_status(Some(false));
1165            pitch.set_written_accidental(Some(accidental));
1166        }
1167        None => {}
1168    }
1169    let mut read = NoteRead {
1170        pitch,
1171        tie_start: false,
1172        tie_stop: false,
1173        tie_placement: None,
1174        velocity: child_integer(element, "velocity")?.filter(|velocity| *velocity != 0),
1175        hidden: element.child_text("visible") == "0",
1176        small: flag(element, "small"),
1177        head: None,
1178        head_filled: None,
1179        fingerings: Vec::new(),
1180        color: None,
1181    };
1182    for held in &element.children {
1183        match held.tag.as_str() {
1184            "head" => read.head = head_named(held.stripped())?,
1185            "headType" => {
1186                read.head_filled = match held.stripped() {
1187                    "quarter" => Some(true),
1188                    "half" | "whole" => Some(false),
1189                    "auto" | "breve" => None,
1190                    other => return Err(refused(format!("A notehead of the kind {other:?}"))),
1191                };
1192            }
1193            "Fingering" => {
1194                if let Some(fingering) = fingering_of(held)? {
1195                    read.fingerings.push(fingering);
1196                }
1197            }
1198            "color" => read.color = Some(color_of(held)?),
1199            // Shape notes are drawn by the degree of the scale a note is.
1200            "headScheme" => {
1201                if !matches!(held.stripped(), "auto" | "normal") {
1202                    return Err(refused("A note drawn as a shape note"));
1203                }
1204            }
1205            "tuning" => {
1206                if held
1207                    .stripped()
1208                    .parse::<FloatType>()
1209                    .is_ok_and(|cents| cents != 0.0)
1210                {
1211                    return Err(refused("A note tuned away from its pitch"));
1212                }
1213            }
1214            "Spanner" => match held.get("type") {
1215                Some("Tie" | "LaissezVib" | "PartialTie") => {}
1216                Some(other) => return Err(refused(format!("A {other} on a note"))),
1217                None => {}
1218            },
1219            // Read above or below, or only drawn or played.
1220            "pitch" | "tpc" | "tpc2" | "Accidental" | "Tie" | "endSpanner" | "visible"
1221            | "small" | "velocity" | "veloType" | "veloOffset" | "play" | "eid" | "NoteDot"
1222            | "Events" | "mirror" | "dotPosition" | "fixed" | "fixedLine" | "offset" | "linked"
1223            | "linkedMain" | "track" | "Symbol" | "fret" | "string" | "ghost" | "dead" | "z"
1224            | "autoplace" | "LaissezVib" | "PartialTie" => {}
1225            other => return Err(refused(format!("A <{other}> on a note"))),
1226        }
1227    }
1228    for held in element.find_all("Spanner") {
1229        if held.get("type") == Some("Tie") {
1230            if held.find("next").is_some() {
1231                read.tie_start = true;
1232                read.tie_placement = match held.find("Tie").map(|tie| tie.child_text("up")) {
1233                    Some("up") => Some(Placement::Above),
1234                    Some("down") => Some(Placement::Below),
1235                    _ => None,
1236                };
1237            }
1238            if held.find("prev").is_some() {
1239                read.tie_stop = true;
1240            }
1241        }
1242    }
1243    // MuseScore 3 wrote a tie as a `<Tie>` on the note it starts from.
1244    if element.find("Tie").is_some() {
1245        read.tie_start = true;
1246    }
1247    if element.find("endSpanner").is_some() {
1248        read.tie_stop = true;
1249    }
1250    Ok(read)
1251}
1252
1253/// The shape of a notehead by MuseScore's name for it; nothing for the
1254/// usual one.
1255fn head_named(name: &str) -> Result<Option<Notehead>> {
1256    let written = match name {
1257        "normal" => return Ok(None),
1258        "cross" => "x",
1259        "plus" => "cross",
1260        "xcircle" => "circle-x",
1261        "circled" => "circled",
1262        "triangle-up" => "triangle",
1263        "triangle-down" => "inverted triangle",
1264        "slashed1" => "slashed",
1265        "slashed2" => "back slashed",
1266        "diamond" | "slash" | "do" | "re" | "mi" | "la" | "ti" => name,
1267        "sol" => "so",
1268        other => return Err(refused(format!("A notehead of the shape {other:?}"))),
1269    };
1270    Notehead::from_name(written).map(Some)
1271}
1272
1273/// A colour as MuseScore's export writes one.
1274fn color_of(element: &Xml) -> Result<String> {
1275    let part = |name: &str| -> Result<i64> {
1276        element
1277            .get(name)
1278            .and_then(|text| text.parse::<i64>().ok())
1279            .filter(|value| (0..=255).contains(value))
1280            .ok_or_else(|| mscx_error(format!("a colour with no {name}")))
1281    };
1282    if part("a")? != 255 {
1283        return Err(refused("A colour that is partly transparent"));
1284    }
1285    Ok(format!(
1286        "#{:02X}{:02X}{:02X}",
1287        part("r")?,
1288        part("g")?,
1289        part("b")?
1290    ))
1291}
1292
1293/// A fingering written on a note: a finger, a plucking finger, or the
1294/// string it is played on.
1295fn fingering_of(element: &Xml) -> Result<Option<Articulation>> {
1296    if element.child_text("visible") == "0" {
1297        return Ok(None);
1298    }
1299    let text = element
1300        .find("text")
1301        .map(plain_text)
1302        .transpose()?
1303        .unwrap_or_default();
1304    let text = text.trim();
1305    let style = element.child_text("style");
1306    let made = |class: &str| {
1307        ArticulationKind::from_class_name(class)
1308            .map(Articulation::of_kind)
1309            .ok_or_else(|| mscx_error(format!("no articulation {class}")))
1310    };
1311    let plucked = matches!(text, "p" | "i" | "m" | "a" | "c");
1312    Ok(Some(match style {
1313        "String Number" | "string_number" => match text.parse::<IntegerType>() {
1314            Ok(0) => made("OpenString")?,
1315            Ok(number) if number > 0 => {
1316                let mut string = made("StringIndication")?;
1317                string.set_number(number);
1318                string
1319            }
1320            _ => return Ok(None),
1321        },
1322        "RH Guitar Fingering" | "guitar_fingering_rh" => made("FrettedPluck")?,
1323        "" | "Fingering" | "fingering" if plucked => made("FrettedPluck")?,
1324        "" | "Fingering" | "fingering" | "LH Guitar Fingering" | "guitar_fingering_lh" => {
1325            let mut fingering = made("Fingering")?;
1326            fingering.set_finger(Some(Finger::from_written(text)));
1327            fingering
1328        }
1329        other => return Err(refused(format!("A fingering in the style {other:?}"))),
1330    }))
1331}
1332
1333/// A breath mark or a caesura.
1334fn breath_of(element: &Xml) -> Result<Articulation> {
1335    let symbol = element.child_text("symbol");
1336    let made = |class: &str| {
1337        ArticulationKind::from_class_name(class)
1338            .map(Articulation::of_kind)
1339            .ok_or_else(|| mscx_error(format!("no articulation {class}")))
1340    };
1341    let mut mark = if symbol.contains("aesura") {
1342        made("Caesura")?
1343    } else {
1344        let mut breath = made("BreathMark")?;
1345        breath.set_symbol(Some(
1346            match symbol {
1347                "breathMarkTick" => "tick",
1348                "breathMarkUpbow" => "upbow",
1349                "breathMarkSalzedo" => "salzedo",
1350                _ => "comma",
1351            }
1352            .to_string(),
1353        ));
1354        breath
1355    };
1356    if element.child_text("placement") == "below" {
1357        mark.set_placement(Some("below".to_string()));
1358    }
1359    Ok(mark)
1360}
1361
1362fn lyric_of(element: &Xml) -> Result<LyricRead> {
1363    let number = child_integer(element, "no")?.unwrap_or(0) + 1;
1364    let syllabic = match element.child_text("syllabic") {
1365        "" => None,
1366        written => Some(Syllabic::from_name(written)?),
1367    };
1368    Ok(LyricRead {
1369        number: number as IntegerType,
1370        syllabic,
1371        text: element
1372            .find("text")
1373            .map(plain_text)
1374            .transpose()?
1375            .unwrap_or_default(),
1376    })
1377}
1378
1379fn articulation_of(element: &Xml) -> Result<Option<Articulation>> {
1380    let subtype = element.child_text("subtype");
1381    let base = subtype
1382        .strip_suffix("Above")
1383        .or_else(|| subtype.strip_suffix("Below"))
1384        .unwrap_or(subtype);
1385    let class = match base {
1386        "articAccent" => "Accent",
1387        "articStaccato" => "Staccato",
1388        "articStaccatissimo" | "articStaccatissimoWedge" => "Staccatissimo",
1389        "articStaccatissimoStroke" => "Spiccato",
1390        "articTenuto" => "Tenuto",
1391        "articMarcato" => "StrongAccent",
1392        "articTenutoStaccato" => "DetachedLegato",
1393        "articStress" => "Stress",
1394        "articUnstress" => "Unstress",
1395        "stringsUpBow" => "UpBow",
1396        "stringsDownBow" => "DownBow",
1397        "stringsHarmonic" => "StringHarmonic",
1398        "brassMuteOpen" => "OpenString",
1399        "stringsThumbPosition" => "StringThumbPosition",
1400        "brassMuteClosed" => "Stopped",
1401        "pluckedSnapPizzicatoAbove" | "pluckedSnapPizzicato" => "SnapPizzicato",
1402        other => return Err(refused(format!("The articulation {other:?}"))),
1403    };
1404    let kind = ArticulationKind::from_class_name(class)
1405        .ok_or_else(|| mscx_error(format!("no articulation {class}")))?;
1406    let mut articulation = Articulation::of_kind(kind);
1407    if class == "StrongAccent" {
1408        let up = !subtype.ends_with("Below");
1409        articulation.set_point_direction(Some(if up { "up" } else { "down" }.to_string()));
1410    } else {
1411        // Above, below, or wherever it falls; any other number is an older
1412        // file's, which MuseScore reads as below.
1413        match element.child_text("anchor") {
1414            "" | "2" => {}
1415            "0" => articulation.set_placement(Some("above".to_string())),
1416            _ => articulation.set_placement(Some("below".to_string())),
1417        }
1418    }
1419    Ok(Some(articulation))
1420}
1421
1422/// Whether a `<LayoutBreak>` ends a section, after which measures are
1423/// numbered from one again.
1424fn ends_section(element: &Xml) -> bool {
1425    element.child_text("subtype") == "section" && element.child_text("startWithMeasureOne") != "0"
1426}
1427
1428/// Words saying where to go next: a repeat mark where they are ones music21
1429/// reads as one, and plain words otherwise.
1430fn repeat_words(text: &str) -> StreamElement {
1431    let repeat = RepeatExpressionKind::ALL
1432        .into_iter()
1433        .find(|kind| RepeatExpression::new(*kind).is_valid_text(text));
1434    match repeat {
1435        Some(kind) => {
1436            let mut mark = RepeatExpression::new(kind);
1437            mark.set_text(text);
1438            // The words are placed, not the mark: one drawn as its sign, a
1439            // coda or a segno, stands where a sign stands.
1440            if !mark.use_symbol() {
1441                mark.set_placement(Some(Placement::Above));
1442            }
1443            mark.into()
1444        }
1445        None => {
1446            let mut words = TextExpression::new(text);
1447            words.set_placement(Some(Placement::Above));
1448            words.into()
1449        }
1450    }
1451}
1452
1453/// A `<Marker>`: a segno or a coda sign, which stands at the start of its
1454/// measure, or *Fine* or *To Coda*, which stand at its end. The first
1455/// answer is whether it stands at the end.
1456fn marker_of(element: &Xml, by_label: bool) -> Result<(bool, StreamElement)> {
1457    let sign = |kind: RepeatExpressionKind| (false, RepeatExpression::new(kind).into());
1458    let text = element
1459        .find("text")
1460        .map(plain_text)
1461        .transpose()?
1462        .unwrap_or_default();
1463    let kind = match element.child_text("markerType") {
1464        "" if by_label => element.child_text("label"),
1465        "" => "fine",
1466        kind => kind,
1467    };
1468    Ok(match kind {
1469        "segno" | "varsegno" => sign(RepeatExpressionKind::Segno),
1470        "codab" | "varcoda" | "codetta" => sign(RepeatExpressionKind::Coda),
1471        "fine" if !text.trim().is_empty() => (true, repeat_words(text.trim())),
1472        "coda" | "codasym" | "dacoda" | "dadblcoda" => (
1473            true,
1474            repeat_words(if text.trim().is_empty() {
1475                "To Coda"
1476            } else {
1477                text.trim()
1478            }),
1479        ),
1480        other => return Err(refused(format!("A marker of the kind {other:?}"))),
1481    })
1482}
1483
1484/// A `<Jump>`: *D.C.*, *D.S.* and their kin, in the words written or else
1485/// the words MuseScore has for where the jump goes.
1486fn jump_of(element: &Xml) -> Result<StreamElement> {
1487    let text = element
1488        .find("text")
1489        .map(plain_text)
1490        .transpose()?
1491        .unwrap_or_default();
1492    let text = text.trim();
1493    let to = (
1494        element.child_text("jumpTo"),
1495        element.child_text("playUntil"),
1496        element.child_text("continueAt"),
1497    );
1498    let words = match to {
1499        ("segno", "end", "") => "D.S.",
1500        _ if !text.is_empty() => text,
1501        ("start", "end", "") => "D.C.",
1502        ("start", "fine", "") => "D.C. al Fine",
1503        ("start", "coda", "codab") => "D.C. al Coda",
1504        ("segno", "coda", "codab") => "D.S. al Coda",
1505        ("segno", "fine", "") => "D.S. al Fine",
1506        _ => return Err(refused("A jump that does not say in words where it goes")),
1507    };
1508    Ok(repeat_words(words))
1509}
1510
1511fn ornament_of(element: &Xml) -> Result<Expression> {
1512    if element.find("Accidental").is_some() {
1513        return Err(refused("An ornament with an accidental"));
1514    }
1515    let subtype = element.child_text("subtype");
1516    let (class, turn) = match subtype {
1517        "ornamentTrill" => ("Trill", false),
1518        "ornamentTurn" => ("Turn", true),
1519        "ornamentTurnInverted" => ("InvertedTurn", true),
1520        "ornamentMordent"
1521        | "ornamentPrallMordent"
1522        | "ornamentUpMordent"
1523        | "ornamentDownMordent" => ("Mordent", false),
1524        "ornamentShortTrill"
1525        | "ornamentTremblement"
1526        | "ornamentUpPrall"
1527        | "ornamentPrecompMordentUpperPrefix"
1528        | "ornamentPrallDown"
1529        | "ornamentPrallUp"
1530        | "ornamentLinePrall" => ("InvertedMordent", false),
1531        "ornamentPrecompSlide" => ("Schleifer", false),
1532        other => return Err(refused(format!("The ornament {other:?}"))),
1533    };
1534    let kind = OrnamentKind::from_class_name(class)
1535        .ok_or_else(|| mscx_error(format!("no ornament {class}")))?;
1536    let mut ornament = Ornament::of_kind(kind);
1537    if turn {
1538        ornament.set_delay(OrnamentDelay::NoDelay);
1539    }
1540    Ok(ornament.into())
1541}
1542
1543/// An arpeggio on one chord. One drawn across staves joins chords, which
1544/// is not read.
1545fn arpeggio_of(element: &Xml) -> Result<Expression> {
1546    if child_integer(element, "span")?.is_some_and(|span| span > 1) {
1547        return Err(refused("An arpeggio across staves"));
1548    }
1549    Ok(Expression::Arpeggio(match element.child_text("subtype") {
1550        "0" | "" | "arpeggiato" => ArpeggioType::Normal,
1551        "1" | "4" | "up" | "upStraight" => ArpeggioType::Up,
1552        "2" | "5" | "down" | "downStraight" => ArpeggioType::Down,
1553        "3" | "bracket" => ArpeggioType::NonArpeggio,
1554        other => return Err(refused(format!("An arpeggio of the kind {other:?}"))),
1555    }))
1556}
1557
1558fn fermata_of(element: &Xml) -> Fermata {
1559    let mut fermata = Fermata::new();
1560    let subtype = element.child_text("subtype");
1561    if subtype.ends_with("Below") {
1562        fermata.set_fermata_type(FermataType::Inverted);
1563    } else {
1564        fermata.set_fermata_type(FermataType::Upright);
1565    }
1566    let shape = subtype
1567        .strip_suffix("Above")
1568        .or_else(|| subtype.strip_suffix("Below"))
1569        .unwrap_or(subtype);
1570    let shape = match shape {
1571        "fermata" => None,
1572        "fermataShort" => Some("angled"),
1573        "fermataLong" => Some("square"),
1574        "fermataVeryShort" => Some("double-angled"),
1575        "fermataVeryLong" => Some("double-square"),
1576        "fermataLongHenze" => Some("double-dot"),
1577        "fermataShortHenze" => Some("half-curve"),
1578        _ => None,
1579    };
1580    fermata.set_shape(shape.map(str::to_string));
1581    fermata
1582}
1583
1584/// Where a mark is placed: what it says, else its default.
1585fn placement_of(element: &Xml, default: Placement) -> Placement {
1586    match element.child_text("placement") {
1587        "above" => Placement::Above,
1588        "below" => Placement::Below,
1589        _ => match element.child_text("direction") {
1590            "up" => Placement::Above,
1591            "down" => Placement::Below,
1592            _ => default,
1593        },
1594    }
1595}
1596
1597/// A key signature as it is written, for a transposing instrument in the
1598/// key it reads.
1599fn key_of(element: &Xml, transposition: (i64, i64), old_format: bool) -> Result<StreamElement> {
1600    if flag(element, "custom") || element.find("KeySym").is_some() {
1601        return Err(refused("A key signature of its own making"));
1602    }
1603    // Before format 4.1 a signature said the key its staff is written in,
1604    // as `<accidental>`; since then it says the sounding key, and the
1605    // written one beside it where a transposing instrument has another.
1606    // MuseScore reads each format by its own rule and so does this.
1607    let mut sharps = if old_format {
1608        child_integer(element, "accidental")?.unwrap_or(0)
1609    } else {
1610        let concert = child_integer(element, "concertKey")?.unwrap_or(0);
1611        match child_integer(element, "actualKey")? {
1612            Some(actual) => actual,
1613            None => concert - 7 * transposition.1 + 12 * transposition.0,
1614        }
1615    };
1616    while sharps > 7 {
1617        sharps -= 12;
1618    }
1619    while sharps < -7 {
1620        sharps += 12;
1621    }
1622    let signature = KeySignature::new(sharps as IntegerType);
1623    let mode = element.child_text("mode");
1624    if !mode.is_empty()
1625        && mode != "none"
1626        && mode != "unknown"
1627        && let Ok(key) = signature.try_as_key(Some(mode), None)
1628    {
1629        return Ok(key.into());
1630    }
1631    Ok(signature.into())
1632}
1633
1634fn meter_of(element: &Xml) -> Result<TimeSignature> {
1635    let numerator =
1636        child_integer(element, "sigN")?.ok_or_else(|| mscx_error("a meter with no <sigN>"))?;
1637    let denominator =
1638        child_integer(element, "sigD")?.ok_or_else(|| mscx_error("a meter with no <sigD>"))?;
1639    let written = element.child_text("textN");
1640    let ratio = if written.is_empty() {
1641        format!("{numerator}/{denominator}")
1642    } else {
1643        format!("{written}/{denominator}")
1644    };
1645    let mut meter = TimeSignature::from_ratio_string(&ratio)
1646        .map_err(|_| mscx_error(format!("the meter {ratio}")))?;
1647    match element.child_text("subtype") {
1648        "1" => meter.set_symbol(Some("common".to_string())),
1649        "2" => meter.set_symbol(Some("cut".to_string())),
1650        _ => {}
1651    }
1652    Ok(meter)
1653}
1654
1655/// A barline written in a voice. A repeat is not one: that is the
1656/// measure's.
1657fn barline_of(element: &Xml) -> Result<Option<Barline>> {
1658    if element.child_text("visible") == "0" {
1659        return Ok(Some(Barline::new(BarlineType::None)));
1660    }
1661    let kind = match element.child_text("subtype") {
1662        "normal" | "" => BarlineType::Regular,
1663        "double" => BarlineType::Double,
1664        "end" => BarlineType::Final,
1665        "reverse-end" => BarlineType::HeavyLight,
1666        "heavy" => BarlineType::Heavy,
1667        "double-heavy" => BarlineType::HeavyHeavy,
1668        "dashed" => BarlineType::Dashed,
1669        "dotted" => BarlineType::Dotted,
1670        "start-repeat" | "end-repeat" | "end-start-repeat" => return Ok(None),
1671        other => return Err(mscx_error(format!("a barline of the kind {other:?}"))),
1672    };
1673    Ok(Some(Barline::new(kind)))
1674}
1675
1676/// A tempo mark: the mark, and whatever words stand beside it.
1677fn tempo_of(element: &Xml) -> Result<Vec<StreamElement>> {
1678    let per_second: FloatType = element
1679        .child_text("tempo")
1680        .parse()
1681        .map_err(|_| mscx_error("a tempo that is not a number"))?;
1682    let bpm = (per_second * 60.0 * 100.0).round() / 100.0;
1683    if element.child_text("visible") == "0" {
1684        return Ok(vec![
1685            MetronomeMark::default().with_number_sounding(bpm).into(),
1686        ]);
1687    }
1688    let placement = placement_of(element, Placement::Above);
1689    let text = element.find("text");
1690    let found = text.and_then(metronome_in);
1691    let mut out: Vec<StreamElement> = Vec::new();
1692    match found {
1693        Some((left, referent, number)) => {
1694            if !left.trim().is_empty() {
1695                let mut words = TextExpression::new(left.trim());
1696                words.set_placement(Some(placement));
1697                out.push(words.into());
1698            }
1699            let mut mark = MetronomeMark::default();
1700            mark.set_number(Some(number));
1701            mark.set_referent(referent);
1702            mark.set_placement(Some(placement));
1703            out.push(mark.into());
1704        }
1705        None => {
1706            if let Some(text) = text {
1707                let written = plain_text(text)?;
1708                if !written.trim().is_empty() {
1709                    let mut words = TextExpression::new(written.trim());
1710                    words.set_placement(Some(placement));
1711                    out.push(words.into());
1712                }
1713            }
1714            let mut mark = MetronomeMark::default().with_number_sounding(bpm);
1715            mark.set_placement(Some(placement));
1716            out.push(mark.into());
1717        }
1718    }
1719    Ok(out)
1720}
1721
1722/// The metronome mark in a tempo's text: the words before it, the note it
1723/// counts and how many to the minute.
1724fn metronome_in(text: &Xml) -> Option<(String, Duration, FloatType)> {
1725    let mut before = String::new();
1726    if let Some(leading) = text.text() {
1727        before.push_str(leading);
1728    }
1729    for (index, child) in text.children.iter().enumerate() {
1730        if child.tag == "sym" {
1731            let name = child.text().unwrap_or("").trim();
1732            let referent = match name {
1733                "metNoteWhole" => Duration::from_type(DurationType::Whole),
1734                "metNoteHalfUp" => Duration::from_type(DurationType::Half),
1735                "metNoteQuarterUp" => Duration::from_type(DurationType::Quarter),
1736                "metNote8thUp" => Duration::from_type(DurationType::Eighth),
1737                "metNote16thUp" => Duration::from_type(DurationType::Sixteenth),
1738                _ => return None,
1739            };
1740            let mut referent = referent;
1741            let mut rest = String::new();
1742            let mut dots = 0;
1743            if let Some(tail) = child.tail() {
1744                rest.push_str(tail);
1745            }
1746            for later in &text.children[index + 1..] {
1747                if later.tag == "sym" && later.text().unwrap_or("").trim() == "metAugmentationDot" {
1748                    dots += 1;
1749                } else {
1750                    rest.push_str(&plain_text(later).ok()?);
1751                }
1752                if let Some(tail) = later.tail() {
1753                    rest.push_str(tail);
1754                }
1755            }
1756            if dots > 0
1757                && let Some((kind, _)) = referent.type_and_dots()
1758            {
1759                referent = Duration::from_type_with_dots(kind, dots);
1760            }
1761            let rest = rest.trim_start();
1762            let rest = rest.strip_prefix('=')?.trim_start();
1763            let digits: String = rest
1764                .chars()
1765                .take_while(|c| c.is_ascii_digit() || *c == '.' || *c == ',')
1766                .collect();
1767            let number: FloatType = digits.replace(',', ".").parse().ok()?;
1768            let before = before.trim_end_matches('(').to_string();
1769            return Some((before, referent, number));
1770        }
1771        before.push_str(&plain_text(child).ok()?);
1772        if let Some(tail) = child.tail() {
1773            before.push_str(tail);
1774        }
1775    }
1776    None
1777}
1778
1779/// Words written over or under the staff; words saying where to go next
1780/// are a repeat mark.
1781fn words_of(element: &Xml, default: Placement) -> Result<Option<StreamElement>> {
1782    let text = element
1783        .find("text")
1784        .map(plain_text)
1785        .transpose()?
1786        .unwrap_or_default();
1787    let text = text.trim();
1788    if text.is_empty() {
1789        return Ok(None);
1790    }
1791    let placement = placement_of(element, default);
1792    let repeat = RepeatExpressionKind::ALL
1793        .into_iter()
1794        .find(|kind| RepeatExpression::new(*kind).is_valid_text(text));
1795    Ok(Some(match repeat {
1796        Some(kind) => {
1797            let mut mark = RepeatExpression::new(kind);
1798            mark.set_text(text);
1799            if !mark.use_symbol() {
1800                mark.set_placement(Some(placement));
1801            }
1802            mark.into()
1803        }
1804        None => {
1805            let mut words = TextExpression::new(text);
1806            words.set_placement(Some(placement));
1807            words.into()
1808        }
1809    }))
1810}
1811
1812/// A chord symbol: its root and bass as tonal pitch classes, and the name
1813/// of its kind as written, which is read as music21 reads a figure.
1814fn harmony_of(element: &Xml) -> Result<ChordSymbol> {
1815    let info = element.find("harmonyInfo").unwrap_or(element);
1816    let name = info.child_text("name");
1817    let Some(root) = child_integer(info, "root")? else {
1818        return Err(refused("A chord symbol with no root"));
1819    };
1820    let pitch = |tpc: i64| -> Result<(Pitch, String)> {
1821        let (step, alter) = spelling(tpc)?;
1822        let mut pitch = Pitch::from_name(step.to_string())?;
1823        if alter != 0 {
1824            pitch.set_written_accidental(Some(Accidental::new(alter as FloatType)?));
1825        }
1826        let written = match alter {
1827            -2 => "--",
1828            -1 => "-",
1829            1 => "#",
1830            2 => "##",
1831            _ => "",
1832        };
1833        Ok((pitch, format!("{step}{written}")))
1834    };
1835    let (root, root_name) = pitch(root)?;
1836    let bass = match child_integer(info, "bass")? {
1837        Some(bass) => Some(bass),
1838        None => child_integer(info, "base")?,
1839    }
1840    .map(pitch)
1841    .transpose()?;
1842    let mut figure = format!("{root_name}{name}");
1843    if let Some((_, bass_name)) = &bass {
1844        figure.push('/');
1845        figure.push_str(bass_name);
1846    }
1847    let unread = || refused(format!("The chord symbol {figure:?}"));
1848    let parsed = ChordSymbol::parse_music21(&figure).map_err(|_| unread())?;
1849    let kind = parsed
1850        .kind()
1851        .filter(|kind| !kind.is_empty())
1852        .ok_or_else(unread)?
1853        .to_string();
1854    // The text of the kind itself: the name up to where its added and
1855    // altered degrees begin.
1856    let mut kind_text = name;
1857    if !parsed.chord_step_modifications().is_empty() {
1858        kind_text = "";
1859        for (end, _) in name.char_indices().skip(1).chain([(name.len(), ' ')]) {
1860            let alone = ChordSymbol::parse_music21(format!("{root_name}{}", &name[..end]));
1861            if let Ok(alone) = alone
1862                && alone.kind() == Some(kind.as_str())
1863                && alone.chord_step_modifications().is_empty()
1864            {
1865                kind_text = &name[..end];
1866            }
1867        }
1868    }
1869    let mut symbol =
1870        ChordSymbol::from_kind(root, &kind, bass.map(|(pitch, _)| pitch)).map_err(|_| unread())?;
1871    symbol.set_kind_text(Some(kind_text.to_string()));
1872    symbol.set_chord_step_modifications(parsed.chord_step_modifications().to_vec());
1873    Ok(symbol)
1874}
1875
1876// ---------------------------------------------------------------- the head
1877
1878fn metadata_of(score: &Xml, program: &str) -> Metadata {
1879    let mut tags: Vec<(String, String)> = score
1880        .find_all("metaTag")
1881        .filter_map(|tag| {
1882            let name = tag.get("name")?.to_string();
1883            let value = tag.all_text();
1884            (!value.is_empty()).then_some((name, value))
1885        })
1886        .collect();
1887    // MuseScore keeps its tags sorted by name.
1888    tags.sort_by(|left, right| left.0.cmp(&right.0));
1889    let get = |name: &str| {
1890        tags.iter()
1891            .find(|(held, _)| held == name)
1892            .map(|(_, value)| value.clone())
1893    };
1894    let mut metadata = Metadata::new();
1895    if let Some(number) = get("workNumber") {
1896        metadata.add_text("number", number);
1897    }
1898    if let Some(number) = get("movementNumber") {
1899        metadata.add_text("movementNumber", number);
1900    }
1901    // A work title that only repeats the movement's is left out, as the
1902    // MusicXML reader leaves it out, so that both read one score alike.
1903    let title = get("workTitle").filter(|title| Some(title) != get("movementTitle").as_ref());
1904    if let Some(title) = title {
1905        metadata.add_text("title", title);
1906    }
1907    if let Some(title) = get("movementTitle") {
1908        metadata.add_text("movementName", title);
1909    }
1910    for role in ["arranger", "composer", "lyricist", "poet", "translator"] {
1911        if let Some(name) = get(role) {
1912            let unique = if crate::metadata::is_contributor_unique_name(role) {
1913                role
1914            } else {
1915                "otherContributor"
1916            };
1917            metadata.add(unique, MetadataValue::with_role(name.trim(), role));
1918        }
1919    }
1920    if let Some(copyright) = get("copyright") {
1921        metadata.add("copyright", MetadataValue::new(copyright.trim()));
1922    }
1923    if !program.is_empty() {
1924        metadata.add_text("software", program);
1925    }
1926    const NAMED: [&str; 12] = [
1927        "arranger",
1928        "composer",
1929        "lyricist",
1930        "poet",
1931        "translator",
1932        "copyright",
1933        "source",
1934        "workTitle",
1935        "workNumber",
1936        "movementTitle",
1937        "movementNumber",
1938        "originalFormat",
1939    ];
1940    for (name, value) in &tags {
1941        if NAMED.contains(&name.as_str()) {
1942            continue;
1943        }
1944        let unique = crate::metadata::STANDARD_PROPERTIES
1945            .iter()
1946            .find(|(unique, namespaced, _)| unique == name || namespaced == name)
1947            .map_or(name.as_str(), |(unique, _, _)| unique);
1948        metadata.add_text(unique, value.clone());
1949    }
1950    metadata
1951}
1952
1953/// music21's `getDefaultInstrument`, from what MuseScore says of the
1954/// instrument: a program and a name.
1955fn instrument_of(program: Option<u8>, channel: Option<u8>, name: Option<&str>) -> Instrument {
1956    let mut instrument = program
1957        .and_then(|program| Instrument::from_midi_program(program).ok())
1958        .unwrap_or_default();
1959    if program.is_some() {
1960        instrument.set_midi_channel(channel);
1961    }
1962    if let Some(name) = name.filter(|name| !name.is_empty()) {
1963        let mut from_name = Instrument::from_name(name, SearchLanguage::All).unwrap_or_default();
1964        from_name.set_midi_channel(instrument.midi_channel());
1965        if from_name.midi_program() == instrument.midi_program() || instrument.is_a("Piano") {
1966            instrument = from_name;
1967        }
1968    }
1969    instrument
1970}
1971
1972fn parts_of(score: &Xml, hides_single_name: bool) -> Result<Vec<PartRead>> {
1973    let mut parts = Vec::new();
1974    let mut first_staff = 0;
1975    let mut channel: usize = 0;
1976    let listed: Vec<&Xml> = score.find_all("Part").collect();
1977    let hidden = hides_single_name && listed.len() == 1;
1978    for (index, part) in listed.into_iter().enumerate() {
1979        let staves = part.find_all("Staff").count();
1980        if staves == 0 {
1981            return Err(mscx_error("a part with no staff"));
1982        }
1983        for staff in part.find_all("Staff") {
1984            if let Some(kind) = staff.find("StaffType")
1985                && let Some(group) = kind.get("group")
1986                && group != "pitched"
1987            {
1988                return Err(refused(format!("A {group} staff")));
1989            }
1990        }
1991        let instrument = part
1992            .find("Instrument")
1993            .ok_or_else(|| mscx_error("a part with no instrument"))?;
1994        if flag(instrument, "useDrumset") {
1995            return Err(refused("A drum set"));
1996        }
1997        let text = |element: &Xml, tag: &str| -> Result<Option<String>> {
1998            Ok(element
1999                .find(tag)
2000                .map(plain_text)
2001                .transpose()?
2002                .map(|text| text.trim().replace('\n', " "))
2003                .filter(|text| !text.is_empty()))
2004        };
2005        let long_name = text(instrument, "longName")?;
2006        let short_name = text(instrument, "shortName")?;
2007        let track_name = text(instrument, "trackName")?;
2008        let part_track = text(part, "trackName")?;
2009        // A part with no name of its own goes by its track's, which is not
2010        // printed.
2011        let (name, name_hidden) = match long_name {
2012            Some(name) => (Some(name), hidden),
2013            None => (part_track.clone(), part_track.is_some() || hidden),
2014        };
2015        let abbreviation_hidden = name_hidden && short_name.is_some();
2016        let mut brackets: Vec<(i64, i64, usize)> = Vec::new();
2017        let mut bracket_found = false;
2018        for (staff_index, staff) in part.find_all("Staff").enumerate() {
2019            for bracket in staff.find_all("bracket") {
2020                let number = |name: &str| {
2021                    bracket
2022                        .get(name)
2023                        .and_then(|value| value.parse::<i64>().ok())
2024                        .unwrap_or(0)
2025                };
2026                if number("type") < 0 {
2027                    continue;
2028                }
2029                bracket_found = true;
2030                if staff_index == 0 {
2031                    brackets.push((
2032                        number("col"),
2033                        number("type"),
2034                        number("span").max(0) as usize,
2035                    ));
2036                }
2037            }
2038        }
2039        brackets.sort_by_key(|(column, _, _)| *column);
2040        let channels: Vec<&Xml> = instrument.find_all("Channel").collect();
2041        let program = channels
2042            .first()
2043            .and_then(|first| first.find("program"))
2044            .and_then(|program| program.get("value"))
2045            .and_then(|value| value.parse::<u8>().ok());
2046        // The channel the file gives the instrument's first sound, or the
2047        // next one free where it gives none; the tenth is the drums'.
2048        let written = channels
2049            .first()
2050            .and_then(|first| first.find("midiChannel"))
2051            .map(integer)
2052            .transpose()?;
2053        let mut first_channel = written.map(|written| written.rem_euclid(16) as u8);
2054        for _ in 0..channels.len().max(1) {
2055            if channel % 16 == 9 {
2056                channel += 1;
2057            }
2058            first_channel.get_or_insert((channel % 16) as u8);
2059            channel += 1;
2060        }
2061        let mut made = instrument_of(program, first_channel, track_name.as_deref());
2062        made.set_part_id(Some(format!("P{}", index + 1)));
2063        made.set_part_name(name.clone());
2064        made.set_part_abbreviation(short_name.clone());
2065        if let Some(track_name) = &track_name {
2066            made.set_name(Some(track_name.clone()));
2067        }
2068        let sound = instrument.child_text("instrumentId");
2069        if !sound.is_empty() {
2070            made.set_sound(Some(sound.to_string()));
2071        }
2072        let transposition = (
2073            child_integer(instrument, "transposeDiatonic")?.unwrap_or(0),
2074            child_integer(instrument, "transposeChromatic")?.unwrap_or(0),
2075        );
2076        if transposition.1 != 0 {
2077            // The interval from the written note to the sounding one, the
2078            // octaves counted into both halves.
2079            let octaves = transposition.1 / 12;
2080            let diatonic = transposition.0 % 7 + 7 * octaves;
2081            let steps = if diatonic < 0 {
2082                diatonic - 1
2083            } else {
2084                diatonic + 1
2085            };
2086            let interval = Interval::from_generic_and_chromatic(
2087                steps as IntegerType,
2088                transposition.1 as IntegerType,
2089            )
2090            .or_else(|_| Interval::from_semitones(transposition.1 as IntegerType))?;
2091            made.set_transposition(Some(interval));
2092        }
2093        // The clef a staff starts with where its first measure writes
2094        // none: the staff's own, else a treble clef, whatever clef its
2095        // instrument is usually written in. A transposing instrument's is
2096        // the one it reads.
2097        let mut default_clefs = vec![String::from("G"); staves];
2098        for (staff_index, staff) in part.find_all("Staff").enumerate() {
2099            for tag in [
2100                "defaultClef",
2101                "defaultConcertClef",
2102                "defaultTransposingClef",
2103            ] {
2104                if let Some(clef) = staff.find(tag) {
2105                    default_clefs[staff_index] = clef.stripped().to_string();
2106                }
2107            }
2108        }
2109        parts.push(PartRead {
2110            staves,
2111            first_staff,
2112            name,
2113            abbreviation: short_name,
2114            name_hidden,
2115            abbreviation_hidden,
2116            instrument: made,
2117            default_clefs,
2118            brackets: brackets
2119                .into_iter()
2120                .map(|(_, kind, span)| (kind, span))
2121                .collect(),
2122            bracket_found,
2123            transposition,
2124        });
2125        first_staff += staves;
2126    }
2127    if parts.is_empty() {
2128        return Err(mscx_error("a score with no parts"));
2129    }
2130    Ok(parts)
2131}
2132
2133// --------------------------------------------------------------- assembly
2134
2135/// One element of a measure as music21 would hold it once read.
2136#[derive(Clone, Debug)]
2137struct Item {
2138    offset: FloatType,
2139    seq: usize,
2140    staff: usize,
2141    uid: Option<usize>,
2142    element: StreamElement,
2143}
2144
2145impl Item {
2146    fn order(&self) -> i32 {
2147        self.element.class_sort_order()
2148    }
2149
2150    fn is_grace(&self) -> bool {
2151        self.element.duration().is_some_and(Duration::is_grace)
2152    }
2153}
2154
2155fn sorted(mut items: Vec<Item>) -> Vec<Item> {
2156    items.sort_by(|left, right| {
2157        left.offset
2158            .total_cmp(&right.offset)
2159            .then(left.order().cmp(&right.order()))
2160            .then(right.is_grace().cmp(&left.is_grace()))
2161            .then(left.seq.cmp(&right.seq))
2162    });
2163    items
2164}
2165
2166#[derive(Clone, Debug, Default)]
2167struct VoiceIr {
2168    id: String,
2169    items: Vec<Item>,
2170}
2171
2172#[derive(Clone, Debug, Default)]
2173struct MeasureIr {
2174    offset: FloatType,
2175    number: IntegerType,
2176    suffix: Option<String>,
2177    hidden: bool,
2178    left: Option<Barline>,
2179    right: Option<Barline>,
2180    ending: Option<Ending>,
2181    items: Vec<Item>,
2182    voices: Vec<VoiceIr>,
2183}
2184
2185fn build_measure(measure: MeasureIr) -> (Stream, Vec<Option<usize>>) {
2186    let mut entries: Vec<(Item, Vec<Option<usize>>)> = Vec::new();
2187    for (index, voice) in measure.voices.into_iter().enumerate() {
2188        let mut uids = Vec::new();
2189        let mut events = Vec::new();
2190        for item in sorted(voice.items) {
2191            uids.push(item.uid);
2192            events.push(StreamEvent::new(item.offset, item.element));
2193        }
2194        let mut stream = Stream::from_events(events);
2195        stream.set_kind(StreamKind::Voice);
2196        stream.set_id(Some(voice.id));
2197        entries.push((
2198            Item {
2199                offset: 0.0,
2200                seq: index,
2201                staff: 0,
2202                uid: None,
2203                element: stream.into(),
2204            },
2205            uids,
2206        ));
2207    }
2208    let voices = entries.len();
2209    for mut item in measure.items {
2210        item.seq = item.seq.saturating_add(voices);
2211        let uid = item.uid;
2212        entries.push((item, vec![uid]));
2213    }
2214    entries.sort_by(|(left, _), (right, _)| {
2215        left.offset
2216            .total_cmp(&right.offset)
2217            .then(left.order().cmp(&right.order()))
2218            .then(right.is_grace().cmp(&left.is_grace()))
2219            .then(left.seq.cmp(&right.seq))
2220    });
2221    let mut uids = Vec::new();
2222    let mut events = Vec::new();
2223    for (item, leaf_uids) in entries {
2224        uids.extend(leaf_uids);
2225        events.push(StreamEvent::new(item.offset, item.element));
2226    }
2227    let mut stream = Stream::from_events(events);
2228    stream.set_kind(StreamKind::Measure);
2229    stream.set_number(measure.number);
2230    stream.set_number_suffix(measure.suffix);
2231    stream.set_number_hidden(measure.hidden);
2232    stream.set_left_barline(measure.left);
2233    stream.set_right_barline(measure.right);
2234    stream.set_ending(measure.ending);
2235    (stream, uids)
2236}
2237
2238/// The tuplets a note is written inside, outermost first, each marked where
2239/// it starts and stops.
2240fn tuplets_for(
2241    tuplets: &[TupletRead],
2242    innermost: Option<usize>,
2243    note: usize,
2244    brackets: &[bool],
2245) -> Vec<Tuplet> {
2246    let mut chain = Vec::new();
2247    let mut open = innermost;
2248    let mut held = Held::Note(note);
2249    while let Some(index) = open {
2250        let tuplet = &tuplets[index];
2251        let first = tuplet.elements.first() == Some(&held);
2252        let last = tuplet.elements.last() == Some(&held);
2253        let mut made = Tuplet::new(tuplet.actual, tuplet.normal, tuplet.base.0, tuplet.base.1);
2254        made.set_tuplet_type(match (first, last) {
2255            (true, true) => Some(TupletType::StartStop),
2256            (true, false) => Some(TupletType::Start),
2257            (false, true) => Some(TupletType::Stop),
2258            _ => None,
2259        });
2260        let bracket = match tuplet.bracket {
2261            1 => true,
2262            2 => false,
2263            // As the beams decide.
2264            _ => brackets[index],
2265        };
2266        made.set_bracket(if bracket {
2267            TupletBracket::Bracket
2268        } else {
2269            TupletBracket::None
2270        });
2271        match tuplet.number {
2272            1 => {
2273                made.set_normal_show(Some(TupletShow::Number));
2274            }
2275            2 => made.set_actual_show(None),
2276            _ => {}
2277        }
2278        made.set_placement(tuplet.placement);
2279        chain.push(made);
2280        held = Held::Tuplet(index);
2281        open = tuplet.parent;
2282    }
2283    chain.reverse();
2284    chain
2285}
2286
2287fn collect_notes(tuplets: &[TupletRead], index: usize, out: &mut Vec<usize>) {
2288    for held in &tuplets[index].elements {
2289        match held {
2290            Held::Note(note) => out.push(*note),
2291            Held::Tuplet(inner) => collect_notes(tuplets, *inner, out),
2292        }
2293    }
2294}
2295
2296/// The note, chord or rest a read one stands for.
2297fn element_of(
2298    read: &ChordRest,
2299    tuplets: Vec<Tuplet>,
2300    beams: Option<Beams>,
2301    beam_stem: StemDirection,
2302) -> Result<StreamElement> {
2303    // Its own stem direction, else its beam's; a note written with no
2304    // stem has neither.
2305    let stem = match read.stem {
2306        _ if read.no_stem => StemDirection::NoStem,
2307        _ if read.order < 3 => StemDirection::Unspecified,
2308        StemDirection::Unspecified => beam_stem,
2309        own => own,
2310    };
2311    let duration = duration_of(read, tuplets)?;
2312    let lyrics: Vec<Lyric> = read
2313        .lyrics
2314        .iter()
2315        .map(|lyric| {
2316            let mut made = Lyric::unsung();
2317            made.set_text(lyric.text.trim());
2318            made.set_syllabic(lyric.syllabic.unwrap_or(Syllabic::Single));
2319            made.set_number(lyric.number);
2320            made
2321        })
2322        .collect();
2323    // Fermatas, then an arpeggio, then the ornaments.
2324    let is_arpeggio = |expression: &&Expression| matches!(expression, Expression::Arpeggio(_));
2325    let expressions: Vec<Expression> = read
2326        .fermatas
2327        .iter()
2328        .cloned()
2329        .map(Expression::Fermata)
2330        .chain(read.expressions.iter().filter(is_arpeggio).cloned())
2331        .chain(
2332            read.expressions
2333                .iter()
2334                .filter(|expression| !is_arpeggio(expression))
2335                .cloned(),
2336        )
2337        .collect();
2338    if read.is_rest {
2339        let mut rest = Rest::new(duration);
2340        rest.set_hidden(read.hidden);
2341        if read.small {
2342            rest.set_size(Some(NoteSize::Cue));
2343        }
2344        rest.set_step_shift(read.step_shift);
2345        *rest.lyrics_mut() = lyrics;
2346        *rest.expressions_mut() = expressions;
2347        *rest.articulations_mut() = read.articulations.clone();
2348        return Ok(rest.into());
2349    }
2350    let mut notes: Vec<Note> = Vec::new();
2351    for written in &read.notes {
2352        let mut note = Note::from_pitch(written.pitch.clone());
2353        note.set_duration(duration.clone());
2354        // MuseScore hides a note's head and its stem each on its own, so a
2355        // hidden note is one with no head and not one left off the page.
2356        if written.hidden {
2357            note.set_notehead(Notehead::NoneShape);
2358        }
2359        if written.small || read.small {
2360            note.set_size(Some(NoteSize::Cue));
2361        }
2362        let tie = match (written.tie_start, written.tie_stop) {
2363            (true, true) => Some(TieType::Continue),
2364            (true, false) => Some(TieType::Start),
2365            (false, true) => Some(TieType::Stop),
2366            (false, false) => None,
2367        };
2368        note.set_tie(tie.map(|tie_type| {
2369            let mut tie = Tie::new(tie_type);
2370            if tie_type == TieType::Start {
2371                tie.set_placement(written.tie_placement);
2372            }
2373            tie
2374        }));
2375        if let Some(velocity) = written.velocity {
2376            // MuseScore's MusicXML writes a velocity as a percentage of
2377            // ninety to two places, which is as fine as the two can agree.
2378            let percent = (velocity as FloatType * 10000.0 / 90.0).round() / 100.0;
2379            note.set_volume(Some(Volume::from_velocity_scalar(
2380                percent * 90.0 / 12700.0,
2381            )?));
2382        }
2383        if let Some(head) = written.head {
2384            note.set_notehead(head);
2385        }
2386        if written.color.is_some() {
2387            note.set_color(written.color.clone());
2388        }
2389        if written.head_filled.is_some() {
2390            note.set_notehead_fill(written.head_filled);
2391        }
2392        note.set_stem_direction(stem);
2393        notes.push(note);
2394    }
2395    // The marks of the chord, each note's fingerings among them: those of
2396    // the lowest note first, then what is written on the chord itself, then
2397    // the fingerings of the notes above.
2398    let mut articulations: Vec<Articulation> = Vec::new();
2399    for (index, written) in read.notes.iter().enumerate() {
2400        articulations.extend(written.fingerings.iter().cloned());
2401        if index == 0 {
2402            articulations.extend(read.articulations.iter().cloned());
2403        }
2404    }
2405    if notes.len() == 1 {
2406        let mut note = notes.remove(0);
2407        if let Some(beams) = beams {
2408            note.set_beams(beams);
2409        }
2410        *note.lyrics_mut() = lyrics;
2411        *note.articulations_mut() = articulations;
2412        *note.expressions_mut() = expressions;
2413        return Ok(note.into());
2414    }
2415    if let Some(first) = notes.first_mut() {
2416        *first.lyrics_mut() = lyrics;
2417    }
2418    let mut chord = Chord::new(notes)?;
2419    chord.set_duration(duration);
2420    if let Some(beams) = beams {
2421        chord.set_beams(beams);
2422    }
2423    *chord.articulations_mut() = articulations;
2424    *chord.expressions_mut() = expressions;
2425    Ok(chord.into())
2426}
2427
2428fn duration_of(read: &ChordRest, tuplets: Vec<Tuplet>) -> Result<Duration> {
2429    let quarter_length = quarters(read.length);
2430    let Some((kind, dots)) = read.value else {
2431        return Duration::new(quarter_length);
2432    };
2433    if let Some(grace) = read.grace {
2434        let mut written = Duration::default();
2435        written.clear();
2436        written.set_tuplets(Vec::new());
2437        written.add_duration_tuple(kind, dots);
2438        let mut value = written.grace_duration();
2439        let mut marks = Grace::new();
2440        marks.set_slash(grace == GraceKind::Acciaccatura);
2441        value.set_grace(Some(marks));
2442        return Ok(value);
2443    }
2444    if tuplets.is_empty() {
2445        return Duration::new(quarter_length);
2446    }
2447    let mut duration = Duration::default();
2448    duration.clear();
2449    duration.set_tuplets(Vec::new());
2450    duration.add_duration_tuple(kind, dots);
2451    duration.set_tuplets(tuplets);
2452    if (duration.quarter_length() - quarter_length).abs() > 1e-7 {
2453        duration.set_linked(false);
2454        duration.set_quarter_length(quarter_length)?;
2455    }
2456    Ok(duration)
2457}
2458
2459/// A part put together out of its measures, with the uid of each leaf.
2460struct BuiltPart {
2461    stream: Stream,
2462    uids: Vec<Option<usize>>,
2463}
2464
2465/// Reads a MuseScore file into a score.
2466///
2467/// The text is an uncompressed `.mscx` as MuseScore 4 saves it, or as
2468/// MuseScore 3 does where the two formats are the same. A `.mscz` is a zip
2469/// archive holding one; the library opens no file and unpacks nothing, so
2470/// the caller hands over the text of the `.mscx` inside it.
2471///
2472/// Every `<Part>` is a part, and a part on several staves -- a piano -- is
2473/// a part for each staff joined by a brace, as a MusicXML reader makes it.
2474/// Pitches are spelled as the file spells them, and a transposing
2475/// instrument's are written as it reads them.
2476///
2477/// # What is read
2478///
2479/// - Parts, their names and abbreviations, instruments, MIDI programs and
2480///   channels, transpositions, and the brackets and braces joining staves.
2481/// - Measures and their numbers -- pickups, measures left out of the count,
2482///   sections that start again from one -- and up to four voices in each.
2483/// - Notes, chords and rests, measure rests among them: lengths, dots,
2484///   tuplets and tuplets inside tuplets, ties, written accidentals,
2485///   noteheads, colours, velocities, stems the file fixes, and grace notes
2486///   before and after a note.
2487/// - Clefs, key signatures, meters, tempo marks, dynamics, staff and system
2488///   text, lyrics and chord symbols.
2489/// - Articulations, fingerings, breath marks, fermatas on notes and on
2490///   barlines, ornaments and arpeggios.
2491/// - Barlines, repeats, endings, and the signs and words that send a
2492///   player elsewhere: segno, coda, *To Coda*, *Fine*, *D.C.* and *D.S.*
2493/// - Slurs, hairpins and pedal lines.
2494/// - The title, composer and the rest of the file's `metaTag`s.
2495///
2496/// A MuseScore file leaves some of what a page shows to be worked out when
2497/// the page is laid out, and that is worked out here as MuseScore does it:
2498/// beams from the meter and the beam modes of the notes, whether a tuplet
2499/// takes a bracket, the clef and key signature a staff starts with when
2500/// none is written, and the final barline.
2501///
2502/// # What is not read
2503///
2504/// What is only drawn -- frames, page and system breaks, positions, fonts,
2505/// rehearsal marks, and whatever is marked invisible -- is passed over.
2506/// MuseScore 4 keeps a score's style in a file of its own beside the
2507/// `.mscx`, which this function is not given; where the style decides
2508/// something, MuseScore's default is taken.
2509///
2510/// What the crate has no value for, or cannot read yet, is refused with an
2511/// [`Error::MuseScore`] naming it rather than dropped: percussion and
2512/// tablature staves, files in a format older than MuseScore 3's, tremolos,
2513/// trill, octave, glissando and text lines, arpeggios across staves,
2514/// ornaments with accidentals, key signatures of a score's own making,
2515/// measure repeats, fret diagrams, figured bass, instrument changes, shape
2516/// notes, and a chord symbol whose name is none the crate knows.
2517///
2518/// ```
2519/// use music21::musescore::from_mscx;
2520///
2521/// let score = from_mscx(
2522///     r#"<museScore version="4.20"><Score>
2523///     <Part><Staff/><trackName>Flute</trackName>
2524///       <Instrument><longName>Flute</longName><trackName>Flute</trackName>
2525///       <Channel><program value="73"/></Channel></Instrument></Part>
2526///     <Staff id="1"><Measure><voice>
2527///       <TimeSig><sigN>2</sigN><sigD>4</sigD></TimeSig>
2528///       <Chord><durationType>quarter</durationType><Note><pitch>60</pitch><tpc>14</tpc></Note></Chord>
2529///       <Chord><durationType>quarter</durationType><Note><pitch>63</pitch><tpc>11</tpc></Note></Chord>
2530///     </voice></Measure></Staff>
2531///     </Score></museScore>"#,
2532/// )?;
2533/// let names: Vec<String> = score.parts()[0]
2534///     .pitches()
2535///     .iter()
2536///     .map(|pitch| pitch.name_with_octave())
2537///     .collect();
2538/// assert_eq!(names, ["C4", "Eb4"]);
2539/// # Ok::<(), music21::Error>(())
2540/// ```
2541pub fn from_mscx(document: &str) -> Result<Stream> {
2542    let root = Xml::parse(document)?;
2543    if root.tag != "museScore" {
2544        return Err(mscx_error(format!(
2545            "a MuseScore file starts with <museScore>, not <{}>",
2546            root.tag
2547        )));
2548    }
2549    let version = root.get("version").unwrap_or("");
2550    let format: FloatType = version.parse().unwrap_or(0.0);
2551    if format < 3.0 {
2552        return Err(refused(format!("A file in MuseScore's format {version:?}")));
2553    }
2554    let score = root
2555        .find("Score")
2556        .ok_or_else(|| mscx_error("a MuseScore file with no <Score>"))?;
2557
2558    let program_version = root.child_text("programVersion");
2559    let program = if program_version.is_empty() {
2560        String::new()
2561    } else {
2562        let studio = program_version
2563            .split('.')
2564            .take(2)
2565            .map(|piece| piece.parse::<u32>().unwrap_or(0))
2566            .collect::<Vec<_>>();
2567        let renamed = studio.first().copied().unwrap_or(0) > 4
2568            || (studio.first() == Some(&4) && studio.get(1).copied().unwrap_or(0) >= 4);
2569        if renamed {
2570            format!("MuseScore Studio {program_version}")
2571        } else {
2572            format!("MuseScore {program_version}")
2573        }
2574    };
2575    let metadata = metadata_of(score, &program);
2576    // A score of one part does not print the part's name, unless its style
2577    // says otherwise. MuseScore 4 keeps the style in a file of its own,
2578    // which is not read; MuseScore 3 wrote it into the score.
2579    let hides_single_name = score
2580        .find("Style")
2581        .and_then(|style| style.find("hideInstrumentNameIfOneInstrument"))
2582        .is_none_or(|hide| hide.stripped() != "0");
2583    let parts = parts_of(score, hides_single_name)?;
2584    let staves: Vec<&Xml> = score.find_all("Staff").collect();
2585    let staff_count: usize = parts.iter().map(|part| part.staves).sum();
2586    if staves.len() != staff_count {
2587        return Err(mscx_error(format!(
2588            "{} staves are written for parts with {staff_count}",
2589            staves.len()
2590        )));
2591    }
2592
2593    let mut reader = Reader {
2594        old_format: format < 4.1,
2595        markers_by_label: format < 4.6,
2596        next_uid: 0,
2597        spanners: Vec::new(),
2598    };
2599    let mut read_staves: Vec<Vec<StaffMeasure>> = Vec::new();
2600    for part in &parts {
2601        for index in 0..part.staves {
2602            let global = part.first_staff + index;
2603            read_staves.push(reader.staff(staves[global], global, part.transposition)?);
2604        }
2605    }
2606    let measure_count = read_staves.first().map_or(0, Vec::len);
2607    if read_staves.iter().any(|staff| staff.len() != measure_count) {
2608        return Err(mscx_error("staves with different numbers of measures"));
2609    }
2610
2611    let grid = grid_of(&mut read_staves)?;
2612    Assembler {
2613        parts: &parts,
2614        staves: &read_staves,
2615        grid: &grid,
2616        spanners: &reader.spanners,
2617        uids: HashMap::new(),
2618        placed: Vec::new(),
2619    }
2620    .score(metadata)
2621}
2622
2623/// Where each measure starts and how long it is.
2624///
2625/// A clef, a key or a meter written at the very end of a measure is the
2626/// next measure's, and is moved to its start on the way.
2627fn grid_of(staves: &mut [Vec<StaffMeasure>]) -> Result<Grid> {
2628    let count = staves.first().map_or(0, Vec::len);
2629    let mut grid = Grid {
2630        starts: Vec::with_capacity(count),
2631        lengths: Vec::with_capacity(count),
2632        meters: Vec::with_capacity(count),
2633    };
2634    let mut meter = (4, 4);
2635    let mut start = Frac::zero();
2636    for index in 0..count {
2637        // The meter a measure starts with, from whichever staff says one.
2638        for staff in staves.iter() {
2639            let found = staff[index]
2640                .voices
2641                .iter()
2642                .flat_map(|voice| &voice.events)
2643                .find_map(|event| match &event.what {
2644                    What::Meter(written) if event.tick.is_zero() => Some(written),
2645                    _ => None,
2646                });
2647            if let Some(written) = found {
2648                meter = (
2649                    i64::from(written.numerator()),
2650                    i64::from(written.denominator()),
2651                );
2652                break;
2653            }
2654        }
2655        let length = staves
2656            .iter()
2657            .find_map(|staff| staff[index].len)
2658            .unwrap_or_else(|| Frac::new(meter.0, meter.1));
2659        grid.starts.push(start);
2660        grid.lengths.push(length);
2661        grid.meters.push(meter);
2662        start += length;
2663        if index + 1 < count {
2664            for staff in staves.iter_mut() {
2665                let mut moved: Vec<Event> = Vec::new();
2666                for voice in &mut staff[index].voices {
2667                    let (ending, kept): (Vec<Event>, Vec<Event>) =
2668                        std::mem::take(&mut voice.events)
2669                            .into_iter()
2670                            .partition(|event| {
2671                                event.tick == length
2672                                    && matches!(
2673                                        event.what,
2674                                        What::Clef(_) | What::Key(_) | What::Meter(_)
2675                                    )
2676                            });
2677                    voice.events = kept;
2678                    moved.extend(ending);
2679                }
2680                if moved.is_empty() {
2681                    continue;
2682                }
2683                let next = &mut staff[index + 1];
2684                if next.voices.is_empty() {
2685                    next.voices.push(VoiceRead::default());
2686                }
2687                for (place, mut event) in moved.into_iter().enumerate() {
2688                    event.tick = Frac::zero();
2689                    next.voices[0].events.insert(place, event);
2690                }
2691            }
2692        }
2693    }
2694    Ok(grid)
2695}
2696
2697struct Assembler<'a> {
2698    parts: &'a [PartRead],
2699    staves: &'a [Vec<StaffMeasure>],
2700    grid: &'a Grid,
2701    spanners: &'a [SpannerRead],
2702    /// Each note's uid by where it stands: measure, tick and track.
2703    uids: HashMap<(i64, Frac, usize, Option<usize>), usize>,
2704    /// Every note, chord and rest in the order MuseScore writes them out:
2705    /// part by part, measure by measure, staff by staff, voice by voice.
2706    placed: Vec<Placed>,
2707}
2708
2709/// A note, chord or rest by where it stands, from the top of the score.
2710#[derive(Clone, Copy, Debug)]
2711struct Placed {
2712    part: usize,
2713    track: usize,
2714    start: Frac,
2715    end: Frac,
2716    uid: usize,
2717}
2718
2719impl Assembler<'_> {
2720    fn score(mut self, metadata: Metadata) -> Result<Stream> {
2721        let mut built: Vec<BuiltPart> = Vec::new();
2722        let mut groups: Vec<StaffGroup> = Vec::new();
2723        let mut part_positions: Vec<Vec<usize>> = Vec::new();
2724        for (index, part) in self.parts.iter().enumerate() {
2725            let first = built.len();
2726            let staves = self.part(index, part)?;
2727            let count = staves.len();
2728            built.extend(staves);
2729            part_positions.push((first..first + count).collect());
2730            if count > 1 {
2731                let mut group = StaffGroup::new((first..first + count).collect());
2732                group.set_name(part.name.clone());
2733                group.set_symbol(Some("brace"))?;
2734                group.set_bar_together(Some(BarTogether::Yes));
2735                group.set_name_hidden(true);
2736                groups.push(group);
2737            }
2738        }
2739        groups.extend(self.part_groups(&part_positions)?);
2740        let mut uids: Vec<Option<usize>> = Vec::new();
2741        let mut events = Vec::new();
2742        for part in built {
2743            uids.extend(part.uids);
2744            events.push(StreamEvent::new(0.0, part.stream));
2745        }
2746        let mut assembled = Stream::from_events(events);
2747        assembled.set_kind(StreamKind::Score);
2748        assembled.set_metadata(Some(metadata));
2749        for group in groups {
2750            assembled.add_staff_group(group);
2751        }
2752        let position_of = |uid: usize| uids.iter().position(|held| *held == Some(uid));
2753        // A part's spanners in the order they start: measure by measure,
2754        // and in a measure staff by staff and voice by voice.
2755        let part_of = |track: usize| {
2756            self.parts
2757                .iter()
2758                .position(|part| {
2759                    (part.first_staff..part.first_staff + part.staves).contains(&(track / 4))
2760                })
2761                .unwrap_or(0)
2762        };
2763        let mut order: Vec<&SpannerRead> = self.spanners.iter().collect();
2764        order.sort_by_key(|spanner| {
2765            (
2766                part_of(spanner.start.track),
2767                spanner.start.measure,
2768                spanner.start.track,
2769                spanner.start.tick,
2770            )
2771        });
2772        for spanner in order {
2773            let (start, end) = (spanner.start, spanner.end);
2774            let made = match &spanner.kind {
2775                SpannerStart::Slur(placement, line_type) => {
2776                    let first = self
2777                        .uids
2778                        .get(&(start.measure, start.tick, start.track, start.grace))
2779                        .copied();
2780                    let last = self
2781                        .uids
2782                        .get(&(end.measure, end.tick, end.track, end.grace))
2783                        .copied();
2784                    let mut positions = Vec::new();
2785                    for uid in [first, last].into_iter().flatten() {
2786                        if let Some(position) = position_of(uid)
2787                            && !positions.contains(&Some(position))
2788                        {
2789                            positions.push(Some(position));
2790                        }
2791                    }
2792                    if positions.is_empty() {
2793                        continue;
2794                    }
2795                    let mut slur = Spanner::with_unplaced(SpannerKind::Slur, positions);
2796                    slur.set_placement(*placement);
2797                    slur.set_line_type(line_type.map(str::to_string));
2798                    slur
2799                }
2800                SpannerStart::Wedge(_) | SpannerStart::Pedal(_) => {
2801                    let part = part_of(start.track);
2802                    let mut positions: Vec<Option<usize>> = Vec::new();
2803                    if let Some(first) = self.line_start(part, start, end)
2804                        && let Some(position) = position_of(first)
2805                    {
2806                        positions.push(Some(position));
2807                    }
2808                    if let Some(last) = self.line_end(part, start, end)
2809                        && let Some(position) = position_of(last)
2810                        && !positions.contains(&Some(position))
2811                    {
2812                        positions.push(Some(position));
2813                    }
2814                    match &spanner.kind {
2815                        SpannerStart::Wedge(kind) => {
2816                            let template = Spanner::wedge(*kind, Vec::new());
2817                            let mut wedge = Spanner::with_unplaced(*kind, positions);
2818                            wedge.set_placement(template.placement());
2819                            wedge.set_spread(template.spread());
2820                            wedge
2821                        }
2822                        SpannerStart::Pedal(pedal) => {
2823                            let mut mark =
2824                                Spanner::with_unplaced(SpannerKind::PedalMark, positions);
2825                            mark.set_pedal(Some(*pedal));
2826                            mark
2827                        }
2828                        _ => continue,
2829                    }
2830                }
2831                SpannerStart::Volta { .. } => continue,
2832            };
2833            if made.is_empty() {
2834                continue;
2835            }
2836            assembled.add_spanner(made);
2837        }
2838        Ok(assembled)
2839    }
2840
2841    /// The note a line under the staff starts on. A line belongs to a staff
2842    /// and runs from one moment to another, not from note to note: it
2843    /// starts on the note of its staff standing where it starts, or on the
2844    /// first note of its voice to start under it, or else on the note
2845    /// sounding as it starts.
2846    fn line_start(&self, part: usize, start: Position, end: Position) -> Option<usize> {
2847        let from = *self.grid.starts.get(usize::try_from(start.measure).ok()?)? + start.tick;
2848        let to = *self.grid.starts.get(usize::try_from(end.measure).ok()?)? + end.tick;
2849        let staff = start.track / 4;
2850        let here = |placed: &&Placed| placed.part == part && placed.track / 4 == staff;
2851        let exact = self
2852            .placed
2853            .iter()
2854            .filter(here)
2855            .filter(|placed| placed.start == from)
2856            .min_by_key(|placed| placed.track);
2857        if let Some(exact) = exact {
2858            return Some(exact.uid);
2859        }
2860        let mut tracks: Vec<usize> = vec![start.track];
2861        tracks.extend((staff * 4..staff * 4 + 4).filter(|track| *track != start.track));
2862        for track in tracks {
2863            let voice = || {
2864                self.placed
2865                    .iter()
2866                    .filter(here)
2867                    .filter(move |placed| placed.track == track)
2868            };
2869            let under = voice()
2870                .filter(|placed| placed.start >= from && placed.start < to)
2871                .min_by_key(|placed| placed.start);
2872            let sounding = || voice().find(|placed| placed.start <= from && from < placed.end);
2873            if let Some(found) = under.or_else(sounding) {
2874                return Some(found.uid);
2875            }
2876        }
2877        None
2878    }
2879
2880    /// The note a line under the staff ends on: the note of its staff
2881    /// ending where it ends, or else the last note of its voice to start
2882    /// under it.
2883    fn line_end(&self, part: usize, start: Position, end: Position) -> Option<usize> {
2884        let from = *self.grid.starts.get(usize::try_from(start.measure).ok()?)? + start.tick;
2885        let to = *self.grid.starts.get(usize::try_from(end.measure).ok()?)? + end.tick;
2886        let staff = start.track / 4;
2887        let here = |placed: &&Placed| placed.part == part && placed.track / 4 == staff;
2888        let exact = self
2889            .placed
2890            .iter()
2891            .filter(here)
2892            .filter(|placed| placed.end == to)
2893            .min_by_key(|placed| placed.track);
2894        if let Some(exact) = exact {
2895            return Some(exact.uid);
2896        }
2897        let mut tracks: Vec<usize> = vec![start.track];
2898        tracks.extend((staff * 4..staff * 4 + 4).filter(|track| *track != start.track));
2899        for track in tracks {
2900            let voice = || {
2901                self.placed
2902                    .iter()
2903                    .filter(here)
2904                    .filter(move |placed| placed.track == track)
2905            };
2906            let last = voice()
2907                .filter(|placed| placed.start < to && placed.end > from)
2908                .max_by_key(|placed| placed.start);
2909            if let Some(found) = last {
2910                return Some(found.uid);
2911            }
2912        }
2913        None
2914    }
2915
2916    /// The brackets joining parts, as MuseScore's part list says them: a
2917    /// bracket on a part's first staff joins the parts its span reaches, a
2918    /// brace over exactly the staves of one part being that part's own.
2919    fn part_groups(&self, positions: &[Vec<usize>]) -> Result<Vec<StaffGroup>> {
2920        let mut groups: Vec<(i64, Vec<usize>)> = Vec::new();
2921        // Each open group's index and the staff it ends before.
2922        let mut open: Vec<Option<(usize, usize)>> = vec![None; 8];
2923        let mut staff_count = 0usize;
2924        let last = self.parts.len().saturating_sub(1);
2925        for (index, part) in self.parts.iter().enumerate() {
2926            let mut starting: Vec<(i64, usize)> = Vec::new();
2927            for (kind, span) in &part.brackets {
2928                if *span == part.staves && *kind == 1 {
2929                    continue;
2930                }
2931                if index < last {
2932                    starting.push((*kind, staff_count + span));
2933                }
2934            }
2935            if !part.bracket_found && part.staves > 1 {
2936                starting.push((-1, index + part.staves));
2937            }
2938            for (kind, end) in starting {
2939                if let Some(slot) = open.iter().position(Option::is_none) {
2940                    open[slot] = Some((groups.len(), end));
2941                    groups.push((kind, Vec::new()));
2942                }
2943            }
2944            for (group, _) in open.iter().flatten() {
2945                groups[*group].1.extend(positions[index].iter().copied());
2946            }
2947            staff_count += part.staves;
2948            for slot in open.iter_mut() {
2949                if let Some((_, end)) = slot
2950                    && staff_count >= *end
2951                {
2952                    *slot = None;
2953                }
2954            }
2955        }
2956        let mut out = Vec::new();
2957        for (kind, parts) in groups {
2958            let mut group = StaffGroup::new(parts);
2959            group.set_symbol(Some(match kind {
2960                0 => "bracket",
2961                1 => "brace",
2962                2 => "square",
2963                3 => "line",
2964                _ => "none",
2965            }))?;
2966            group.set_bar_together(Some(BarTogether::Yes));
2967            out.push(group);
2968        }
2969        Ok(out)
2970    }
2971
2972    /// A part, or one part for each of its staves.
2973    fn part(&mut self, index: usize, part: &PartRead) -> Result<Vec<BuiltPart>> {
2974        let measures = self.measures(index, part)?;
2975        if part.staves == 1 {
2976            let (stream, uids) = self.build(part, StreamKind::Part, None, measures);
2977            return Ok(vec![BuiltPart { stream, uids }]);
2978        }
2979        let mut out = Vec::new();
2980        let mut taken: Vec<usize> = Vec::new();
2981        for key in 1..=part.staves {
2982            let mut split = Vec::new();
2983            for measure in &measures {
2984                let mut copy = MeasureIr {
2985                    offset: measure.offset,
2986                    number: measure.number,
2987                    suffix: measure.suffix.clone(),
2988                    hidden: false,
2989                    left: measure.left.clone(),
2990                    right: measure.right.clone(),
2991                    ending: measure.ending.clone(),
2992                    items: Vec::new(),
2993                    voices: Vec::new(),
2994                };
2995                let mut seq = 0usize;
2996                let mut take = |items: &[Item], into: &mut Vec<Item>, seq: &mut usize| {
2997                    for item in sorted(items.to_vec()) {
2998                        if item.staff != 0 && item.staff != key {
2999                            continue;
3000                        }
3001                        let mut item = item;
3002                        let seq_key = item.uid.unwrap_or(usize::MAX);
3003                        if taken.contains(&seq_key) {
3004                            item.uid = None;
3005                        } else {
3006                            taken.push(seq_key);
3007                        }
3008                        item.seq = *seq;
3009                        *seq += 1;
3010                        into.push(item);
3011                    }
3012                };
3013                take(&measure.items, &mut copy.items, &mut seq);
3014                for voice in &measure.voices {
3015                    let mut items = Vec::new();
3016                    take(&voice.items, &mut items, &mut seq);
3017                    copy.voices.push(VoiceIr {
3018                        id: voice.id.clone(),
3019                        items,
3020                    });
3021                }
3022                copy.voices.retain(|voice| !voice.items.is_empty());
3023                if copy.voices.len() == 1 {
3024                    let voice = copy.voices.remove(0);
3025                    for mut item in sorted(voice.items) {
3026                        item.seq = seq;
3027                        seq += 1;
3028                        copy.items.push(item);
3029                    }
3030                }
3031                split.push(copy);
3032            }
3033            let id = format!("P{}-Staff{key}", index + 1);
3034            let (stream, uids) = self.build(part, StreamKind::PartStaff, Some(id), split);
3035            out.push(BuiltPart { stream, uids });
3036        }
3037        Ok(out)
3038    }
3039
3040    fn build(
3041        &self,
3042        part: &PartRead,
3043        kind: StreamKind,
3044        id: Option<String>,
3045        measures: Vec<MeasureIr>,
3046    ) -> (Stream, Vec<Option<usize>>) {
3047        let mut uids = vec![None];
3048        let mut events = vec![StreamEvent::new(0.0, part.instrument.clone())];
3049        for measure in measures {
3050            let offset = measure.offset;
3051            let (stream, leaf_uids) = build_measure(measure);
3052            uids.extend(leaf_uids);
3053            events.push(StreamEvent::new(offset, stream));
3054        }
3055        let mut stream = Stream::from_events(events);
3056        stream.set_kind(kind);
3057        stream.set_id(id.or_else(|| part.instrument.best_name().map(str::to_string)));
3058        stream.set_name(part.name.clone());
3059        stream.set_abbreviation(part.abbreviation.clone());
3060        if kind != StreamKind::PartStaff {
3061            stream.set_name_hidden(part.name_hidden);
3062            stream.set_abbreviation_hidden(part.abbreviation_hidden);
3063        }
3064        (stream, uids)
3065    }
3066
3067    /// Every measure of a part, all its staves together, as music21 holds
3068    /// a measure it has read before separating the staves.
3069    fn measures(&mut self, part_index: usize, part: &PartRead) -> Result<Vec<MeasureIr>> {
3070        let mut out: Vec<MeasureIr> = Vec::new();
3071        let mut number: IntegerType = 1;
3072        let mut irregular_count: IntegerType = 1;
3073        let mut last_number: IntegerType = 0;
3074        let mut last_suffix: Option<String> = None;
3075        let mut offset = 0.0;
3076        let mut last_meter: Option<TimeSignature> = None;
3077        let mut open_voltas: Vec<(Vec<u32>, i64)> = Vec::new();
3078        let count = self.grid.lengths.len();
3079        for index in 0..count {
3080            // What is said of a measure as a whole is said on the score's
3081            // first staff.
3082            let first_staff = &self.staves[0][index];
3083            // The measure's number, as MuseScore's MusicXML writes it and
3084            // music21 reads that.
3085            if first_staff.starts_section {
3086                number = 1;
3087                irregular_count = 1;
3088            }
3089            number += first_staff.number_offset as IntegerType;
3090            let (mut measure_number, mut suffix, hidden) =
3091                if first_staff.irregular && irregular_count + number == 2 {
3092                    (0, None, true)
3093                } else if first_staff.irregular {
3094                    let written = irregular_count;
3095                    irregular_count += 1;
3096                    (written, Some("X".to_string()), true)
3097                } else {
3098                    number += 1;
3099                    (number - 1, None, false)
3100                };
3101            if suffix.as_deref() == Some("X") && measure_number != last_number + 1 {
3102                let mut written = format!("X{measure_number}");
3103                if let Some(before) = &last_suffix {
3104                    written = format!("{before}{written}");
3105                }
3106                measure_number = last_number;
3107                suffix = Some(written);
3108            }
3109            if measure_number != last_number {
3110                last_number = measure_number;
3111                last_suffix = suffix.clone();
3112            }
3113
3114            let mut ir = MeasureIr {
3115                number: measure_number,
3116                suffix,
3117                hidden,
3118                ..MeasureIr::default()
3119            };
3120            let mut seq = 0usize;
3121            let mut push = |items: &mut Vec<Item>,
3122                            offset: FloatType,
3123                            staff: usize,
3124                            uid: Option<usize>,
3125                            element: StreamElement| {
3126                items.push(Item {
3127                    offset,
3128                    seq,
3129                    staff,
3130                    uid,
3131                    element,
3132                });
3133                seq += 1;
3134            };
3135
3136            // What the measure's staves hold, staff by staff, voice by voice.
3137            let mut voice_items: Vec<(String, Vec<Item>)> = Vec::new();
3138            let mut plain: Vec<Item> = Vec::new();
3139            let mut right: Option<Barline> = None;
3140            let mut right_fermata = false;
3141            // Signs and words saying where to go next are the first part's;
3142            // a sign stands ahead of the notes it stands with.
3143            if part_index == 0 {
3144                let target = usize::from(part.staves > 1);
3145                for mark in &first_staff.marks_at_start {
3146                    push(&mut plain, 0.0, target, None, mark.clone());
3147                }
3148            }
3149            for staff in 0..part.staves {
3150                let global = part.first_staff + staff;
3151                let staff_number = if part.staves > 1 { staff + 1 } else { 0 };
3152                let key_staff = staff + 1;
3153                let read = &self.staves[global][index];
3154                // A staff with no clef at its very start takes its
3155                // instrument's.
3156                if index == 0 {
3157                    let has_clef = read
3158                        .voices
3159                        .iter()
3160                        .flat_map(|voice| &voice.events)
3161                        .any(|event| matches!(event.what, What::Clef(_)) && event.tick.is_zero());
3162                    if !has_clef {
3163                        let clef = clef_named(&part.default_clefs[staff])?;
3164                        push(&mut plain, 0.0, key_staff, None, clef.into());
3165                    }
3166                    let has_key = read
3167                        .voices
3168                        .iter()
3169                        .flat_map(|voice| &voice.events)
3170                        .any(|event| matches!(event.what, What::Key(_)) && event.tick.is_zero());
3171                    if !has_key && staff == 0 {
3172                        push(&mut plain, 0.0, 0, None, KeySignature::new(0).into());
3173                    }
3174                }
3175                for (voice_index, voice) in read.voices.iter().enumerate() {
3176                    let track = global * 4 + voice_index;
3177                    let voice_id = if part.staves > 1 {
3178                        (staff * 4 + voice_index + 1).to_string()
3179                    } else {
3180                        (voice_index + 1).to_string()
3181                    };
3182                    let groups = self.beams_for(voice, index, voice_index, global)?;
3183                    let mut note_index = 0usize;
3184                    for event in &voice.events {
3185                        let at = quarters(event.tick);
3186                        match &event.what {
3187                            What::ChordRest(read_cr) => {
3188                                let beams = groups.beams.get(&note_index).cloned();
3189                                let tuplets = tuplets_for(
3190                                    &voice.tuplets,
3191                                    read_cr.tuplet,
3192                                    note_index,
3193                                    &groups.brackets,
3194                                );
3195                                note_index += 1;
3196                                // A chord may be written on the staff beside its
3197                                // own; a rest is not.
3198                                let staff_move = if read_cr.is_rest {
3199                                    0
3200                                } else {
3201                                    read_cr.staff_move
3202                                };
3203                                let cr_staff = if part.staves > 1 {
3204                                    (staff as i64 + 1 + staff_move).clamp(1, part.staves as i64)
3205                                        as usize
3206                                } else {
3207                                    0
3208                                };
3209                                let items =
3210                                    match voice_items.iter_mut().find(|(id, _)| *id == voice_id) {
3211                                        Some((_, items)) => items,
3212                                        None => {
3213                                            voice_items.push((voice_id.clone(), Vec::new()));
3214                                            &mut voice_items.last_mut().expect("just pushed").1
3215                                        }
3216                                    };
3217                                for (grace_index, grace) in read_cr.graces_before.iter().enumerate()
3218                                {
3219                                    let grace_beams = grace_beams(&read_cr.graces_before)
3220                                        .get(grace_index)
3221                                        .cloned()
3222                                        .flatten();
3223                                    let element = element_of(
3224                                        grace,
3225                                        Vec::new(),
3226                                        grace_beams,
3227                                        grace.beam_stem,
3228                                    )?;
3229                                    self.uids.insert(
3230                                        (index as i64, event.tick, track, Some(grace.grace_index)),
3231                                        grace.uid,
3232                                    );
3233                                    push(items, at, cr_staff, Some(grace.uid), element);
3234                                }
3235                                let beam_stem = groups
3236                                    .stems
3237                                    .get(&(note_index - 1))
3238                                    .copied()
3239                                    .unwrap_or(StemDirection::Unspecified);
3240                                let element = element_of(read_cr, tuplets, beams, beam_stem)?;
3241                                self.uids
3242                                    .insert((index as i64, event.tick, track, None), read_cr.uid);
3243                                let begins = self.grid.starts[index] + event.tick;
3244                                self.placed.push(Placed {
3245                                    part: part_index,
3246                                    track,
3247                                    start: begins,
3248                                    end: begins + read_cr.length,
3249                                    uid: read_cr.uid,
3250                                });
3251                                push(items, at, cr_staff, Some(read_cr.uid), element);
3252                                let end = quarters(event.tick + read_cr.length);
3253                                for (grace_index, grace) in read_cr.graces_after.iter().enumerate()
3254                                {
3255                                    let grace_beams = grace_beams(&read_cr.graces_after)
3256                                        .get(grace_index)
3257                                        .cloned()
3258                                        .flatten();
3259                                    let element = element_of(
3260                                        grace,
3261                                        Vec::new(),
3262                                        grace_beams,
3263                                        grace.beam_stem,
3264                                    )?;
3265                                    self.uids.insert(
3266                                        (index as i64, event.tick, track, Some(grace.grace_index)),
3267                                        grace.uid,
3268                                    );
3269                                    push(items, end, cr_staff, Some(grace.uid), element);
3270                                }
3271                            }
3272                            What::Clef(clef) => {
3273                                // A clef at the end of a measure is the next
3274                                // measure's.
3275                                if event.tick == self.grid.lengths[index] {
3276                                    continue;
3277                                }
3278                                push(&mut plain, at, key_staff, None, clef.clone().into());
3279                            }
3280                            What::Key(key) => {
3281                                if event.tick == self.grid.lengths[index] {
3282                                    continue;
3283                                }
3284                                if staff == 0 {
3285                                    push(&mut plain, at, 0, None, key.clone());
3286                                }
3287                            }
3288                            What::Meter(meter) => {
3289                                if event.tick == self.grid.lengths[index] {
3290                                    continue;
3291                                }
3292                                if staff == 0 {
3293                                    push(&mut plain, at, 0, None, meter.clone().into());
3294                                }
3295                            }
3296                            What::Element(element) => {
3297                                let target = if part.staves > 1 { staff + 1 } else { 0 };
3298                                let _ = staff_number;
3299                                push(&mut plain, at, target, None, element.clone());
3300                            }
3301                            What::Barline(barline, fermata) => {
3302                                if event.tick == self.grid.lengths[index] && staff == 0 {
3303                                    right = Some(barline.clone());
3304                                    right_fermata = *fermata;
3305                                }
3306                            }
3307                        }
3308                    }
3309                }
3310            }
3311            if part_index == 0 {
3312                let target = usize::from(part.staves > 1);
3313                let end = quarters(self.grid.lengths[index]);
3314                for mark in &first_staff.marks_at_end {
3315                    push(&mut plain, end, target, None, mark.clone());
3316                }
3317            }
3318            if part.staves == 1 {
3319                for item in &mut plain {
3320                    item.staff = 0;
3321                }
3322            }
3323
3324            // Repeats and endings.
3325            if first_staff.start_repeat {
3326                ir.left = Some(Barline::repeat(RepeatDirection::Start, None));
3327            }
3328            if let Some(times) = first_staff.end_repeat {
3329                let times = (times > 2).then_some(times);
3330                ir.right = Some(Barline::repeat(RepeatDirection::End, times));
3331            } else {
3332                // An ordinary barline written out is what a measure ends
3333                // with anyway, and stands in the way of any other.
3334                ir.right = match right {
3335                    Some(barline)
3336                        if barline.bar_type() == BarlineType::Regular && !right_fermata =>
3337                    {
3338                        None
3339                    }
3340                    Some(barline) => Some(barline),
3341                    None => self.drawn_barline(index),
3342                };
3343            }
3344            for spanner in self.spanners {
3345                if let SpannerStart::Volta { numbers } = &spanner.kind
3346                    && spanner.start.measure == index as i64
3347                    && spanner.start.track / 4 >= part.first_staff
3348                    && spanner.start.track / 4 < part.first_staff + part.staves
3349                {
3350                    let end = self.measure_at(spanner.end);
3351                    open_voltas.push((numbers.clone(), end));
3352                }
3353            }
3354            if let Some(position) = open_voltas.iter().position(|(_, end)| index as i64 <= *end) {
3355                let (numbers, end) = open_voltas[position].clone();
3356                let starts = self.volta_start(&numbers, index, part);
3357                let stops = index as i64 == end;
3358                ir.ending = Some(Ending::new(numbers.clone(), starts, stops));
3359                // A bracket starts and ends on a barline, an ordinary one
3360                // where the measure has no other.
3361                if starts && ir.left.is_none() {
3362                    ir.left = Some(Barline::default());
3363                }
3364                if stops {
3365                    if ir.right.is_none() {
3366                        ir.right = Some(Barline::default());
3367                    }
3368                    open_voltas.remove(position);
3369                }
3370            }
3371
3372            let use_voices = voice_items.len() > 1;
3373            if use_voices {
3374                voice_items.sort_by(|left, right| left.0.cmp(&right.0));
3375                ir.voices = voice_items
3376                    .into_iter()
3377                    .map(|(id, items)| VoiceIr { id, items })
3378                    .collect();
3379                ir.items = plain;
3380            } else {
3381                ir.items = plain;
3382                for (_, items) in voice_items {
3383                    ir.items.extend(items);
3384                }
3385            }
3386
3387            // Where the measure stands, as music21 works it out.
3388            let own_meter = ir.items.iter().find_map(|item| match &item.element {
3389                StreamElement::TimeSignature(meter) => Some(meter.clone()),
3390                _ => None,
3391            });
3392            if let Some(meter) = own_meter {
3393                last_meter = Some(meter);
3394            } else if last_meter.is_none() {
3395                last_meter = Some(TimeSignature::new(4, 4)?);
3396            }
3397            ir.offset = offset;
3398            offset = op_frac(offset + quarters(self.grid.lengths[index]));
3399            let _ = part_index;
3400            out.push(ir);
3401        }
3402        Ok(out)
3403    }
3404
3405    /// The barline a measure ends with where it writes none of its own: a
3406    /// final one at the end of the score, which MuseScore draws without
3407    /// being told. The double barline it draws before a courtesy key
3408    /// signature depends on where a system ends, which is the page's to
3409    /// say, and is not one of these.
3410    fn drawn_barline(&self, index: usize) -> Option<Barline> {
3411        (index + 1 == self.staves[0].len()).then(|| Barline::new(BarlineType::Final))
3412    }
3413
3414    /// Whether a volta starting at this measure is the first measure of it.
3415    fn volta_start(&self, _numbers: &[u32], index: usize, part: &PartRead) -> bool {
3416        self.spanners.iter().any(|spanner| {
3417            matches!(spanner.kind, SpannerStart::Volta { .. })
3418                && spanner.start.measure == index as i64
3419                && spanner.start.track / 4 >= part.first_staff
3420                && spanner.start.track / 4 < part.first_staff + part.staves
3421        })
3422    }
3423
3424    /// The last measure a spanner ending here covers.
3425    fn measure_at(&self, end: Position) -> i64 {
3426        let Some(start) = self.grid.starts.get(end.measure.max(0) as usize) else {
3427            return self.grid.starts.len() as i64 - 1;
3428        };
3429        let at = *start + end.tick;
3430        let mut last = 0;
3431        for (index, begins) in self.grid.starts.iter().enumerate() {
3432            if *begins < at {
3433                last = index as i64;
3434            }
3435        }
3436        last
3437    }
3438
3439    /// The beams of every note of a voice.
3440    fn beams_for(
3441        &self,
3442        voice: &VoiceRead,
3443        index: usize,
3444        voice_index: usize,
3445        _staff: usize,
3446    ) -> Result<Beamed> {
3447        let (numerator, denominator) = self.grid.meters[index];
3448        let groups_table = beams::default_groups(numerator, denominator);
3449        let bar = Frac::new(numerator, denominator);
3450        let anacrusis = if index == 0 && self.grid.lengths[0] < bar {
3451            ticks(bar - self.grid.lengths[0])
3452        } else {
3453            0
3454        };
3455        let context = beams::Context {
3456            groups: &groups_table,
3457            denominator,
3458            anacrusis,
3459            voice: voice_index,
3460        };
3461        let mut members = Vec::new();
3462        let mut written_stems = Vec::new();
3463        for event in &voice.events {
3464            if let What::ChordRest(read) = &event.what {
3465                members.push(member_of(read));
3466                written_stems.push((read.beam_stem, read.staff_move != 0));
3467            }
3468        }
3469        let groups = beams::beam_groups(&context, &members);
3470        let mut beams = HashMap::new();
3471        let mut stems = HashMap::new();
3472        for group in &groups {
3473            let held: Vec<&Member> = group.iter().map(|index| &members[*index]).collect();
3474            for (at, made) in group.iter().zip(beams::beams_of_group(&held)) {
3475                if !members[*at].is_rest {
3476                    beams.insert(*at, made);
3477                }
3478            }
3479            // A beam written out with a direction turns every stem under
3480            // it, unless it runs between two staves, where the stems of
3481            // each staff go their own way.
3482            let direction = group
3483                .iter()
3484                .map(|at| written_stems[*at].0)
3485                .find(|stem| *stem != StemDirection::Unspecified);
3486            let crosses = group.iter().any(|at| written_stems[*at].1);
3487            if let Some(direction) = direction
3488                && !crosses
3489            {
3490                for at in group {
3491                    stems.insert(*at, direction);
3492                }
3493            }
3494        }
3495        // Whether each tuplet that leaves its bracket to MuseScore takes
3496        // one.
3497        let under: Vec<Vec<usize>> = (0..voice.tuplets.len())
3498            .map(|index| {
3499                let mut notes = Vec::new();
3500                collect_notes(&voice.tuplets, index, &mut notes);
3501                notes
3502            })
3503            .collect();
3504        let tuplet_ticks =
3505            |index: usize| -> i64 { under[index].iter().map(|note| members[*note].ticks).sum() };
3506        let brackets = voice
3507            .tuplets
3508            .iter()
3509            .enumerate()
3510            .map(|(index, tuplet)| {
3511                let own: Vec<usize> = tuplet
3512                    .elements
3513                    .iter()
3514                    .filter_map(|held| match held {
3515                        Held::Note(note) => Some(*note),
3516                        Held::Tuplet(_) => None,
3517                    })
3518                    .collect();
3519                beams::tuplet_has_bracket(
3520                    &context,
3521                    &members,
3522                    &groups,
3523                    &beams::TupletShape {
3524                        notes: &under[index],
3525                        own: &own,
3526                        nests: own.len() != tuplet.elements.len(),
3527                    },
3528                    &tuplet_ticks,
3529                )
3530            })
3531            .collect();
3532        Ok(Beamed {
3533            beams,
3534            stems,
3535            brackets,
3536        })
3537    }
3538}
3539
3540struct Beamed {
3541    beams: HashMap<usize, Beams>,
3542    /// Whether each tuplet of the voice is drawn with a bracket, where it
3543    /// leaves that to MuseScore.
3544    brackets: Vec<bool>,
3545    /// The stem direction each beamed note takes from its beam.
3546    stems: HashMap<usize, StemDirection>,
3547}
3548
3549fn member_of(read: &ChordRest) -> Member {
3550    let dots = read.value.map_or(0, |(_, dots)| dots);
3551    Member {
3552        tick: ticks(read.tick),
3553        ticks: ticks(read.length),
3554        value: (if read.measure_rest { 14 } else { read.order }, dots),
3555        is_rest: read.is_rest,
3556        mode: read.mode,
3557        tuplet: read.tuplet,
3558    }
3559}
3560
3561/// The beams of a run of grace notes.
3562fn grace_beams(graces: &[ChordRest]) -> Vec<Option<Beams>> {
3563    let members: Vec<Member> = graces.iter().map(member_of).collect();
3564    let mut out = vec![None; graces.len()];
3565    for group in beams::grace_groups(&members) {
3566        let held: Vec<&Member> = group.iter().map(|index| &members[*index]).collect();
3567        for (at, made) in group.iter().zip(beams::beams_of_group(&held)) {
3568            out[*at] = Some(made);
3569        }
3570    }
3571    out
3572}
3573
3574#[cfg(test)]
3575mod tests {
3576    use super::*;
3577
3578    fn document(voice: &str) -> String {
3579        format!(
3580            r#"<museScore version="4.70"><Score>
3581            <Part><Staff/><trackName>Flute</trackName>
3582            <Instrument><longName>Flute</longName><trackName>Flute</trackName>
3583            <Channel><program value="73"/></Channel></Instrument></Part>
3584            <Staff id="1"><Measure><voice>
3585            <TimeSig><sigN>4</sigN><sigD>4</sigD></TimeSig>{voice}</voice></Measure></Staff>
3586            </Score></museScore>"#
3587        )
3588    }
3589
3590    fn chord(value: &str, pitch: i64, tpc: i64) -> String {
3591        format!(
3592            "<Chord><durationType>{value}</durationType><Note><pitch>{pitch}</pitch>\
3593             <tpc>{tpc}</tpc></Note></Chord>"
3594        )
3595    }
3596
3597    #[test]
3598    fn a_length_past_what_ticks_count_is_refused() {
3599        let dotted = "<Chord><dots>2147483647</dots><durationType>quarter</durationType>\
3600                      <Note><pitch>60</pitch><tpc>14</tpc></Note></Chord>";
3601        assert!(from_mscx(&document(dotted)).is_err());
3602        let rest = "<Rest><durationType>measure</durationType>\
3603                    <duration>9223372036854775807</duration></Rest>";
3604        assert!(from_mscx(&document(rest)).is_err());
3605        assert!(fraction("3/4").is_ok());
3606    }
3607
3608    #[test]
3609    fn a_tonal_pitch_class_spells_its_note() {
3610        assert_eq!(spelling(14).unwrap(), ('C', 0));
3611        assert_eq!(spelling(21).unwrap(), ('C', 1));
3612        assert_eq!(spelling(-1).unwrap(), ('F', -2));
3613        // The letter and the octave; the accidental is the note's to add.
3614        assert_eq!(pitch_of(60, 26).unwrap().name_with_octave(), "B3");
3615        assert_eq!(pitch_of(59, 7).unwrap().name_with_octave(), "C4");
3616    }
3617
3618    #[test]
3619    fn notes_follow_one_another_and_eighths_are_beamed() {
3620        let body = [
3621            chord("eighth", 60, 14),
3622            chord("eighth", 62, 16),
3623            chord("quarter", 64, 18),
3624            chord("half", 65, 13),
3625        ]
3626        .concat();
3627        let score = from_mscx(&document(&body)).unwrap();
3628        let notes = score.parts()[0].notes();
3629        let offsets: Vec<FloatType> = notes.iter().map(|(offset, _)| *offset).collect();
3630        assert_eq!(offsets, [0.0, 0.5, 1.0, 2.0]);
3631        let beams: Vec<usize> = notes
3632            .iter()
3633            .map(|(_, element)| match element {
3634                StreamElement::Note(note) => note.beams().len(),
3635                _ => 0,
3636            })
3637            .collect();
3638        assert_eq!(beams, [1, 1, 0, 0]);
3639    }
3640
3641    #[test]
3642    fn a_triplet_shortens_its_notes() {
3643        let body = format!(
3644            "<Tuplet><normalNotes>2</normalNotes><actualNotes>3</actualNotes>\
3645             <baseNote>eighth</baseNote></Tuplet>{}{}{}<endTuplet/>\
3646             <Chord><durationType>quarter</durationType><dots>1</dots><Note><pitch>60</pitch><tpc>14</tpc></Note></Chord>\
3647             <Rest><durationType>quarter</durationType></Rest>",
3648            chord("eighth", 60, 14),
3649            chord("eighth", 62, 16),
3650            chord("eighth", 64, 18),
3651        );
3652        let score = from_mscx(&document(&body)).unwrap();
3653        let lengths: Vec<FloatType> = score.parts()[0].measures()[0]
3654            .events()
3655            .iter()
3656            .map(|event| event.element().quarter_length())
3657            .filter(|length| *length > 0.0)
3658            .collect();
3659        assert_eq!(lengths.len(), 5);
3660        assert!((lengths[0] - 1.0 / 3.0).abs() < 1e-9);
3661        assert_eq!(lengths[3], 1.5);
3662    }
3663
3664    /// The notes of the first measure of the first part.
3665    fn notes_of(score: &Stream) -> Vec<Note> {
3666        score.parts()[0].measures()[0]
3667            .events()
3668            .iter()
3669            .filter_map(|event| match event.element() {
3670                StreamElement::Note(note) => Some(note.clone()),
3671                _ => None,
3672            })
3673            .collect()
3674    }
3675
3676    #[test]
3677    fn a_grace_note_is_slashed_only_where_it_is_an_acciaccatura() {
3678        let grace = |kind: &str| {
3679            format!(
3680                "<Chord><durationType>eighth</durationType><{kind}/>\
3681                 <Note><pitch>62</pitch><tpc>16</tpc></Note></Chord>"
3682            )
3683        };
3684        let body = [
3685            grace("acciaccatura"),
3686            chord("half", 60, 14),
3687            grace("appoggiatura"),
3688            chord("half", 60, 14),
3689        ]
3690        .concat();
3691        let notes = notes_of(&from_mscx(&document(&body)).unwrap());
3692        let slashed: Vec<Option<bool>> = notes
3693            .iter()
3694            .map(|note| {
3695                note.duration()
3696                    .and_then(Duration::grace)
3697                    .map(|grace| grace.slash())
3698            })
3699            .collect();
3700        assert_eq!(slashed, [Some(true), None, Some(false), None]);
3701    }
3702
3703    #[test]
3704    fn a_stem_points_where_the_file_fixes_it_and_nowhere_otherwise() {
3705        let body = format!(
3706            "<Chord><durationType>quarter</durationType><StemDirection>up</StemDirection>\
3707             <Note><pitch>72</pitch><tpc>14</tpc></Note></Chord>{}\
3708             <Chord><durationType>half</durationType><noStem>1</noStem>\
3709             <Note><pitch>72</pitch><tpc>14</tpc></Note></Chord>",
3710            chord("quarter", 72, 14),
3711        );
3712        let stems: Vec<StemDirection> = notes_of(&from_mscx(&document(&body)).unwrap())
3713            .iter()
3714            .map(Note::stem_direction)
3715            .collect();
3716        assert_eq!(
3717            stems,
3718            [
3719                StemDirection::Up,
3720                StemDirection::Unspecified,
3721                StemDirection::NoStem
3722            ]
3723        );
3724    }
3725
3726    #[test]
3727    fn a_notehead_a_colour_and_a_fingering_are_read_off_the_note() {
3728        let body = "<Chord><durationType>whole</durationType><Note><pitch>60</pitch>\
3729                    <tpc>14</tpc><head>cross</head>\
3730                    <color r=\"255\" g=\"38\" b=\"0\" a=\"255\"/>\
3731                    <Fingering><text>3</text></Fingering></Note></Chord>";
3732        let notes = notes_of(&from_mscx(&document(body)).unwrap());
3733        assert_eq!(notes[0].notehead(), Notehead::from_name("x").unwrap());
3734        assert_eq!(notes[0].color(), Some("#FF2600"));
3735        assert_eq!(notes[0].articulations().len(), 1);
3736        assert!(notes[0].articulations()[0].is_a("Fingering"));
3737    }
3738
3739    #[test]
3740    fn a_tuplet_takes_a_bracket_unless_a_beam_sets_it_apart() {
3741        let triplet = |value: &str| {
3742            format!(
3743                "<Tuplet><normalNotes>2</normalNotes><actualNotes>3</actualNotes>                 <baseNote>{value}</baseNote></Tuplet>{}{}{}<endTuplet/>",
3744                chord(value, 60, 14),
3745                chord(value, 62, 16),
3746                chord(value, 64, 18),
3747            )
3748        };
3749        let body = [
3750            triplet("eighth"),
3751            triplet("quarter"),
3752            chord("quarter", 60, 14),
3753        ]
3754        .concat();
3755        let brackets: Vec<TupletBracket> = notes_of(&from_mscx(&document(&body)).unwrap())
3756            .iter()
3757            .filter_map(|note| {
3758                note.duration()
3759                    .and_then(|length| length.tuplets().first().cloned())
3760            })
3761            .map(|tuplet| tuplet.bracket())
3762            .collect();
3763        // Three beamed eighths, then three quarters no beam can join.
3764        assert_eq!(brackets[..3], [TupletBracket::None; 3]);
3765        assert_eq!(brackets[3..], [TupletBracket::Bracket; 3]);
3766    }
3767
3768    /// A score of one whole note with something written in its measure.
3769    fn marked(version: &str, mark: &str) -> String {
3770        format!(
3771            r#"<museScore version="{version}"><Score>
3772            <Part><Staff/><trackName>Flute</trackName>
3773            <Instrument><longName>Flute</longName><trackName>Flute</trackName>
3774            <Channel><program value="73"/></Channel></Instrument></Part>
3775            <Staff id="1"><Measure>{mark}<voice>
3776            <TimeSig><sigN>4</sigN><sigD>4</sigD></TimeSig>{}</voice></Measure></Staff>
3777            </Score></museScore>"#,
3778            chord("whole", 60, 14)
3779        )
3780    }
3781
3782    /// The signs and jumps of the first measure, each with its offset.
3783    fn repeat_marks(score: &Stream) -> Vec<(FloatType, RepeatExpressionKind)> {
3784        score.parts()[0].measures()[0]
3785            .events()
3786            .iter()
3787            .filter_map(|event| match event.element() {
3788                StreamElement::RepeatExpression(mark) => Some((event.offset(), mark.kind())),
3789                _ => None,
3790            })
3791            .collect()
3792    }
3793
3794    #[test]
3795    fn a_marker_is_of_the_kind_the_file_says_in_the_way_its_format_says_it() {
3796        let segno = "<Marker><text><sym>segno</sym></text><label>segno</label></Marker>";
3797        let typed = "<Marker><text><sym>segno</sym></text><label>segno</label>\
3798                     <markerType>segno</markerType></Marker>";
3799        // Before format 4.6 the label says what a marker is.
3800        let old = from_mscx(&marked("4.40", segno)).unwrap();
3801        assert_eq!(repeat_marks(&old), [(0.0, RepeatExpressionKind::Segno)]);
3802        // From 4.6 on its kind is said apart, and one that says none is the
3803        // end of the piece, as MuseScore reads it.
3804        let new = from_mscx(&marked("4.70", typed)).unwrap();
3805        assert_eq!(repeat_marks(&new), [(0.0, RepeatExpressionKind::Segno)]);
3806        let untyped = from_mscx(&marked("4.70", segno)).unwrap();
3807        assert!(
3808            !repeat_marks(&untyped)
3809                .iter()
3810                .any(|(_, kind)| *kind == RepeatExpressionKind::Segno)
3811        );
3812        // A jump stands at the end of its measure.
3813        let jump = "<Jump><text>D.C.</text><jumpTo>start</jumpTo><playUntil>end</playUntil>\
3814                    <continueAt></continueAt></Jump>";
3815        let jumped = from_mscx(&marked("4.70", jump)).unwrap();
3816        assert_eq!(repeat_marks(&jumped), [(4.0, RepeatExpressionKind::DaCapo)]);
3817    }
3818
3819    #[test]
3820    fn what_the_model_has_no_value_for_is_refused_by_name() {
3821        let tremolo = "<Chord><durationType>whole</durationType><Tremolo><subtype>r8</subtype>\
3822                       </Tremolo><Note><pitch>60</pitch><tpc>14</tpc></Note></Chord>";
3823        let error = from_mscx(&document(tremolo)).unwrap_err().to_string();
3824        assert!(
3825            error.contains("Tremolo") && error.contains("cannot be read yet"),
3826            "{error}"
3827        );
3828        let trill = "<Spanner type=\"Trill\"><Trill/><next><location><measures>1</measures>\
3829                     </location></next></Spanner>";
3830        let error = from_mscx(&document(&[trill, &chord("whole", 60, 14)].concat()))
3831            .unwrap_err()
3832            .to_string();
3833        assert!(error.contains("Trill"), "{error}");
3834    }
3835
3836    #[test]
3837    fn what_is_not_a_musescore_file_is_refused() {
3838        assert!(from_mscx("<score-partwise/>").is_err());
3839        assert!(from_mscx("not xml").is_err());
3840        assert!(from_mscx(r#"<museScore version="2.06"><Score/></museScore>"#).is_err());
3841    }
3842}