Skip to main content

music21/musicxml/
read.rs

1//! Reading MusicXML: music21's `musicxml.xmlToM21`.
2//!
3//! The document is read measure by measure into a list of elements, each
4//! with its offset, the voice and staff it was written in and the order it
5//! was met in, and the score is put together from those at the end -- which
6//! is where music21 sorts its streams and splits a part written on several
7//! staves into one part for each.
8//!
9//! What the crate does not model is passed over as music21's reader passes
10//! over what it does not know: page layout, fonts, positions, credits.
11
12use crate::articulations::{Articulation, ArticulationKind, Finger};
13use crate::bar::{Barline, BarlineType, Ending, RepeatDirection};
14use crate::chord::Chord;
15use crate::chordsymbol::{ChordStepModification, ChordSymbol};
16use crate::clef::Clef;
17use crate::defaults::{FloatType, IntegerType};
18use crate::duration::{
19    Duration, DurationType, Grace, Tuplet, TupletBracket, TupletShow, TupletType,
20};
21use crate::dynamics::Dynamic;
22use crate::error::{Error, FormatErrorKind, Position, Result};
23use crate::expressions::{
24    ArpeggioType, Expression, Fermata, FermataType, Ornament, OrnamentDelay, OrnamentKind,
25    RehearsalMark, TextExpression,
26};
27use crate::instrument::{Instrument, SearchLanguage};
28use crate::interval::Interval;
29use crate::key::KeySignature;
30use crate::layout::{
31    Layout, PageLayout, ScoreLayout, StaffLayout, StaffType, SystemLayout, Tenths,
32};
33use crate::metadata::{Metadata, MetadataValue};
34use crate::meter::TimeSignature;
35use crate::notation::{
36    Beam, BeamDirection, BeamType, Beams, Justification, Lyric, NoteSize, Notehead, Placement,
37    StemDirection, Syllabic, Tie, TieStyle, TieType,
38};
39use crate::note::Note;
40use crate::percussion::{PercussionChord, PercussionNote, Unpitched};
41use crate::pitch::{Accidental, Pitch};
42use crate::repeat::{RepeatExpression, RepeatExpressionKind};
43use crate::rest::Rest;
44use crate::spanner::{
45    Glissando, LineEnd, LineEnds, MultiRest, OctaveShift, Pedal, PedalForm, PedalObject,
46    PedalObjectKind, PedalType, SlideType, Spanner, SpannerKind,
47};
48use crate::stream::{BarTogether, StaffGroup, Stream, StreamElement, StreamEvent, StreamKind};
49use crate::style::{StyleValue, TextStyle};
50use crate::tempo::{MetricModulation, MetronomeMark};
51use crate::text::TextBox;
52use crate::volume::Volume;
53use crate::xml::Xml;
54
55/// What may be drawn round a mark: music21's `style.Enclosure`.
56const ENCLOSURES: [&str; 15] = [
57    "rectangle",
58    "square",
59    "oval",
60    "circle",
61    "bracket",
62    "triangle",
63    "diamond",
64    "pentagon",
65    "hexagon",
66    "heptagon",
67    "octagon",
68    "nonagon",
69    "decagon",
70    "inverted-bracket",
71    "none",
72];
73
74/// The staff an element is written on where nothing says: music21's
75/// `NO_STAFF_ASSIGNED`.
76const NO_STAFF: i32 = 0;
77
78fn import_error(message: impl Into<String>) -> Error {
79    Error::music_xml(message.into())
80}
81
82/// Reads a MusicXML document -- the text of a `.musicxml` file, or of the
83/// score inside a compressed `.mxl` -- into a score: music21's
84/// `converter.parse` for the format.
85///
86/// ```
87/// use music21::musicxml::from_musicxml;
88///
89/// let score = from_musicxml(
90///     "<score-partwise><part-list><score-part id=\"P1\"><part-name>Flute</part-name>\
91///      </score-part></part-list><part id=\"P1\"><measure number=\"1\">\
92///      <attributes><divisions>1</divisions></attributes>\
93///      <note><pitch><step>C</step><octave>4</octave></pitch><duration>4</duration>\
94///      <type>whole</type></note></measure></part></score-partwise>",
95/// )?;
96/// let part = score.parts()[0];
97/// assert_eq!(part.name(), Some("Flute"));
98/// assert_eq!(part.measures()[0].pitches()[0].name_with_octave(), "C4");
99/// # Ok::<(), music21::Error>(())
100/// ```
101pub fn from_musicxml(document: &str) -> Result<Stream> {
102    let root = Xml::parse(document)?;
103    if root.tag != "score-partwise" {
104        return Err(import_error(format!(
105            "only a score-partwise document can be read, not <{}>",
106            root.tag
107        ))
108        .of_kind(FormatErrorKind::Unsupported));
109    }
110    Importer::default().score(&root)
111}
112
113// ------------------------------------------------------------------ numbers
114
115/// A number snapped to the nearest fraction music21 would hold it as:
116/// `opFrac`, which keeps a float whose denominator is a small power of two
117/// and otherwise takes the closest fraction with a denominator up to 65535.
118fn op_frac(value: FloatType) -> FloatType {
119    if (value * 32768.0).fract() == 0.0 {
120        return value;
121    }
122    let negative = value < 0.0;
123    let target = value.abs();
124    // Continued fractions, stopping at the last convergent under the limit.
125    let (mut p0, mut q0, mut p1, mut q1) = (0i64, 1i64, 1i64, 0i64);
126    let mut x = target;
127    let (mut best_p, mut best_q) = (target.round() as i64, 1i64);
128    for _ in 0..40 {
129        let a = x.floor();
130        let (p2, q2) = (a as i64 * p1 + p0, a as i64 * q1 + q0);
131        if q2 > 65535 {
132            // The best semiconvergent under the limit, as Python's
133            // `limit_denominator` takes it.
134            let k = (65535 - q0) / q1.max(1);
135            let (sp, sq) = (p0 + k * p1, q0 + k * q1);
136            let semi = (sp as FloatType / sq as FloatType - target).abs();
137            let conv = (p1 as FloatType / q1 as FloatType - target).abs();
138            (best_p, best_q) = if semi < conv { (sp, sq) } else { (p1, q1) };
139            break;
140        }
141        (p0, q0, p1, q1) = (p1, q1, p2, q2);
142        (best_p, best_q) = (p1, q1);
143        let rest = x - a;
144        if rest.abs() < 1e-12 {
145            break;
146        }
147        x = 1.0 / rest;
148    }
149    let snapped = best_p as FloatType / best_q as FloatType;
150    if negative { -snapped } else { snapped }
151}
152
153fn parse_float(text: &str) -> Option<FloatType> {
154    text.trim().parse::<FloatType>().ok()
155}
156
157fn parse_int(text: &str) -> Option<IntegerType> {
158    let text = text.trim();
159    text.parse::<IntegerType>().ok().or_else(|| {
160        // Python's `int(float_text)` is not accepted by music21 either, but
161        // a whole number written with a point is common in the wild.
162        text.parse::<FloatType>()
163            .ok()
164            .filter(|value| value.fract() == 0.0)
165            .map(|value| value as IntegerType)
166    })
167}
168
169/// How many strokes a `<tremolo>` says, three where it says nothing a
170/// number can be read from: music21's `xmlToTremolo`.
171fn tremolo_marks(mark: &Xml) -> Result<u8> {
172    let marks = parse_int(mark.stripped()).unwrap_or(3);
173    u8::try_from(marks)
174        .ok()
175        .filter(|marks| *marks <= 8)
176        .ok_or_else(|| import_error("Number of marks must be a number from 0 to 8"))
177}
178
179fn yes(value: Option<&str>) -> bool {
180    value == Some("yes")
181}
182
183fn placement_of(element: &Xml) -> Option<Placement> {
184    element
185        .get("placement")
186        .and_then(|name| Placement::from_name(name).ok())
187}
188
189/// music21's `musicXMLTypeToType`.
190fn duration_type(name: &str) -> Result<DurationType> {
191    let name = match name {
192        "long" => "longa",
193        "32th" => "32nd",
194        other => other,
195    };
196    DurationType::from_music21_name(name)
197        .ok_or_else(|| import_error(format!("found unknown MusicXML type: {name}")))
198}
199
200// ------------------------------------------------------------ what is read
201
202/// One element of a measure as it was read.
203#[derive(Clone, Debug)]
204struct Item {
205    offset: FloatType,
206    /// The order it was put into its stream, which settles the order of
207    /// two elements music21 sorts alike.
208    seq: usize,
209    staff: i32,
210    /// What spanners know it by; nothing for a copy.
211    uid: Option<usize>,
212    element: StreamElement,
213}
214
215impl Item {
216    /// music21's `classSortOrder`.
217    fn order(&self) -> i32 {
218        self.element.class_sort_order()
219    }
220
221    fn is_grace(&self) -> bool {
222        self.element.duration().is_some_and(Duration::is_grace)
223    }
224
225    /// Whether it lasts, as a note, chord or rest does: music21's
226    /// `GeneralNote` that is not a chord symbol.
227    fn is_note_or_rest(&self) -> bool {
228        matches!(
229            self.element,
230            StreamElement::Note(_)
231                | StreamElement::Chord(_)
232                | StreamElement::Rest(_)
233                | StreamElement::Unpitched(_)
234                | StreamElement::PercussionChord(_)
235        )
236    }
237}
238
239/// Elements in the order music21 sorts a stream into: by offset, then by
240/// class, grace notes ahead of the note they lean on, then as inserted.
241fn sorted(mut items: Vec<Item>) -> Vec<Item> {
242    items.sort_by(|left, right| {
243        left.offset
244            .total_cmp(&right.offset)
245            .then(left.order().cmp(&right.order()))
246            .then(right.is_grace().cmp(&left.is_grace()))
247            .then(left.seq.cmp(&right.seq))
248    });
249    items
250}
251
252#[derive(Clone, Debug, Default)]
253struct VoiceIr {
254    id: String,
255    items: Vec<Item>,
256}
257
258#[derive(Clone, Debug, Default)]
259struct MeasureIr {
260    offset: FloatType,
261    number: IntegerType,
262    suffix: Option<String>,
263    hidden: bool,
264    left: Option<Barline>,
265    right: Option<Barline>,
266    ending: Option<Ending>,
267    padding_left: FloatType,
268    padding_right: FloatType,
269    width: Option<StyleValue>,
270    measure_numbering: Option<String>,
271    items: Vec<Item>,
272    voices: Vec<VoiceIr>,
273}
274
275impl MeasureIr {
276    /// Where the last thing in the measure ends: music21's `highestTime`.
277    fn highest_time(&self) -> FloatType {
278        let end = |item: &Item| item.offset + item.element.quarter_length();
279        self.items
280            .iter()
281            .chain(self.voices.iter().flat_map(|voice| &voice.items))
282            .map(end)
283            .fold(0.0, FloatType::max)
284    }
285
286    /// Where the last thing standing in the measure itself ends, its voices
287    /// counted as empty: what music21's `highestTime` answers while the
288    /// measure is still being read, each voice's length having been worked
289    /// out before anything was put in it.
290    fn highest_time_outside_voices(&self) -> FloatType {
291        self.items
292            .iter()
293            .map(|item| item.offset + item.element.quarter_length())
294            .fold(0.0, FloatType::max)
295    }
296}
297
298/// A spanner while its ends are still being met.
299#[derive(Clone, Debug)]
300struct Building {
301    kind: SpannerKind,
302    id_local: Option<String>,
303    complete: bool,
304    uids: Vec<usize>,
305    placement: Option<Placement>,
306    line_type: Option<String>,
307    ends: LineEnds,
308    shift: Option<OctaveShift>,
309    pedal: Pedal,
310    arpeggio: ArpeggioType,
311    slide: SlideType,
312    marks: u8,
313    /// Whether a rest of several measures is drawn with symbols.
314    use_symbols: bool,
315    /// Whether it has been put into a part already.
316    placed: bool,
317}
318
319impl Building {
320    fn new(kind: SpannerKind, id_local: Option<&str>) -> Self {
321        Self {
322            kind,
323            id_local: id_local.map(str::to_string),
324            complete: false,
325            uids: Vec::new(),
326            placement: match kind {
327                SpannerKind::Crescendo | SpannerKind::Diminuendo => Some(Placement::Below),
328                SpannerKind::Ottava | SpannerKind::Line => Some(Placement::Above),
329                _ => None,
330            },
331            line_type: (kind == SpannerKind::Glissando).then(|| "wavy".to_string()),
332            ends: LineEnds::default(),
333            shift: None,
334            pedal: Pedal::default(),
335            arpeggio: ArpeggioType::Normal,
336            slide: SlideType::Chromatic,
337            marks: 3,
338            use_symbols: false,
339            placed: false,
340        }
341    }
342
343    fn add(&mut self, uid: usize) {
344        if !self.uids.contains(&uid) {
345            self.uids.push(uid);
346        }
347    }
348
349    /// music21's `insertFirstSpannedElement`.
350    fn add_first(&mut self, uid: usize) {
351        if !self.uids.contains(&uid) {
352            self.uids.insert(0, uid);
353        }
354    }
355
356    /// The finished spanner, its elements named by where they stand.
357    fn finish(&self, position_of: &dyn Fn(usize) -> Option<usize>) -> Spanner {
358        let positions: Vec<Option<usize>> = self.uids.iter().map(|uid| position_of(*uid)).collect();
359        let mut spanner = match self.kind {
360            SpannerKind::Crescendo | SpannerKind::Diminuendo => {
361                let mut wedge = Spanner::wedge(self.kind, Vec::new());
362                wedge = with_positions(wedge, positions);
363                wedge
364            }
365            SpannerKind::Line => {
366                let mut line = with_positions(Spanner::line(Vec::new()), positions);
367                line.set_ends(self.ends);
368                line
369            }
370            _ => Spanner::with_unplaced(self.kind, positions),
371        };
372        spanner.set_placement(self.placement);
373        spanner.set_id_local(self.id_local.clone());
374        spanner.set_complete(true);
375        if self.kind != SpannerKind::Crescendo && self.kind != SpannerKind::Diminuendo {
376            spanner.set_line_type(self.line_type.clone());
377        }
378        if self.kind == SpannerKind::Line {
379            spanner.set_line_type(Some(
380                self.line_type
381                    .clone()
382                    .unwrap_or_else(|| "solid".to_string()),
383            ));
384        }
385        spanner.set_shift(self.shift);
386        if self.kind == SpannerKind::PedalMark {
387            spanner.set_pedal(Some(self.pedal));
388        }
389        if self.kind == SpannerKind::Glissando {
390            spanner.set_glissando_details(Some(Glissando {
391                slide_type: self.slide,
392                label: None,
393            }));
394        }
395        if self.kind == SpannerKind::TremoloSpanner {
396            // Read already within the range a tremolo allows.
397            let _ = spanner.set_number_of_marks(Some(self.marks));
398        }
399        if self.kind == SpannerKind::MultiMeasureRest {
400            spanner.set_multi_rest(Some(MultiRest {
401                use_symbols: self.use_symbols,
402                ..MultiRest::default()
403            }));
404        }
405        spanner.set_arpeggio(self.arpeggio);
406        spanner
407    }
408}
409
410/// A spanner of the same kind and drawing over other positions.
411fn with_positions(template: Spanner, positions: Vec<Option<usize>>) -> Spanner {
412    let mut spanner = Spanner::with_unplaced(template.kind(), positions);
413    spanner.set_placement(template.placement());
414    spanner.set_line_type(template.line_type().map(str::to_string));
415    spanner.set_spread(template.spread());
416    spanner
417}
418
419/// A first or second ending while its measures are still being met:
420/// music21's `RepeatBracket`.
421#[derive(Clone, Debug)]
422struct Bracket {
423    numbers: Vec<u32>,
424    measures: Vec<usize>,
425    complete: bool,
426}
427
428/// music21's `RepeatBracket.number` setter: nothing at all is nought,
429/// which is written as no number; otherwise `1`, `1, 2` or `1-3`.
430fn bracket_numbers(text: &str) -> Vec<u32> {
431    if text.is_empty() {
432        return vec![0];
433    }
434    let number = |piece: &str| piece.trim().parse::<u32>().ok();
435    let read = if let Some((low, high)) = text.split_once('-') {
436        number(low)
437            .zip(number(high))
438            .map(|(low, high)| (low..=high).collect())
439    } else if text.contains(',') {
440        text.split(',').map(number).collect::<Option<Vec<u32>>>()
441    } else {
442        number(text).map(|number| vec![number])
443    };
444    // music21 falls back to a first ending where it cannot read one.
445    read.unwrap_or_else(|| vec![1])
446}
447
448// ----------------------------------------------------------------- importer
449
450#[derive(Default)]
451struct Importer {
452    spanners: Vec<Building>,
453    /// The spanners waiting for the next note, chord or rest, which becomes
454    /// the first thing the one at the front joins.
455    pending: std::collections::VecDeque<usize>,
456    /// Spanners in the order music21's score holds them once it is read, by
457    /// where each stands in `spanners`: one finished in a part may still be
458    /// handed the first note of the next by the queue.
459    finished: Vec<usize>,
460    next_uid: usize,
461    finale: bool,
462}
463
464struct PartGroup<'a> {
465    element: &'a Xml,
466    number: IntegerType,
467    part_ids: Vec<String>,
468}
469
470/// A part as it goes into the score, with the uid of each leaf in the order
471/// the score's leaves run.
472struct BuiltPart {
473    /// The id the part list knows it by, a staff of a part written on
474    /// several being `<part>-Staff<n>`.
475    key: String,
476    stream: Stream,
477    uids: Vec<Option<usize>>,
478}
479
480impl Importer {
481    fn uid(&mut self) -> usize {
482        self.next_uid += 1;
483        self.next_uid
484    }
485
486    /// music21's `xmlRootToScore`.
487    fn score(mut self, root: &Xml) -> Result<Stream> {
488        let metadata = self.metadata(root);
489        let defaults = root.find("defaults").map(score_layout);
490        let credits = root
491            .find_all("credit")
492            .map(text_box)
493            .collect::<Result<Vec<_>>>()?;
494        let (score_parts, groups) = part_list(root);
495
496        let mut built: Vec<BuiltPart> = Vec::new();
497        let mut staff_groups: Vec<StaffGroup> = Vec::new();
498        for part in root.find_all("part") {
499            let id = match part.get("id") {
500                Some(id) => id.to_string(),
501                None => match score_parts.first() {
502                    Some((id, _)) => id.clone(),
503                    None => continue,
504                },
505            };
506            let Some((_, score_part)) = score_parts.iter().find(|(known, _)| *known == id) else {
507                continue;
508            };
509            let first = built.len();
510            let staves = PartParser::new(&mut self, part, score_part, &id).parse()?;
511            let name = staves
512                .first()
513                .and_then(|staff| staff.stream.name().map(str::to_string));
514            let count = staves.len();
515            let split = staves
516                .first()
517                .is_some_and(|staff| staff.stream.kind() == StreamKind::PartStaff && count > 0)
518                && staves.iter().all(|staff| staff.key != id);
519            built.extend(staves);
520            if split {
521                let mut group = StaffGroup::new((first..first + count).collect());
522                group.set_name(name);
523                group.set_symbol(Some("brace"))?;
524                group.set_bar_together(Some(BarTogether::Yes));
525                group.set_name_hidden(true);
526                staff_groups.push(group);
527            }
528        }
529
530        // music21's `partGroups`.
531        for group in &groups {
532            let mut positions = Vec::new();
533            for part_id in &group.part_ids {
534                match built.iter().position(|part| part.key == *part_id) {
535                    Some(position) => positions.push(position),
536                    None => {
537                        let prefix = format!("{part_id}-Staff");
538                        let mut staves: Vec<(usize, &str)> = built
539                            .iter()
540                            .enumerate()
541                            .filter(|(_, part)| part.key.starts_with(&prefix))
542                            .map(|(position, part)| (position, part.key.as_str()))
543                            .collect();
544                        staves.sort_by(|left, right| left.1.cmp(right.1));
545                        if staves.is_empty() {
546                            return Err(import_error(format!(
547                                "Cannot find part in m21PartObjectsById dictionary by Id: {part_id}"
548                            )));
549                        }
550                        positions.extend(staves.into_iter().map(|(position, _)| position));
551                    }
552                }
553            }
554            let mut read = StaffGroup::new(positions);
555            let text = |tag: &str| {
556                group
557                    .element
558                    .find(tag)
559                    .and_then(Xml::text)
560                    .filter(|text| !text.is_empty())
561                    .map(str::to_string)
562            };
563            read.set_name(text("group-name"));
564            read.set_abbreviation(text("group-abbreviation"));
565            match group.element.find("group-symbol") {
566                Some(symbol_element) => {
567                    if let Some(symbol) = text("group-symbol") {
568                        read.set_symbol(Some(&symbol))?;
569                    }
570                    read_position(symbol_element, &mut read.style);
571                }
572                None => read.set_symbol(Some("brace"))?,
573            }
574            read.set_bar_together(Some(match text("group-barline").as_deref() {
575                Some("no") => BarTogether::No,
576                Some("Mensurstrich") => BarTogether::Mensurstrich,
577                _ => BarTogether::Yes,
578            }));
579            staff_groups.push(read);
580        }
581
582        // Whatever was completed and never put into a part goes into the
583        // score, an arpeggio across parts among them.
584        for building in &mut self.spanners {
585            if building.kind == SpannerKind::ArpeggioMark {
586                building.complete = true;
587            }
588        }
589        let late: Vec<usize> = (0..self.spanners.len())
590            .filter(|index| self.spanners[*index].complete && !self.spanners[*index].placed)
591            .collect();
592        self.finished.extend(late);
593
594        // The credits sort before the parts, as music21 sorts its text boxes,
595        // and stand before the parts' leaves where spanners count them.
596        let mut uids: Vec<Option<usize>> = vec![None; credits.len()];
597        let mut events: Vec<StreamEvent> = credits
598            .into_iter()
599            .map(|credit| StreamEvent::new(0.0, StreamElement::TextBox(Box::new(credit))))
600            .collect();
601        for part in built {
602            uids.extend(part.uids);
603            events.push(StreamEvent::new(0.0, part.stream));
604        }
605        if let Some(defaults) = defaults {
606            // music21 inserts it first, and sorts it after the parts.
607            events.push(StreamEvent::new(
608                0.0,
609                StreamElement::Layout(Layout::Score(Box::new(defaults))),
610            ));
611        }
612        let mut assembled = Stream::from_events(events);
613        assembled.set_kind(StreamKind::Score);
614        assembled.set_metadata(Some(metadata));
615        for group in staff_groups {
616            assembled.add_staff_group(group);
617        }
618        let position_of = |uid: usize| uids.iter().position(|held| *held == Some(uid));
619        for index in &self.finished {
620            assembled.add_spanner(self.spanners[*index].finish(&position_of));
621        }
622        Ok(assembled)
623    }
624
625    /// music21's `xmlMetadata`.
626    fn metadata(&mut self, root: &Xml) -> Metadata {
627        let mut metadata = Metadata::new();
628        let add = |metadata: &mut Metadata, element: &Xml, tag: &str, name: &str| {
629            if let Some(text) = element.find(tag).and_then(Xml::text)
630                && !text.is_empty()
631            {
632                metadata.add_text(name, text);
633            }
634        };
635        if let Some(work) = root.find("work") {
636            add(&mut metadata, work, "work-title", "title");
637            add(&mut metadata, work, "work-number", "number");
638            add(&mut metadata, work, "opus", "opusNumber");
639        }
640        add(&mut metadata, root, "movement-number", "movementNumber");
641        add(&mut metadata, root, "movement-title", "movementName");
642        // A title that only repeats the movement name was put there by a
643        // writer for the sake of renderers, and is taken out again.
644        let texts = |metadata: &Metadata, name: &str| -> Vec<String> {
645            metadata
646                .get(name)
647                .iter()
648                .map(|value| value.text().to_string())
649                .collect()
650        };
651        if texts(&metadata, "title") == texts(&metadata, "movementName") {
652            metadata.set("title", Vec::new());
653        }
654
655        let Some(identification) = root.find("identification") else {
656            return metadata;
657        };
658        for creator in identification.find_all("creator") {
659            let name = creator.stripped();
660            match creator.get("type") {
661                Some(role) if crate::metadata::is_contributor_unique_name(role) => {
662                    metadata.add(role, MetadataValue::with_role(name, role));
663                }
664                Some(role) => {
665                    metadata.add("otherContributor", MetadataValue::with_role(name, role));
666                }
667                None => metadata.add("otherContributor", MetadataValue::new(name)),
668            }
669        }
670        if let Some(rights) = identification.find("rights") {
671            // music21 writes a copyright that says nothing as `None`.
672            let text = match rights.text() {
673                Some(text) => text.trim(),
674                None => "None",
675            };
676            metadata.add(
677                "copyright",
678                match rights.get("type") {
679                    Some(role) => MetadataValue::with_role(text, role),
680                    None => MetadataValue::new(text),
681                },
682            );
683        }
684        if let Some(encoding) = identification.find("encoding") {
685            let mut found = false;
686            for software in encoding.find_all("software") {
687                let text = software.stripped();
688                if text.is_empty() {
689                    continue;
690                }
691                if !found && text.contains("Finale") {
692                    self.finale = true;
693                }
694                found = true;
695                metadata.add_text("software", text);
696            }
697        }
698        if let Some(miscellaneous) = identification.find("miscellaneous") {
699            for field in miscellaneous.find_all("miscellaneous-field") {
700                let Some(name) = field.get("name") else {
701                    continue;
702                };
703                let value = field.text().unwrap_or("");
704                // A namespaced name is kept under the unique name it has.
705                let unique = crate::metadata::STANDARD_PROPERTIES
706                    .iter()
707                    .find(|(unique, namespaced, _)| *unique == name || *namespaced == name)
708                    .map_or(name, |(unique, _, _)| unique);
709                metadata.add_text(unique, value);
710            }
711        }
712        metadata
713    }
714}
715
716/// music21's `parsePartList`: each `<score-part>` by id, and the groups.
717fn part_list(root: &Xml) -> (Vec<(String, &Xml)>, Vec<PartGroup<'_>>) {
718    let mut score_parts = Vec::new();
719    let mut groups: Vec<PartGroup<'_>> = Vec::new();
720    let mut open: Vec<usize> = Vec::new();
721    let Some(list) = root.find("part-list") else {
722        return (score_parts, groups);
723    };
724    for element in &list.children {
725        match element.tag.as_str() {
726            "score-part" => {
727                let id = element.get("id").unwrap_or_default().to_string();
728                for index in &open {
729                    groups[*index].part_ids.push(id.clone());
730                }
731                score_parts.push((id, element));
732            }
733            "part-group" => {
734                let number = element.get("number").and_then(parse_int).unwrap_or(1);
735                match element.get("type") {
736                    Some("start") => {
737                        open.push(groups.len());
738                        groups.push(PartGroup {
739                            element,
740                            number,
741                            part_ids: Vec::new(),
742                        });
743                    }
744                    Some("stop") => open.retain(|index| groups[*index].number != number),
745                    _ => {}
746                }
747            }
748            _ => {}
749        }
750    }
751    (score_parts, groups)
752}
753
754// --------------------------------------------------------------------- part
755
756/// music21's `PartParser`.
757struct PartParser<'a, 'x> {
758    importer: &'a mut Importer,
759    part: &'x Xml,
760    score_part: &'x Xml,
761    id: String,
762    name: Option<String>,
763    abbreviation: Option<String>,
764    name_hidden: bool,
765    abbreviation_hidden: bool,
766    /// The part's instruments with where each starts.
767    instruments: Vec<(FloatType, Instrument)>,
768    measures: Vec<MeasureIr>,
769    brackets: Vec<Bracket>,
770    multi_staff: bool,
771    max_staves: i32,
772    last_divisions: FloatType,
773    last_meter: Option<TimeSignature>,
774    last_measure_was_short: bool,
775    last_measure_offset: FloatType,
776    last_measure_number: IntegerType,
777    last_number_suffix: Option<String>,
778    last_clefs: Vec<(i32, Clef)>,
779    active_tuplets: [Option<Tuplet>; 7],
780    first_measure_parsed: bool,
781    /// music21's `PartParser.atSoundingPitch`: a part is at sounding pitch
782    /// until a `<transpose>` says otherwise.
783    at_sounding_pitch: bool,
784    /// The arpeggios across chords, by their number and the offset in the
785    /// part they stand at, each as where it stands among the spanners.
786    arpeggios: Vec<((String, FloatType), usize)>,
787    /// The rest of several measures still taking rests, by where it stands
788    /// among the spanners, and how many more it takes: music21's
789    /// `activeMultiMeasureRestSpanner` and `multiMeasureRestsToCapture`.
790    multi_rest: Option<usize>,
791    multi_rests_left: i64,
792    /// The rests of several measures that have stopped taking rests, in
793    /// the order they stopped.
794    multi_rests: Vec<usize>,
795}
796
797impl<'a, 'x> PartParser<'a, 'x> {
798    fn new(importer: &'a mut Importer, part: &'x Xml, score_part: &'x Xml, id: &str) -> Self {
799        let multi_staff = part.find_all("measure").any(|measure| {
800            measure.find_all("attributes").any(|attributes| {
801                attributes
802                    .find_all("staves")
803                    .any(|staves| parse_int(staves.stripped()).is_some_and(|count| count > 1))
804            })
805        });
806        Self {
807            importer,
808            part,
809            score_part,
810            id: id.to_string(),
811            name: None,
812            abbreviation: None,
813            name_hidden: false,
814            abbreviation_hidden: false,
815            instruments: Vec::new(),
816            measures: Vec::new(),
817            brackets: Vec::new(),
818            multi_staff,
819            max_staves: 1,
820            last_divisions: 10080.0,
821            last_meter: None,
822            last_measure_was_short: false,
823            last_measure_offset: 0.0,
824            last_measure_number: 0,
825            last_number_suffix: None,
826            last_clefs: Vec::new(),
827            active_tuplets: Default::default(),
828            first_measure_parsed: false,
829            at_sounding_pitch: true,
830            arpeggios: Vec::new(),
831            multi_rest: None,
832            multi_rests_left: 0,
833            multi_rests: Vec::new(),
834        }
835    }
836
837    /// music21's `applyMultiMeasureRest`: a rest read joins the rest of
838    /// several measures still taking rests, which goes into the part once
839    /// it has taken as many as it said.
840    fn take_multi_rest(&mut self, uid: usize) {
841        let Some(index) = self.multi_rest else {
842            return;
843        };
844        self.importer.spanners[index].add(uid);
845        self.multi_rests_left -= 1;
846        if self.multi_rests_left == 0 {
847            self.end_multi_rest();
848        }
849    }
850
851    /// Puts the rest of several measures still taking rests into the part,
852    /// whether or not it took all it said it would.
853    ///
854    /// One cut short by the part's end or by the next stands in the part as
855    /// music21's does, counting the rests it took; one that took none is
856    /// left out.
857    fn end_multi_rest(&mut self) {
858        let Some(index) = self.multi_rest.take() else {
859            return;
860        };
861        let building = &mut self.importer.spanners[index];
862        if building.uids.is_empty() {
863            return;
864        }
865        building.complete = true;
866        building.placed = true;
867        self.multi_rests.push(index);
868    }
869
870    /// music21's `PartParser.parse`: the part, or one part for each of its
871    /// staves.
872    fn parse(mut self) -> Result<Vec<BuiltPart>> {
873        self.score_part_info()?;
874        let part = self.part;
875        let mut last_forward: Option<(usize, bool)> = None;
876        for measure in part.find_all("measure") {
877            // What goes wrong inside a measure says which one, by the number
878            // the document gives it, and in which part.
879            last_forward = self.measure(measure).map_err(|error| {
880                error.placed(|| {
881                    Position::measure(measure.get("number").unwrap_or("")).in_part(&self.id)
882                })
883            })?;
884        }
885        self.remove_finale_ending_rest(last_forward);
886        self.end_multi_rest();
887
888        // Endings, from the brackets over the measures.
889        for bracket in &self.brackets {
890            for (place, index) in bracket.measures.iter().enumerate() {
891                let measure = &mut self.measures[*index];
892                if measure.ending.is_none() {
893                    measure.ending = Some(Ending::new(
894                        bracket.numbers.clone(),
895                        place == 0,
896                        place + 1 == bracket.measures.len(),
897                    ));
898                }
899            }
900        }
901
902        // Spanners completed by now belong to this part, octave lines last
903        // and rests of several measures first, music21 having put each of
904        // those in as it was completed.
905        let mut ours: Vec<usize> = std::mem::take(&mut self.multi_rests);
906        for pass in [false, true] {
907            for (index, building) in self.importer.spanners.iter_mut().enumerate() {
908                let ottava = building.kind == SpannerKind::Ottava;
909                if building.complete && !building.placed && ottava == pass {
910                    building.placed = true;
911                    ours.push(index);
912                }
913            }
914        }
915        self.importer.finished.extend(ours);
916
917        if self.max_staves > 1 {
918            self.separate_staves()
919        } else {
920            let kind = if self.multi_staff {
921                StreamKind::PartStaff
922            } else {
923                StreamKind::Part
924            };
925            let measures = std::mem::take(&mut self.measures);
926            let built = self.build(kind, None, measures, &self.instruments.clone());
927            Ok(vec![BuiltPart {
928                key: self.id.clone(),
929                stream: built.0,
930                uids: built.1,
931            }])
932        }
933    }
934
935    /// Puts a part together out of its measures.
936    fn build(
937        &self,
938        kind: StreamKind,
939        id: Option<String>,
940        measures: Vec<MeasureIr>,
941        instruments: &[(FloatType, Instrument)],
942    ) -> (Stream, Vec<Option<usize>>) {
943        let mut items: Vec<(Item, Vec<Option<usize>>)> = Vec::new();
944        let mut seq = 0;
945        for (offset, instrument) in instruments {
946            items.push((
947                Item {
948                    offset: *offset,
949                    seq,
950                    staff: NO_STAFF,
951                    uid: None,
952                    element: instrument.clone().into(),
953                },
954                vec![None],
955            ));
956            seq += 1;
957        }
958        for measure in measures {
959            let offset = measure.offset;
960            let (stream, uids) = build_measure(measure);
961            items.push((
962                Item {
963                    offset,
964                    seq,
965                    staff: NO_STAFF,
966                    uid: None,
967                    element: stream.into(),
968                },
969                uids,
970            ));
971            seq += 1;
972        }
973        items.sort_by(|(left, _), (right, _)| {
974            left.offset
975                .total_cmp(&right.offset)
976                .then(left.order().cmp(&right.order()))
977                .then(left.seq.cmp(&right.seq))
978        });
979        let mut uids = Vec::new();
980        let mut events = Vec::new();
981        for (item, leaf_uids) in items {
982            uids.extend(leaf_uids);
983            events.push(StreamEvent::new(item.offset, item.element));
984        }
985        let mut stream = Stream::from_events(events);
986        stream.set_kind(kind);
987        stream.set_id(id.or_else(|| {
988            self.instruments
989                .first()
990                .and_then(|(_, instrument)| instrument.best_name().map(str::to_string))
991        }));
992        stream.set_name(self.name.clone());
993        stream.set_abbreviation(self.abbreviation.clone());
994        // A staff split off a part is music21's `template` of it, which
995        // copies this.
996        stream.set_at_sounding_pitch(Some(self.at_sounding_pitch));
997        // A staff split off a part is a fresh stream, which prints both.
998        if kind != StreamKind::PartStaff || self.max_staves <= 1 {
999            stream.set_name_hidden(self.name_hidden);
1000            stream.set_abbreviation_hidden(self.abbreviation_hidden);
1001        }
1002        (stream, uids)
1003    }
1004
1005    /// music21's `parseXmlScorePart` and `getDefaultInstrument`.
1006    fn score_part_info(&mut self) -> Result<()> {
1007        let clean = |text: &str| text.trim().replace('\n', " ");
1008        let score_part = self.score_part;
1009        let named = |tag: &str| {
1010            score_part
1011                .find(tag)
1012                .and_then(Xml::text)
1013                .filter(|text| !text.is_empty())
1014                .map(clean)
1015        };
1016        let part_name = named("part-name");
1017        let part_abbreviation = named("part-abbreviation");
1018        let name_hidden = score_part
1019            .find("part-name")
1020            .is_some_and(|name| name.get("print-object") == Some("no"));
1021        let abbreviation_hidden = score_part
1022            .find("part-abbreviation")
1023            .is_some_and(|name| name.get("print-object") == Some("no"));
1024
1025        let adjusted = |text: &str| -> Option<u8> {
1026            parse_int(text).map(|value| value.saturating_sub(1).max(0) as u8)
1027        };
1028        let mut instrument: Option<Instrument> = None;
1029        let mut unpitched = false;
1030        if let Some(midi) = score_part.find("midi-instrument") {
1031            let unpitched_text = midi.child_text("midi-unpitched");
1032            let program_text = midi.child_text("midi-program");
1033            if !unpitched_text.is_empty() {
1034                unpitched = true;
1035                let pitch = adjusted(unpitched_text).unwrap_or(0);
1036                instrument = Some(percussion_instrument(pitch));
1037            } else if !program_text.is_empty() {
1038                let mut read = adjusted(program_text)
1039                    .and_then(|program| Instrument::from_midi_program(program).ok())
1040                    .unwrap_or_default();
1041                if let Some(channel) = midi
1042                    .find("midi-channel")
1043                    .and_then(Xml::text)
1044                    .and_then(adjusted)
1045                {
1046                    read.set_midi_channel(Some(channel));
1047                }
1048                instrument = Some(read);
1049            }
1050        }
1051        let mut instrument = instrument.unwrap_or_default();
1052        let score_instrument = score_part.find("score-instrument");
1053        if let Some(score_instrument) = score_instrument
1054            && !unpitched
1055        {
1056            let name = score_instrument.child_text("instrument-name");
1057            if !name.is_empty() {
1058                let mut from_name =
1059                    Instrument::from_name(name, SearchLanguage::All).unwrap_or_default();
1060                from_name.set_midi_channel(instrument.midi_channel());
1061                if from_name.midi_program() == instrument.midi_program() || instrument.is_a("Piano")
1062                {
1063                    instrument = from_name;
1064                }
1065            }
1066        }
1067        instrument.set_part_id(Some(self.id.clone()));
1068        instrument.set_part_name(part_name.clone());
1069        instrument.set_part_abbreviation(part_abbreviation.clone());
1070        if let Some(score_instrument) = score_instrument {
1071            let named = |tag: &str| {
1072                score_instrument
1073                    .find(tag)
1074                    .and_then(Xml::text)
1075                    .filter(|text| !text.is_empty())
1076            };
1077            if let Some(name) = named("instrument-name") {
1078                instrument.set_name(Some(clean(name)));
1079            }
1080            if let Some(abbreviation) = named("instrument-abbreviation") {
1081                instrument.set_abbreviation(Some(clean(abbreviation)));
1082            }
1083            if let Some(sound) = named("instrument-sound") {
1084                instrument.set_sound(Some(sound.to_string()));
1085            }
1086        }
1087        self.name = part_name;
1088        self.abbreviation = part_abbreviation;
1089        self.name_hidden = name_hidden;
1090        self.abbreviation_hidden = abbreviation_hidden;
1091        self.instruments.push((0.0, instrument));
1092        Ok(())
1093    }
1094
1095    /// music21's `xmlMeasureToMeasure`. Hands back where a rest a `<forward>`
1096    /// of Finale's made stands, when it is the last thing read.
1097    fn measure(&mut self, element: &'x Xml) -> Result<Option<(usize, bool)>> {
1098        let mut parser = MeasureParser::new(self, element);
1099        parser.parse()?;
1100        let MeasureParser {
1101            mut measure,
1102            staves,
1103            transposition,
1104            full_measure_rest,
1105            marked_full,
1106            finale_forward,
1107            use_voices,
1108            divisions,
1109            ..
1110        } = parser;
1111        self.last_divisions = divisions;
1112        self.max_staves = self.max_staves.max(staves);
1113        if let Some(transposition) = transposition {
1114            self.update_transposition(transposition);
1115        }
1116        self.first_measure_parsed = true;
1117
1118        // music21's `setLastMeasureInfo`.
1119        if measure.number != self.last_measure_number {
1120            self.last_measure_number = measure.number;
1121            self.last_number_suffix = measure.suffix.clone();
1122        }
1123        let own_meter = measure.items.iter().find_map(|item| match &item.element {
1124            StreamElement::TimeSignature(meter) => Some(meter.clone()),
1125            _ => None,
1126        });
1127        if let Some(meter) = own_meter {
1128            self.last_meter = Some(meter);
1129        } else if self.last_meter.is_none() {
1130            self.last_meter = Some(TimeSignature::new(4, 4)?);
1131        }
1132        let bar_length = self
1133            .last_meter
1134            .as_ref()
1135            .map_or(4.0, TimeSignature::bar_quarter_length);
1136
1137        if full_measure_rest {
1138            // The first rest as music21 walks the measure: what stands in
1139            // the measure itself in order, each voice read through where it
1140            // stands, which is at the start and after the signatures.
1141            let mut walk: Vec<(FloatType, i32, bool, usize, Option<usize>)> = Vec::new();
1142            for (index, voice) in measure.voices.iter().enumerate() {
1143                let first = sorted(voice.items.clone())
1144                    .into_iter()
1145                    .find(|item| matches!(item.element, StreamElement::Rest(_)))
1146                    .and_then(|item| item.uid);
1147                walk.push((0.0, 5, false, index, first));
1148            }
1149            for item in &measure.items {
1150                let rest = matches!(item.element, StreamElement::Rest(_));
1151                walk.push((
1152                    item.offset,
1153                    item.order(),
1154                    !item.is_grace(),
1155                    item.seq + measure.voices.len(),
1156                    item.uid.filter(|_| rest),
1157                ));
1158            }
1159            walk.sort_by(|left, right| {
1160                left.0
1161                    .total_cmp(&right.0)
1162                    .then(left.1.cmp(&right.1))
1163                    .then(left.2.cmp(&right.2))
1164                    .then(left.3.cmp(&right.3))
1165            });
1166            let first_uid = walk.into_iter().find_map(|entry| entry.4);
1167            let first = measure
1168                .items
1169                .iter_mut()
1170                .chain(measure.voices.iter_mut().flat_map(|voice| &mut voice.items))
1171                .filter(|item| first_uid.is_some() && item.uid == first_uid)
1172                .find_map(|item| match &mut item.element {
1173                    StreamElement::Rest(rest) => Some((item.uid, rest)),
1174                    _ => None,
1175                });
1176            if let Some((uid, rest)) = first {
1177                let marked_full = uid.is_some_and(|uid| marked_full.contains(&uid));
1178                let duration = rest.duration().clone();
1179                let whole = matches!(
1180                    duration.type_and_dots(),
1181                    Some((DurationType::Whole | DurationType::Breve, 0))
1182                ) && duration.tuplets().is_empty();
1183                if marked_full || (duration.quarter_length() != bar_length && whole) {
1184                    rest.set_duration(Duration::new(bar_length)?);
1185                    rest.set_full_measure(crate::rest::FullMeasure::Yes);
1186                }
1187            }
1188        }
1189
1190        // music21 sums the lengths as floats and snaps where it inserts.
1191        measure.offset = op_frac(self.last_measure_offset);
1192        let index = self.measures.len();
1193
1194        // music21's `adjustTimeAttributesFromMeasure`.
1195        let highest = measure.highest_time();
1196        let has_notes = measure
1197            .items
1198            .iter()
1199            .chain(measure.voices.iter().flat_map(|voice| &voice.items))
1200            .any(Item::is_note_or_rest);
1201        let shift = if highest == bar_length {
1202            highest
1203        } else if highest > bar_length {
1204            let difference = highest - bar_length;
1205            let near = |unit: FloatType| {
1206                let multiple = (difference / unit).round() * unit;
1207                (difference - multiple).abs() < 1e-6
1208            };
1209            if difference > 0.5 || near(0.0625) || near(1.0 / 12.0) {
1210                highest
1211            } else {
1212                bar_length
1213            }
1214        } else if highest == 0.0 && !has_notes {
1215            // Some writers leave the rest out of an empty measure.
1216            let uid = self.importer.uid();
1217            measure.items.push(Item {
1218                offset: 0.0,
1219                seq: usize::MAX,
1220                staff: NO_STAFF,
1221                uid: Some(uid),
1222                element: Rest::new(Duration::new(bar_length)?).into(),
1223            });
1224            self.last_measure_was_short = false;
1225            bar_length
1226        } else {
1227            // A short measure is a pickup where it opens the part or follows
1228            // another short one, and is cut short at its end otherwise.
1229            // music21 pads a pickup as `padAsAnacrusis` does, by the share
1230            // of the bar it lacks, and a short measure by what it lacks,
1231            // unsnapped.
1232            let anacrusis = || op_frac(bar_length * (1.0 - op_frac(highest / bar_length)));
1233            if self.last_measure_offset == 0.0 {
1234                if highest < bar_length {
1235                    measure.padding_left = anacrusis();
1236                }
1237            } else if self.last_measure_was_short {
1238                if highest < bar_length {
1239                    measure.padding_left = anacrusis();
1240                    self.last_measure_was_short = false;
1241                }
1242            } else {
1243                if highest < bar_length {
1244                    measure.padding_right = bar_length - op_frac(highest);
1245                }
1246                self.last_measure_was_short = highest < bar_length;
1247            }
1248            highest
1249        };
1250        self.last_measure_offset += shift;
1251        self.measures.push(measure);
1252        Ok(finale_forward.map(|_| (index, !use_voices)))
1253    }
1254
1255    /// music21's `removeFinaleIncorrectEndingForwardRest`: a hidden rest
1256    /// Finale's `<forward>` left as the last thing in the last measure.
1257    fn remove_finale_ending_rest(&mut self, last_forward: Option<(usize, bool)>) {
1258        let Some((index, true)) = last_forward else {
1259            return;
1260        };
1261        let Some(measure) = self.measures.get_mut(index) else {
1262            return;
1263        };
1264        let last = sorted(measure.items.clone()).into_iter().rfind(|item| {
1265            item.is_note_or_rest() || matches!(item.element, StreamElement::ChordSymbol(_))
1266        });
1267        if let Some(last) = last
1268            && matches!(&last.element, StreamElement::Rest(rest) if rest.hidden())
1269        {
1270            measure.items.retain(|item| item.seq != last.seq);
1271        }
1272    }
1273
1274    /// music21's `updateTransposition`.
1275    fn update_transposition(&mut self, transposition: Interval) {
1276        let active = self.instruments.last().cloned();
1277        match active {
1278            Some((_, instrument))
1279                if instrument.transposition().is_none() && !self.first_measure_parsed => {}
1280            Some((_, instrument)) => {
1281                let differs = instrument
1282                    .transposition()
1283                    .is_none_or(|held| held.directed_name() != transposition.directed_name());
1284                if differs {
1285                    self.instruments
1286                        .push((self.last_measure_offset, instrument.clone()));
1287                }
1288            }
1289            None => self
1290                .instruments
1291                .push((self.last_measure_offset, Instrument::new())),
1292        }
1293        if let Some((_, instrument)) = self.instruments.last_mut() {
1294            instrument.set_transposition(Some(transposition));
1295        }
1296        self.at_sounding_pitch = false;
1297    }
1298
1299    /// music21's `separateOutPartStaves`: a part written on several staves
1300    /// becomes one part for each, what is written on no staff in particular
1301    /// going into every one.
1302    fn separate_staves(&mut self) -> Result<Vec<BuiltPart>> {
1303        let mut keys: Vec<i32> = Vec::new();
1304        for measure in &self.measures {
1305            for item in measure
1306                .items
1307                .iter()
1308                .chain(measure.voices.iter().flat_map(|voice| &voice.items))
1309            {
1310                if item.staff != NO_STAFF && !keys.contains(&item.staff) {
1311                    keys.push(item.staff);
1312                }
1313            }
1314        }
1315        keys.sort_unstable();
1316
1317        let source = std::mem::take(&mut self.measures);
1318        let mut taken: Vec<usize> = Vec::new();
1319        let mut out = Vec::new();
1320        for key in keys {
1321            let mut measures = Vec::new();
1322            for measure in &source {
1323                let mut copy = MeasureIr {
1324                    offset: measure.offset,
1325                    number: measure.number,
1326                    suffix: measure.suffix.clone(),
1327                    // A measure made for a staff shows its number, whatever
1328                    // the one it was made from did.
1329                    hidden: false,
1330                    left: measure.left.clone(),
1331                    right: measure.right.clone(),
1332                    ending: measure.ending.clone(),
1333                    padding_left: measure.padding_left,
1334                    padding_right: measure.padding_right,
1335                    width: measure.width.clone(),
1336                    // music21 makes the staff's measures from a template,
1337                    // which keeps nothing of how the measure is drawn.
1338                    measure_numbering: None,
1339                    items: Vec::new(),
1340                    voices: Vec::new(),
1341                };
1342                let mut seq = 0usize;
1343                let mut take = |items: &[Item], into: &mut Vec<Item>, seq: &mut usize| {
1344                    for item in sorted(items.to_vec()) {
1345                        if item.staff != NO_STAFF && item.staff != key {
1346                            continue;
1347                        }
1348                        let mut item = item;
1349                        // The element itself goes to the first staff that
1350                        // takes it, and a copy to every one after.
1351                        if taken.contains(&item.seq_key()) {
1352                            item.uid = None;
1353                        } else {
1354                            taken.push(item.seq_key());
1355                        }
1356                        item.seq = *seq;
1357                        *seq += 1;
1358                        into.push(item);
1359                    }
1360                };
1361                take(&measure.items, &mut copy.items, &mut seq);
1362                for voice in &measure.voices {
1363                    let mut items = Vec::new();
1364                    take(&voice.items, &mut items, &mut seq);
1365                    copy.voices.push(VoiceIr {
1366                        id: voice.id.clone(),
1367                        items,
1368                    });
1369                }
1370                // music21's `flattenUnnecessaryVoices`.
1371                copy.voices.retain(|voice| !voice.items.is_empty());
1372                if copy.voices.len() == 1 {
1373                    let voice = copy.voices.remove(0);
1374                    for mut item in sorted(voice.items) {
1375                        item.seq = seq;
1376                        seq += 1;
1377                        copy.items.push(item);
1378                    }
1379                }
1380                measures.push(copy);
1381            }
1382            let id = format!("{}-Staff{key}", self.id);
1383            let instruments = self.instruments.clone();
1384            let (stream, uids) = self.build(
1385                StreamKind::PartStaff,
1386                Some(id.clone()),
1387                measures,
1388                &instruments,
1389            );
1390            out.push(BuiltPart {
1391                key: id,
1392                stream,
1393                uids,
1394            });
1395        }
1396        Ok(out)
1397    }
1398}
1399
1400impl Item {
1401    /// A number no two elements read share, whatever measure or voice they
1402    /// stand in: the uid where there is one.
1403    fn seq_key(&self) -> usize {
1404        self.uid.unwrap_or(usize::MAX)
1405    }
1406}
1407
1408/// A measure put together: the stream, and the uid of each leaf in order.
1409fn build_measure(measure: MeasureIr) -> (Stream, Vec<Option<usize>>) {
1410    let mut entries: Vec<(Item, Vec<Option<usize>>)> = Vec::new();
1411    for (index, voice) in measure.voices.into_iter().enumerate() {
1412        let mut uids = Vec::new();
1413        let mut events = Vec::new();
1414        for item in sorted(voice.items) {
1415            uids.push(item.uid);
1416            events.push(StreamEvent::new(item.offset, item.element));
1417        }
1418        let mut stream = Stream::from_events(events);
1419        stream.set_kind(StreamKind::Voice);
1420        stream.set_id(Some(voice.id));
1421        entries.push((
1422            Item {
1423                offset: 0.0,
1424                // Voices are made before anything is read into the measure.
1425                seq: index,
1426                staff: NO_STAFF,
1427                uid: None,
1428                element: stream.into(),
1429            },
1430            uids,
1431        ));
1432    }
1433    let voices = entries.len();
1434    for mut item in measure.items {
1435        item.seq = item.seq.saturating_add(voices);
1436        let uid = item.uid;
1437        entries.push((item, vec![uid]));
1438    }
1439    entries.sort_by(|(left, _), (right, _)| {
1440        left.offset
1441            .total_cmp(&right.offset)
1442            .then(left.order().cmp(&right.order()))
1443            .then(right.is_grace().cmp(&left.is_grace()))
1444            .then(left.seq.cmp(&right.seq))
1445    });
1446    let mut uids = Vec::new();
1447    let mut events = Vec::new();
1448    for (item, leaf_uids) in entries {
1449        uids.extend(leaf_uids);
1450        events.push(StreamEvent::new(item.offset, item.element));
1451    }
1452    let mut stream = Stream::from_events(events);
1453    stream.set_kind(StreamKind::Measure);
1454    stream.set_number(measure.number);
1455    stream.set_number_suffix(measure.suffix);
1456    stream.set_number_hidden(measure.hidden);
1457    stream.set_left_barline(measure.left);
1458    stream.set_right_barline(measure.right);
1459    stream.set_ending(measure.ending);
1460    stream.set_padding_left(measure.padding_left);
1461    stream.set_padding_right(measure.padding_right);
1462    stream.set_layout_width(measure.width);
1463    stream.set_measure_numbering(measure.measure_numbering);
1464    (stream, uids)
1465}
1466
1467// ------------------------------------------------------------------ measure
1468
1469/// music21's `MeasureParser`.
1470struct MeasureParser<'p, 'a, 'x> {
1471    part: &'p mut PartParser<'a, 'x>,
1472    element: &'x Xml,
1473    measure: MeasureIr,
1474    seq: usize,
1475    divisions: FloatType,
1476    staves: i32,
1477    transposition: Option<Interval>,
1478    use_voices: bool,
1479    last_voice: Option<String>,
1480    offset: FloatType,
1481    /// Notes of a chord being gathered, with the lyrics written on them.
1482    chord_notes: Vec<&'x Xml>,
1483    chord_lyrics: Vec<&'x Xml>,
1484    /// The uid of the last note, chord or rest read.
1485    last_note: Option<usize>,
1486    rests: usize,
1487    notes: usize,
1488    full_measure_rest: bool,
1489    /// The rests that said they fill their measure.
1490    marked_full: Vec<usize>,
1491    /// Whether the rest being read said so.
1492    marking_full: bool,
1493    /// The seq of the last thing read, when it was a rest made of Finale's
1494    /// `<forward>`.
1495    finale_forward: Option<usize>,
1496    pedal_starts: Vec<(usize, FloatType)>,
1497    /// The staff layouts read here, by staff number and offset, each with
1498    /// what it was put in as, where it was put anywhere: music21's
1499    /// `staffLayoutObjects`.
1500    staff_layouts: Vec<((i64, FloatType), Option<usize>)>,
1501}
1502
1503impl<'p, 'a, 'x> MeasureParser<'p, 'a, 'x> {
1504    fn new(part: &'p mut PartParser<'a, 'x>, element: &'x Xml) -> Self {
1505        let divisions = part.last_divisions;
1506        Self {
1507            part,
1508            element,
1509            measure: MeasureIr::default(),
1510            seq: 0,
1511            divisions,
1512            staves: 1,
1513            transposition: None,
1514            use_voices: false,
1515            last_voice: None,
1516            offset: 0.0,
1517            chord_notes: Vec::new(),
1518            chord_lyrics: Vec::new(),
1519            last_note: None,
1520            rests: 0,
1521            notes: 0,
1522            full_measure_rest: false,
1523            marked_full: Vec::new(),
1524            marking_full: false,
1525            finale_forward: None,
1526            pedal_starts: Vec::new(),
1527            staff_layouts: Vec::new(),
1528        }
1529    }
1530
1531    fn parse(&mut self) -> Result<()> {
1532        self.measure_attributes();
1533        self.voice_information();
1534        let element = self.element;
1535        for (index, child) in element.children.iter().enumerate() {
1536            match child.tag.as_str() {
1537                "note" => self.note(child, element.children.get(index + 1))?,
1538                "backup" => {
1539                    if let Some(ticks) = parse_float(child.child_text("duration")) {
1540                        self.offset = op_frac(self.offset - ticks / self.divisions).max(0.0);
1541                    }
1542                }
1543                "forward" => self.forward(child)?,
1544                "direction" => self.direction(child)?,
1545                "attributes" => self.attributes(child)?,
1546                "harmony" => self.harmony(child)?,
1547                "sound" => {
1548                    if child.get("tempo").is_some() {
1549                        let total = self.offset + self.offset_of(child);
1550                        self.sound_tempo(child, None, staff_number(child), total);
1551                    }
1552                }
1553                "barline" => self.barline(child)?,
1554                "print" => self.print(child),
1555                _ => {}
1556            }
1557        }
1558        if self.rests == 1 && self.notes == 0 {
1559            self.full_measure_rest = true;
1560        }
1561        Ok(())
1562    }
1563
1564    /// Puts an element into the measure itself.
1565    fn insert(
1566        &mut self,
1567        offset: FloatType,
1568        staff: i32,
1569        element: impl Into<StreamElement>,
1570    ) -> usize {
1571        let uid = self.part.importer.uid();
1572        self.measure.items.push(Item {
1573            offset: op_frac(offset),
1574            seq: self.seq,
1575            staff,
1576            uid: Some(uid),
1577            element: element.into(),
1578        });
1579        self.seq += 1;
1580        uid
1581    }
1582
1583    /// music21's `insertInMeasureOrVoice`, for a note, chord or rest.
1584    fn insert_note(&mut self, source: &Xml, staff: i32, element: StreamElement) -> usize {
1585        let uid = self.part.importer.uid();
1586        let item = Item {
1587            offset: self.offset,
1588            seq: self.seq,
1589            staff,
1590            uid: Some(uid),
1591            element,
1592        };
1593        self.seq += 1;
1594        if self.use_voices {
1595            let written = source.child_text("voice");
1596            let voice = if written.is_empty() {
1597                self.last_voice.clone().unwrap_or_else(|| "1".to_string())
1598            } else {
1599                self.last_voice = Some(written.to_string());
1600                written.to_string()
1601            };
1602            if let Some(found) = self.measure.voices.iter_mut().find(|held| held.id == voice) {
1603                found.items.push(item);
1604                return uid;
1605            }
1606        }
1607        self.measure.items.push(item);
1608        uid
1609    }
1610
1611    /// music21's `parseMeasureAttributes` and `parseMeasureNumbers`.
1612    fn measure_attributes(&mut self) {
1613        self.measure.hidden = yes(self.element.get("implicit"));
1614        self.measure.width = self.element.get("width").map(StyleValue::read);
1615        let raw = self.element.get("number").unwrap_or("");
1616        // music21's `getNumFromStr`: the digits, and whatever is left.
1617        let digits: String = raw.chars().filter(char::is_ascii_digit).collect();
1618        let rest: String = raw.chars().filter(|c| !c.is_ascii_digit()).collect();
1619        if let Ok(number) = digits.parse::<IntegerType>() {
1620            self.measure.number = number;
1621        }
1622        if !rest.is_empty() {
1623            self.measure.suffix = Some(rest);
1624        }
1625        let last = self.part.last_measure_number;
1626        if self.measure.suffix.as_deref() == Some("X")
1627            && Some(self.measure.number) != last.checked_add(1)
1628        {
1629            let mut suffix = format!("X{}", self.measure.number);
1630            if let Some(before) = &self.part.last_number_suffix {
1631                suffix = format!("{before}{suffix}");
1632            }
1633            self.measure.number = last;
1634            self.measure.suffix = Some(suffix);
1635        }
1636    }
1637
1638    /// music21's `updateVoiceInformation`: a measure naming more than one
1639    /// voice holds a voice for each.
1640    fn voice_information(&mut self) {
1641        let mut ids: Vec<String> = Vec::new();
1642        for tag in ["note", "forward"] {
1643            for element in self.element.find_all(tag) {
1644                let voice = element.child_text("voice");
1645                if !voice.is_empty() && !ids.iter().any(|held| held == voice) {
1646                    ids.push(voice.to_string());
1647                }
1648            }
1649        }
1650        if ids.len() > 1 {
1651            ids.sort();
1652            self.measure.voices = ids
1653                .into_iter()
1654                .map(|id| VoiceIr {
1655                    id,
1656                    items: Vec::new(),
1657                })
1658                .collect();
1659            self.use_voices = true;
1660        }
1661    }
1662
1663    /// music21's `xmlToOffset`.
1664    fn offset_of(&self, element: &Xml) -> FloatType {
1665        parse_float(element.child_text("offset")).map_or(0.0, |ticks| ticks / self.divisions)
1666    }
1667
1668    /// music21's `xmlForward`.
1669    fn forward(&mut self, element: &'x Xml) -> Result<()> {
1670        let Some(ticks) = parse_float(element.child_text("duration")) else {
1671            return Ok(());
1672        };
1673        let change = op_frac(ticks / self.divisions);
1674        if self.part.importer.finale && change != 0.0 {
1675            // Finale writes a hidden rest as a `<forward>`.
1676            let mut rest = Rest::new(Duration::new(change)?);
1677            rest.set_hidden(true);
1678            let seq = self.seq;
1679            self.insert_note(element, staff_number(element), rest.into());
1680            self.finale_forward = Some(seq);
1681        }
1682        self.offset = op_frac(self.offset + change);
1683        Ok(())
1684    }
1685
1686    /// music21's `parseAttributesTag`.
1687    fn attributes(&mut self, attributes: &'x Xml) -> Result<()> {
1688        for child in &attributes.children {
1689            match child.tag.as_str() {
1690                "time" => {
1691                    if let Some(meter) = time_signature(child)? {
1692                        self.insert(self.offset, staff_number(child), meter);
1693                    }
1694                }
1695                "clef" => {
1696                    let clef = clef_of(child)?;
1697                    let staff = staff_number(child);
1698                    self.insert(self.offset, staff, clef.clone());
1699                    self.part.last_clefs.retain(|(held, _)| *held != staff);
1700                    self.part.last_clefs.push((staff, clef));
1701                }
1702                "key" => {
1703                    let key = key_signature(child)?;
1704                    self.insert(self.offset, staff_number(child), key);
1705                }
1706                "divisions" => {
1707                    if let Some(divisions) = parse_float(child.stripped()) {
1708                        self.divisions = op_frac(divisions);
1709                    }
1710                }
1711                "staves" => {
1712                    if let Some(staves) = parse_int(child.stripped()) {
1713                        self.staves = staves;
1714                    }
1715                }
1716                "transpose" => self.transposition = Some(transpose_interval(child)?),
1717                "staff-details" => self.staff_details(child),
1718                "measure-style" => self.measure_style(child)?,
1719                _ => {}
1720            }
1721        }
1722        Ok(())
1723    }
1724
1725    /// music21's `handleMeasureStyle`: a rest of several measures, which
1726    /// takes the next so many rests the part reads. Its font is the page's.
1727    fn measure_style(&mut self, style: &'x Xml) -> Result<()> {
1728        let Some(multiple) = style.find("multiple-rest") else {
1729            return Ok(());
1730        };
1731        let count = parse_int(multiple.stripped()).ok_or_else(|| {
1732            import_error(format!(
1733                "invalid literal for int() with base 10: '{}'",
1734                multiple.stripped()
1735            ))
1736        })?;
1737        self.part.end_multi_rest();
1738        let mut building = Building::new(SpannerKind::MultiMeasureRest, None);
1739        building.use_symbols = multiple.get("use-symbols") == Some("yes");
1740        self.part.importer.spanners.push(building);
1741        self.part.multi_rest = Some(self.part.importer.spanners.len() - 1);
1742        self.part.multi_rests_left = i64::from(count);
1743        Ok(())
1744    }
1745
1746    /// music21's `xmlPrint`: a page layout where the print starts a page or
1747    /// says how one is laid out, a system layout where it starts a system,
1748    /// says how one is laid out, or does not concern a page, and a staff
1749    /// layout for each numbered staff it lays out.
1750    fn print(&mut self, print: &'x Xml) {
1751        let says = |name: &str| print.get(name).is_some_and(|value| value != "no");
1752        let page = says("new-page")
1753            || print.get("page-number").is_some()
1754            || print.find("page-layout").is_some();
1755        let system = says("new-system") || print.find("system-layout").is_some();
1756        if page {
1757            let mut read = PageLayout {
1758                is_new: print.get("new-page").map(|value| value == "yes"),
1759                page_number: print
1760                    .get("page-number")
1761                    .and_then(|value| value.trim().parse().ok()),
1762                ..PageLayout::default()
1763            };
1764            if let Some(written) = print.find("page-layout") {
1765                read_page_layout(written, &mut read);
1766            }
1767            self.insert(0.0, NO_STAFF, StreamElement::Layout(Layout::Page(read)));
1768        }
1769        if system || !page {
1770            let mut read = SystemLayout {
1771                is_new: print.get("new-system").map(|value| value == "yes"),
1772                ..SystemLayout::default()
1773            };
1774            if let Some(written) = print.find("system-layout") {
1775                read_system_layout(written, &mut read);
1776            }
1777            self.insert(0.0, NO_STAFF, StreamElement::Layout(Layout::System(read)));
1778        }
1779        for written in print.find_all("staff-layout") {
1780            let read = staff_layout(written);
1781            let key = (read.staff_number.unwrap_or(1), self.offset);
1782            // music21 keeps it to be added to before knowing whether it is
1783            // put anywhere: one with no number is not, and details for its
1784            // staff added to it are lost with it.
1785            let Some(number) = read.staff_number else {
1786                self.staff_layouts.push((key, None));
1787                continue;
1788            };
1789            let uid = self.insert(
1790                0.0,
1791                i32::try_from(number).unwrap_or(NO_STAFF),
1792                StreamElement::Layout(Layout::Staff(read)),
1793            );
1794            self.staff_layouts.push((key, Some(uid)));
1795        }
1796        if let Some(numbering) = print.find("measure-numbering") {
1797            self.measure.measure_numbering = numbering.text().map(str::to_string);
1798        }
1799    }
1800
1801    /// music21's `handleStaffDetails`: the details of a staff read into the
1802    /// staff layout already standing for it here, or into a new one.
1803    fn staff_details(&mut self, details: &'x Xml) {
1804        let number = details
1805            .get("number")
1806            .and_then(|value| value.trim().parse::<i64>().ok());
1807        let key = (number.unwrap_or(1), self.offset);
1808        let existing = self
1809            .staff_layouts
1810            .iter()
1811            .rev()
1812            .find(|(known, _)| known.0 == key.0 && known.1 == key.1)
1813            .map(|(_, uid)| *uid);
1814        match existing {
1815            Some(Some(uid)) => {
1816                if let Some(item) = self
1817                    .measure
1818                    .items
1819                    .iter_mut()
1820                    .find(|item| item.uid == Some(uid))
1821                    && let StreamElement::Layout(Layout::Staff(layout)) = &mut item.element
1822                {
1823                    read_staff_details(details, layout);
1824                }
1825            }
1826            // Added to one never put anywhere.
1827            Some(None) => {}
1828            None => {
1829                let mut read = StaffLayout::default();
1830                read_staff_details(details, &mut read);
1831                let uid = self.insert(
1832                    self.offset,
1833                    staff_number(details),
1834                    StreamElement::Layout(Layout::Staff(read)),
1835                );
1836                self.staff_layouts.push((key, Some(uid)));
1837            }
1838        }
1839    }
1840
1841    // ----------------------------------------------------------- notes
1842
1843    /// music21's `xmlToNote`.
1844    fn note(&mut self, element: &'x Xml, next: Option<&'x Xml>) -> Result<()> {
1845        let next_is_chord =
1846            next.is_some_and(|next| next.tag == "note" && next.find("chord").is_some());
1847        let is_rest = element.find("rest").is_some();
1848        let mut is_chord = element.find("chord").is_some();
1849        if next_is_chord {
1850            is_chord = true;
1851            let voice = element.child_text("voice");
1852            if element.find("voice").is_some() && !voice.is_empty() {
1853                self.last_voice = Some(voice.to_string());
1854            }
1855        }
1856
1857        let mut increment = 0.0;
1858        if is_chord {
1859            self.chord_notes.push(element);
1860            self.chord_lyrics.extend(element.find_all("lyric"));
1861        } else {
1862            let staff = staff_number(element);
1863            let (mut read, uid) = if is_rest {
1864                self.rests += 1;
1865                let rest = self.rest(element)?;
1866                (ReadNote::Rest(rest), None)
1867            } else {
1868                self.notes += 1;
1869                let (read, uid) = self.simple_note(element, true)?;
1870                (read, Some(uid))
1871            };
1872            let lyrics = lyrics_from(element.find_all("lyric"))?;
1873            let element_value: StreamElement = match &mut read {
1874                ReadNote::Rest(rest) => {
1875                    *rest.lyrics_mut() = lyrics;
1876                    rest.clone().into()
1877                }
1878                ReadNote::Note(note) => {
1879                    *note.lyrics_mut() = lyrics;
1880                    note.clone().into()
1881                }
1882                ReadNote::Unpitched(stroke) => {
1883                    *stroke.written_mut().lyrics_mut() = lyrics;
1884                    stroke.clone().into()
1885                }
1886            };
1887            increment = element_value.quarter_length();
1888            let placed = self.insert_note(element, staff, element_value);
1889            match uid {
1890                // The note was known to its spanners by a number of its own
1891                // before it had a place.
1892                Some(early) => self.rename(early, placed),
1893                None => self.free_pending(placed),
1894            }
1895            if is_rest {
1896                self.part.take_multi_rest(placed);
1897                self.rest_spanners(element, placed)?;
1898                if std::mem::take(&mut self.marking_full) {
1899                    self.marked_full.push(placed);
1900                }
1901            }
1902            self.last_note = Some(placed);
1903        }
1904
1905        if !self.chord_notes.is_empty() && !next_is_chord {
1906            let members = std::mem::take(&mut self.chord_notes);
1907            let lyrics = lyrics_from(std::mem::take(&mut self.chord_lyrics).into_iter())?;
1908            let (chord, member_uids) = self.chord(&members, lyrics)?;
1909            increment = chord.quarter_length();
1910            let source = members
1911                .iter()
1912                .find(|member| member.find("voice").is_some())
1913                .copied()
1914                .unwrap_or(element);
1915            let placed = self.insert_note(source, staff_number(members[0]), chord);
1916            for early in member_uids {
1917                self.rename(early, placed);
1918            }
1919            self.free_pending(placed);
1920            self.last_note = Some(placed);
1921        }
1922
1923        self.offset = op_frac(self.offset + increment);
1924        self.finale_forward = None;
1925        Ok(())
1926    }
1927
1928    /// Spanners that knew an element by one number know it by another.
1929    fn rename(&mut self, from: usize, to: usize) {
1930        for building in &mut self.part.importer.spanners {
1931            let mut renamed: Vec<usize> = Vec::new();
1932            for uid in &building.uids {
1933                let uid = if *uid == from { to } else { *uid };
1934                if !renamed.contains(&uid) {
1935                    renamed.push(uid);
1936                }
1937            }
1938            building.uids = renamed;
1939        }
1940    }
1941
1942    /// music21's `freePendingSpannedElementAssignment`.
1943    fn free_pending(&mut self, uid: usize) {
1944        let importer = &mut *self.part.importer;
1945        if let Some(index) = importer.pending.pop_front() {
1946            importer.spanners[index].add_first(uid);
1947        }
1948    }
1949
1950    /// music21's `xmlToSimpleNote`: a note or a stroke, known to the
1951    /// spanners by the number handed back.
1952    fn simple_note(&mut self, element: &'x Xml, free_spanners: bool) -> Result<(ReadNote, usize)> {
1953        let duration = self.duration(element)?;
1954        let mut note = match element.find("unpitched") {
1955            None => Note::from_pitch(pitch_of(element)?),
1956            Some(unpitched) => {
1957                let step = unpitched
1958                    .child_text("display-step")
1959                    .chars()
1960                    .next()
1961                    .unwrap_or('B');
1962                let octave = parse_int(unpitched.child_text("display-octave")).unwrap_or(4);
1963                Unpitched::displayed_at(step, octave)?.written().clone()
1964            }
1965        };
1966        note.set_duration(duration);
1967
1968        let beams: Vec<&Xml> = element.find_all("beam").collect();
1969        if !beams.is_empty() {
1970            note.set_beams(beams_of(&beams)?);
1971        }
1972        if let Some(stem) = element.find("stem") {
1973            note.set_stem_direction(StemDirection::from_name(stem.stripped())?);
1974        }
1975        if let Some(notehead) = element.find("notehead") {
1976            if let Some(text) = notehead.text().filter(|text| !text.is_empty()) {
1977                note.set_notehead(Notehead::from_name(text)?);
1978            }
1979            if let Some(filled) = notehead.get("filled") {
1980                note.set_notehead_fill(Some(filled == "yes"));
1981            }
1982            if let Some(color) = notehead.get("color") {
1983                note.set_color(Some(color.to_string()));
1984            }
1985            if let Some(parentheses) = notehead.get("parentheses") {
1986                note.set_notehead_parenthesis(parentheses == "yes");
1987            }
1988        }
1989
1990        let uid = self.part.importer.uid();
1991        if free_spanners {
1992            // music21 hands a waiting spanner the note as first read, and a
1993            // grace note is a copy made after that: the spanner is left
1994            // holding a note no stream holds.
1995            let joined = if element.find("grace").is_some() {
1996                self.part.importer.uid()
1997            } else {
1998                uid
1999            };
2000            self.free_pending(joined);
2001        }
2002        let mut general = General {
2003            color: note.color().map(str::to_string),
2004            ..General::default()
2005        };
2006        self.general_note(element, &mut general, note.duration().cloned(), uid, false)?;
2007        note.set_color(general.color);
2008        note.set_hidden(general.hidden);
2009        if let Some(volume) = general.volume {
2010            note.set_volume(Some(volume));
2011        }
2012        note.set_size(general.size);
2013        note.set_tie(general.tie);
2014        if let Some(duration) = general.duration {
2015            note.set_duration(duration);
2016        }
2017        *note.articulations_mut() = general.articulations;
2018        *note.expressions_mut() = general.expressions;
2019
2020        Ok(if element.find("unpitched").is_some() {
2021            let mut stroke = Unpitched::new();
2022            *stroke.written_mut() = note;
2023            (ReadNote::Unpitched(stroke), uid)
2024        } else {
2025            (ReadNote::Note(note), uid)
2026        })
2027    }
2028
2029    /// music21's `xmlToChord`.
2030    fn chord(
2031        &mut self,
2032        members: &[&'x Xml],
2033        lyrics: Vec<Lyric>,
2034    ) -> Result<(StreamElement, Vec<usize>)> {
2035        let mut notes: Vec<(ReadNote, usize)> = Vec::new();
2036        for member in members {
2037            notes.push(self.simple_note(member, false)?);
2038        }
2039        let percussion = members
2040            .iter()
2041            .any(|member| member.find("unpitched").is_some());
2042
2043        // The chord takes the first note's beams, and each kind of mark
2044        // once, read off the notes lowest first.
2045        let mut order: Vec<usize> = (0..notes.len()).collect();
2046        order.sort_by(|left, right| {
2047            let ps = |index: &usize| notes[*index].0.written().pitch().ps();
2048            ps(left).total_cmp(&ps(right))
2049        });
2050        let mut articulations: Vec<Articulation> = Vec::new();
2051        let mut expressions: Vec<Expression> = Vec::new();
2052        let mut uids = Vec::new();
2053        for index in order {
2054            let (read, uid) = &notes[index];
2055            uids.push(*uid);
2056            for articulation in read.written().articulations() {
2057                let repeats = ["Fingering", "StringIndication", "FretIndication"]
2058                    .iter()
2059                    .any(|class| articulation.is_a(class));
2060                if !repeats
2061                    && articulations
2062                        .iter()
2063                        .any(|held| held.kind() == articulation.kind())
2064                {
2065                    continue;
2066                }
2067                articulations.push(articulation.clone());
2068            }
2069            for expression in read.written().expressions() {
2070                let same = |held: &Expression| match (held, expression) {
2071                    (Expression::Ornament(left), Expression::Ornament(right)) => {
2072                        left.kind() == right.kind()
2073                    }
2074                    (Expression::Fermata(_), Expression::Fermata(_)) => true,
2075                    (Expression::Arpeggio(_), Expression::Arpeggio(_)) => true,
2076                    _ => false,
2077                };
2078                if !expressions.iter().any(same) {
2079                    expressions.push(expression.clone());
2080                }
2081            }
2082        }
2083        let beams = notes
2084            .first()
2085            .map(|(read, _)| read.written().beams().clone())
2086            .unwrap_or_default();
2087        let duration = notes
2088            .first()
2089            .and_then(|(read, _)| read.written().duration().cloned());
2090        let stem = notes
2091            .first()
2092            .map_or(StemDirection::Unspecified, |(read, _)| {
2093                read.written().stem_direction()
2094            });
2095        for (read, _) in &mut notes {
2096            let written = read.written_mut();
2097            written.articulations_mut().clear();
2098            written.expressions_mut().clear();
2099        }
2100        if let Some((first, _)) = notes.first_mut() {
2101            first.written_mut().set_beams(Beams::new());
2102            // A chord's lyrics are kept on its first note.
2103            *first.written_mut().lyrics_mut() = lyrics;
2104        }
2105
2106        let element: StreamElement = if percussion {
2107            let members: Vec<PercussionNote> = notes
2108                .into_iter()
2109                .map(|(read, _)| match read {
2110                    ReadNote::Unpitched(stroke) => PercussionNote::Unpitched(stroke),
2111                    ReadNote::Note(note) => PercussionNote::Note(note),
2112                    ReadNote::Rest(_) => unreachable!("a chord holds no rests"),
2113                })
2114                .collect();
2115            let mut chord = PercussionChord::new(members)?;
2116            let written = chord.written_mut();
2117            if let Some(duration) = duration {
2118                written.set_duration(duration);
2119            }
2120            written.set_beams(beams);
2121            *written.articulations_mut() = articulations;
2122            *written.expressions_mut() = expressions;
2123            chord.into()
2124        } else {
2125            let members: Vec<Note> = notes
2126                .into_iter()
2127                .map(|(read, _)| match read {
2128                    ReadNote::Note(note) => note,
2129                    _ => unreachable!("a chord of pitches holds notes"),
2130                })
2131                .collect();
2132            let mut chord = Chord::new(members)?;
2133            if let Some(duration) = duration {
2134                chord.set_duration(duration);
2135            }
2136            chord.set_beams(beams);
2137            // A chord reads its stem off its first note where it has none.
2138            let _ = stem;
2139            *chord.articulations_mut() = articulations;
2140            *chord.expressions_mut() = expressions;
2141            chord.into()
2142        };
2143        Ok((element, uids))
2144    }
2145
2146    /// music21's `xmlToRest`.
2147    fn rest(&mut self, element: &'x Xml) -> Result<Rest> {
2148        let duration = self.duration(element)?;
2149        let mut rest = Rest::new(duration);
2150        let tag = element
2151            .find("rest")
2152            .ok_or_else(|| import_error("a rest without a <rest> tag"))?;
2153        if tag.get("measure") == Some("yes") {
2154            let written = element.child_text("type");
2155            if written.is_empty() || matches!(written, "whole" | "breve") {
2156                self.full_measure_rest = true;
2157                self.marking_full = true;
2158                rest.set_full_measure(crate::rest::FullMeasure::Yes);
2159            }
2160        }
2161        let step = tag.child_text("display-step");
2162        if !step.is_empty() {
2163            let name = format!("{step}{}", tag.child_text("display-octave"));
2164            let displayed = Pitch::from_name(&name)?;
2165            let staff = match staff_number(element) {
2166                NO_STAFF => 1,
2167                staff => staff,
2168            };
2169            let middle = self
2170                .part
2171                .last_clefs
2172                .iter()
2173                .find(|(held, _)| *held == staff)
2174                .and_then(|(_, clef)| clef.lowest_line().filter(|_| clef.places_pitches()))
2175                .map_or(35, |lowest| lowest.saturating_add(4));
2176            let shift = displayed
2177                .checked_diatonic_note_number()
2178                .and_then(|position| position.checked_sub(middle))
2179                .ok_or_else(|| import_error(format!("a rest shown at {name} is too far off")))?;
2180            rest.set_step_shift(shift);
2181        }
2182        let mut general = General::default();
2183        let uid = 0;
2184        self.general_note(
2185            element,
2186            &mut general,
2187            Some(rest.duration().clone()),
2188            uid,
2189            true,
2190        )?;
2191        rest.set_hidden(general.hidden);
2192        rest.set_color(general.color);
2193        rest.set_size(general.size);
2194        if let Some(duration) = general.duration {
2195            rest.set_duration(duration);
2196        }
2197        *rest.articulations_mut() = general.articulations;
2198        *rest.expressions_mut() = general.expressions;
2199        Ok(rest)
2200    }
2201
2202    /// The spanners a rest's notations start or stop, which can only be
2203    /// joined once the rest has a number.
2204    fn rest_spanners(&mut self, element: &'x Xml, uid: usize) -> Result<()> {
2205        for notations in element.find_all("notations") {
2206            self.notation_spanners(notations, uid)?;
2207        }
2208        Ok(())
2209    }
2210
2211    /// music21's `xmlNoteToGeneralNoteHelper`.
2212    fn general_note(
2213        &mut self,
2214        element: &'x Xml,
2215        general: &mut General,
2216        duration: Option<Duration>,
2217        uid: usize,
2218        is_rest: bool,
2219    ) -> Result<()> {
2220        if let Some(color) = element.get("color") {
2221            general.color = Some(color.to_string());
2222        }
2223        general.hidden = element.get("print-object") == Some("no");
2224        if !is_rest && let Some(dynamics) = element.get("dynamics").and_then(parse_float) {
2225            general.volume = Some(Volume::from_velocity_scalar(dynamics * (90.0 / 12700.0))?);
2226        }
2227        if element.get("pizzicato") == Some("yes")
2228            && let Some(kind) = ArticulationKind::from_class_name("Pizzicato")
2229        {
2230            general.articulations.push(Articulation::of_kind(kind));
2231        }
2232        if let Some(written) = element.find("type")
2233            && let Some(size) = written.get("size")
2234        {
2235            general.size = NoteSize::from_name(size);
2236        }
2237        if element.find("tie").is_some() {
2238            general.tie = tie_of(element);
2239        }
2240        if let Some(grace) = element.find("grace") {
2241            // A grace note is written as an eighth where nothing says.
2242            let base = duration.unwrap_or_default();
2243            let mut written = Duration::default();
2244            written.clear();
2245            written.set_tuplets(Vec::new());
2246            for (kind, dots) in base.components() {
2247                written.add_duration_tuple(kind, dots);
2248            }
2249            let unsaid = base.components().is_empty();
2250            if unsaid {
2251                written.add_duration_tuple(DurationType::Eighth, 0);
2252            }
2253            let mut value = written.grace_duration();
2254            let mut marks = Grace::new();
2255            marks.set_value_unsaid(unsaid);
2256            marks.set_slash(matches!(grace.get("slash"), Some("yes") | None));
2257            let share = |name: &str| {
2258                grace
2259                    .get(name)
2260                    .and_then(parse_int)
2261                    .map(|percent| FloatType::from(percent) / 100.0)
2262            };
2263            marks.set_steal_time_previous(share("steal-time-previous"));
2264            marks.set_steal_time_following(share("steal-time-following"));
2265            value.set_grace(Some(marks));
2266            general.duration = Some(value);
2267        }
2268        for notations in element.find_all("notations") {
2269            self.notations(notations, general)?;
2270            if !is_rest {
2271                self.notation_spanners(notations, uid)?;
2272            }
2273        }
2274        Ok(())
2275    }
2276
2277    /// music21's `xmlToDuration`.
2278    fn duration(&mut self, element: &'x Xml) -> Result<Duration> {
2279        let quarter_length = match element.find("duration") {
2280            Some(duration) => {
2281                let ticks = parse_float(duration.stripped())
2282                    .ok_or_else(|| import_error("a <duration> that is not a number"))?;
2283                op_frac(ticks / self.divisions)
2284            }
2285            None => 0.0,
2286        };
2287        let written = element.child_text("type");
2288        if written.is_empty() {
2289            // Some rests say how long they last and nothing else.
2290            return Duration::new(quarter_length);
2291        }
2292        let kind = duration_type(written)?;
2293        let dots = element.find_all("dot").count() as u32;
2294        let tuplets = if element.find("time-modification").is_some() {
2295            self.tuplets(element)?
2296        } else {
2297            Vec::new()
2298        };
2299        if kind.quarter_length_with_dots(dots) == quarter_length && tuplets.is_empty() {
2300            return Duration::new(quarter_length);
2301        }
2302        // Made from the length first, as music21 makes it, so its writing
2303        // counts as worked out even with the written value put in its place.
2304        let mut duration = Duration::new(quarter_length)?;
2305        duration.clear();
2306        duration.set_tuplets(Vec::new());
2307        duration.add_duration_tuple(kind, dots);
2308        duration.set_tuplets(tuplets);
2309        if (duration.quarter_length() - quarter_length).abs() > 1e-7 {
2310            duration.set_linked(false);
2311            duration.set_quarter_length(quarter_length)?;
2312        }
2313        Ok(duration)
2314    }
2315
2316    /// music21's `xmlToTuplets`.
2317    fn tuplets(&mut self, element: &'x Xml) -> Result<Vec<Tuplet>> {
2318        let modification = element
2319            .find("time-modification")
2320            .ok_or_else(|| import_error("Note without time-modification in xmlToTuplets"))?;
2321        let count = |tag: &str, fallback: u32| {
2322            parse_int(modification.child_text(tag)).map_or(fallback, |value| value.max(1) as u32)
2323        };
2324        let normal_type = match modification.child_text("normal-type") {
2325            "" => element.child_text("type"),
2326            written => written,
2327        };
2328        let kind = duration_type(normal_type)?;
2329        let dots = modification.find_all("normal-dot").count() as u32;
2330        let time_mod = Tuplet::new(
2331            count("actual-notes", 3),
2332            count("normal-notes", 2),
2333            kind,
2334            dots,
2335        );
2336
2337        let notations = element.find("notations");
2338        if notations.is_none() {
2339            self.part.active_tuplets[0] = Some(time_mod);
2340        }
2341        let multiplier = |tuplet: &Tuplet| -> (i64, i64) {
2342            // actual notes in the time of normal: each lasts normal/actual,
2343            // scaled by the written values of the two sides.
2344            let ratio = tuplet.multiplier();
2345            (
2346                i64::from(*ratio.numer().unwrap_or(&1)),
2347                i64::from(*ratio.denom().unwrap_or(&1)),
2348            )
2349        };
2350        let divide = |remaining: (i64, i64), by: (i64, i64)| -> (i64, i64) {
2351            reduce(remaining.0 * by.1, remaining.1 * by.0)
2352        };
2353        let mut remaining = multiplier(&time_mod);
2354        let mut returned: [Option<Tuplet>; 8] = Default::default();
2355        // Which of the returned tuplets are the active ones themselves.
2356        let mut from_active = [false; 8];
2357        let mut to_remove: Vec<usize> = Vec::new();
2358        let mut to_stop: Vec<usize> = Vec::new();
2359
2360        if let Some(notations) = notations {
2361            for written in notations.find_all("tuplet") {
2362                let index = written
2363                    .get("number")
2364                    .and_then(parse_int)
2365                    .map_or(1, |number| number.clamp(0, 6) as usize);
2366                let tuplet_type = written.get("type");
2367                if tuplet_type == Some("stop") {
2368                    if let Some(active) = self.part.active_tuplets[index] {
2369                        // One that started on this very note starts and
2370                        // stops on it. music21 asks by value, and so finds
2371                        // an equal tuplet of another number too.
2372                        if returned.iter().flatten().any(|held| *held == active)
2373                            && from_active[index]
2374                            && let Some(held) = &mut returned[index]
2375                        {
2376                            held.set_tuplet_type(Some(TupletType::StartStop));
2377                        }
2378                        to_remove.push(index);
2379                        to_stop.push(index);
2380                    }
2381                    continue;
2382                }
2383                let actual = written.find("tuplet-actual");
2384                let normal = written.find("tuplet-normal");
2385                let mut tuplet = match (actual, normal) {
2386                    (Some(actual), Some(normal)) => {
2387                        let number = |side: &Xml, fallback: u32| {
2388                            parse_int(side.child_text("tuplet-number"))
2389                                .map_or(fallback, |value| value.max(1) as u32)
2390                        };
2391                        let mut tuplet = Tuplet::ratio(number(actual, 3), number(normal, 2));
2392                        let side = |side: &Xml| -> Result<Option<(DurationType, u32)>> {
2393                            match side.find("tuplet-type") {
2394                                Some(written) if written.text().is_some() => Ok(Some((
2395                                    duration_type(written.stripped())?,
2396                                    written.find_all("tuplet-dot").count() as u32,
2397                                ))),
2398                                _ => Ok(None),
2399                            }
2400                        };
2401                        if let Some(value) = side(actual)? {
2402                            tuplet.set_duration_actual(Some(value));
2403                        }
2404                        if let Some(value) = side(normal)? {
2405                            tuplet.set_duration_normal(Some(value));
2406                        }
2407                        tuplet
2408                    }
2409                    _ => time_mod,
2410                };
2411                tuplet
2412                    .set_tuplet_type(tuplet_type.and_then(|name| TupletType::from_name(name).ok()));
2413                let bracket = written.get("bracket");
2414                if let Some(bracket) = bracket {
2415                    tuplet.set_bracket(if bracket == "yes" {
2416                        TupletBracket::Bracket
2417                    } else {
2418                        TupletBracket::None
2419                    });
2420                }
2421                let show_number = written.get("show-number");
2422                if show_number == Some("none") {
2423                    tuplet.set_actual_show(None);
2424                    if bracket.is_none() {
2425                        tuplet.set_bracket(TupletBracket::None);
2426                    }
2427                } else if show_number == Some("both") {
2428                    tuplet.set_normal_show(Some(TupletShow::Number));
2429                }
2430                let both_or_type = |shown: Option<TupletShow>| {
2431                    Some(if shown.is_some() {
2432                        TupletShow::Both
2433                    } else {
2434                        TupletShow::Type
2435                    })
2436                };
2437                if written.get("show-type") == Some("actual") {
2438                    tuplet.set_actual_show(both_or_type(tuplet.actual_show()));
2439                } else if show_number == Some("both") {
2440                    tuplet.set_actual_show(both_or_type(tuplet.actual_show()));
2441                    tuplet.set_normal_show(both_or_type(tuplet.normal_show()));
2442                }
2443                if written.get("line-shape") == Some("curved") {
2444                    tuplet.set_bracket(TupletBracket::Slur);
2445                }
2446                tuplet.set_placement(placement_of(written));
2447                remaining = divide(remaining, multiplier(&tuplet));
2448                returned[index] = Some(tuplet);
2449                from_active[index] = true;
2450                self.part.active_tuplets[index] = Some(tuplet);
2451            }
2452        }
2453
2454        for index in 1..self.part.active_tuplets.len() {
2455            let Some(active) = &self.part.active_tuplets[index] else {
2456                continue;
2457            };
2458            if returned.iter().flatten().any(|held| held == active) {
2459                continue;
2460            }
2461            let mut copy = *active;
2462            copy.set_tuplet_type(if to_stop.contains(&index) {
2463                Some(TupletType::Stop)
2464            } else {
2465                None
2466            });
2467            remaining = divide(remaining, multiplier(&copy));
2468            returned[index] = Some(copy);
2469        }
2470        if remaining != (1, 1) {
2471            let mut remainder = Tuplet::ratio(remaining.1.max(1) as u32, remaining.0.max(1) as u32);
2472            remainder.set_duration_normal(time_mod.duration_normal());
2473            remainder.set_duration_actual(time_mod.duration_actual());
2474            returned[7] = Some(remainder);
2475        }
2476        for index in to_remove {
2477            self.part.active_tuplets[index] = None;
2478        }
2479        Ok(returned.into_iter().flatten().collect())
2480    }
2481
2482    // ------------------------------------------------------- notations
2483
2484    /// music21's `xmlNotations`, apart from the spanners.
2485    fn notations(&mut self, notations: &'x Xml, general: &mut General) -> Result<()> {
2486        for technical in notations.find_all("technical") {
2487            for mark in &technical.children {
2488                if let Some(read) = technical_of(mark) {
2489                    general.articulations.push(read);
2490                }
2491            }
2492        }
2493        for articulations in notations.find_all("articulations") {
2494            for mark in &articulations.children {
2495                if let Some(read) = articulation_of(mark) {
2496                    general.articulations.push(read);
2497                }
2498            }
2499        }
2500        for written in notations.find_all("fermata") {
2501            let mut fermata = Fermata::new();
2502            match written.get("type") {
2503                Some("upright") => fermata.set_fermata_type(FermataType::Upright),
2504                Some("inverted") => fermata.set_fermata_type(FermataType::Inverted),
2505                _ => {}
2506            }
2507            let shape = written.stripped();
2508            if !shape.is_empty() {
2509                fermata.set_shape(Some(shape.to_string()));
2510            }
2511            general.expressions.push(Expression::Fermata(fermata));
2512        }
2513        for tag in ["arpeggiate", "non-arpeggiate"] {
2514            for written in notations.find_all(tag) {
2515                let kind = if tag == "non-arpeggiate" {
2516                    ArpeggioType::NonArpeggio
2517                } else {
2518                    ArpeggioType::from_name(written.get("direction").unwrap_or("normal"))?
2519                };
2520                match written.get("number") {
2521                    None => general.expressions.push(Expression::Arpeggio(kind)),
2522                    Some(number) => general.arpeggio_spanners.push((number.to_string(), kind)),
2523                }
2524            }
2525        }
2526        let mut latest: Option<usize> = None;
2527        for ornaments in notations.find_all("ornaments") {
2528            for mark in &ornaments.children {
2529                if mark.tag == "accidental-mark" {
2530                    let Some(index) = latest else {
2531                        continue;
2532                    };
2533                    let mut accidental = accidental_of(mark)?;
2534                    accidental.set_display_status(Some(true));
2535                    if let Expression::Ornament(ornament) = &mut general.expressions[index] {
2536                        if ornament.is_a("Turn") {
2537                            if mark.get("placement") == Some("below") {
2538                                ornament.set_lower_accidental(Some(accidental));
2539                            } else {
2540                                ornament.set_upper_accidental(Some(accidental));
2541                            }
2542                        } else if ornament.is_a("GeneralMordent") || ornament.is_a("Trill") {
2543                            ornament.set_accidental(Some(accidental))?;
2544                        }
2545                    }
2546                    continue;
2547                }
2548                let class = match mark.tag.as_str() {
2549                    "trill-mark" => "Trill",
2550                    "turn" | "delayed-turn" => "Turn",
2551                    "inverted-turn" | "delayed-inverted-turn" => "InvertedTurn",
2552                    "shake" => "Shake",
2553                    "mordent" => "Mordent",
2554                    "inverted-mordent" => "InvertedMordent",
2555                    "schleifer" => "Schleifer",
2556                    "other-ornament" => "Ornament",
2557                    "tremolo" => {
2558                        if matches!(mark.get("type"), Some("start" | "stop")) {
2559                            continue;
2560                        }
2561                        "Tremolo"
2562                    }
2563                    _ => continue,
2564                };
2565                let Some(kind) = OrnamentKind::from_class_name(class) else {
2566                    continue;
2567                };
2568                let mut ornament = Ornament::of_kind(kind);
2569                match mark.tag.as_str() {
2570                    "delayed-turn" | "delayed-inverted-turn" => {
2571                        ornament.set_delay(OrnamentDelay::Default);
2572                    }
2573                    "turn" | "inverted-turn" => ornament.set_delay(OrnamentDelay::NoDelay),
2574                    "tremolo" => {
2575                        ornament.set_number_of_marks(tremolo_marks(mark)?)?;
2576                    }
2577                    _ => {}
2578                }
2579                if mark.tag != "tremolo" {
2580                    if let Some(placement) = mark.get("placement") {
2581                        ornament.set_placement(Some(placement.to_string()));
2582                    }
2583                    latest = Some(general.expressions.len());
2584                }
2585                general
2586                    .expressions
2587                    .push(Expression::Ornament(Box::new(ornament)));
2588            }
2589        }
2590        Ok(())
2591    }
2592
2593    /// music21's `xmlNotationsToSpanners` and the arpeggios across chords.
2594    fn notation_spanners(&mut self, notations: &'x Xml, uid: usize) -> Result<()> {
2595        for tag in ["arpeggiate", "non-arpeggiate"] {
2596            for written in notations.find_all(tag) {
2597                let Some(number) = written.get("number") else {
2598                    continue;
2599                };
2600                let kind = if tag == "non-arpeggiate" {
2601                    ArpeggioType::NonArpeggio
2602                } else {
2603                    ArpeggioType::from_name(written.get("direction").unwrap_or("normal"))
2604                        .unwrap_or(ArpeggioType::Normal)
2605                };
2606                // A number tells apart arpeggios struck together, which may
2607                // cross staves but not parts.
2608                let key = (
2609                    number.to_string(),
2610                    op_frac(self.part.last_measure_offset + self.offset),
2611                );
2612                let found = self
2613                    .part
2614                    .arpeggios
2615                    .iter()
2616                    .find(|(held, _)| *held == key)
2617                    .map(|(_, index)| *index);
2618                let spanners = &mut self.part.importer.spanners;
2619                let index = found.unwrap_or_else(|| {
2620                    let mut building = Building::new(SpannerKind::ArpeggioMark, Some(number));
2621                    building.arpeggio = kind;
2622                    spanners.push(building);
2623                    spanners.len() - 1
2624                });
2625                if found.is_none() {
2626                    self.part.arpeggios.push((key, index));
2627                }
2628                self.part.importer.spanners[index].add(uid);
2629            }
2630        }
2631        for ornaments in notations.find_all("ornaments") {
2632            for mark in &ornaments.children {
2633                match mark.tag.as_str() {
2634                    "wavy-line" => {
2635                        self.one_spanner(mark, Some(uid), SpannerKind::TrillExtension, false);
2636                    }
2637                    // music21's `xmlToTremolo`: a tremolo that starts or
2638                    // stops is one between notes.
2639                    "tremolo" if matches!(mark.get("type"), Some("start" | "stop")) => {
2640                        let index =
2641                            self.one_spanner(mark, Some(uid), SpannerKind::TremoloSpanner, false);
2642                        self.part.importer.spanners[index].marks = tremolo_marks(mark)?;
2643                    }
2644                    _ => {}
2645                }
2646            }
2647        }
2648        for slur in notations.find_all("slur") {
2649            let index = self.one_spanner(slur, Some(uid), SpannerKind::Slur, false);
2650            let building = &mut self.part.importer.spanners[index];
2651            if let Some(line_type) = slur.get("line-type") {
2652                building.line_type = Some(line_type.to_string());
2653            }
2654            if let Some(placement) = placement_of(slur) {
2655                building.placement = Some(placement);
2656            }
2657        }
2658        for tag in ["glissando", "slide"] {
2659            for written in notations.find_all(tag) {
2660                let index = self.one_spanner(written, Some(uid), SpannerKind::Glissando, false);
2661                let building = &mut self.part.importer.spanners[index];
2662                if tag == "slide" {
2663                    building.slide = SlideType::Continuous;
2664                }
2665                match written.get("line-type") {
2666                    Some(line_type) => {
2667                        let lower = line_type.to_lowercase();
2668                        if !["solid", "dashed", "dotted", "wavy"].contains(&lower.as_str()) {
2669                            return Err(import_error(format!("not a valid value: {line_type}")));
2670                        }
2671                        building.line_type = Some(lower);
2672                    }
2673                    None if tag == "slide" => building.line_type = Some("solid".to_string()),
2674                    None => {}
2675                }
2676            }
2677        }
2678        Ok(())
2679    }
2680
2681    /// music21's `xmlOneSpanner`: the open spanner of a kind and number, or
2682    /// a new one.
2683    fn one_spanner(
2684        &mut self,
2685        element: &Xml,
2686        target: Option<usize>,
2687        kind: SpannerKind,
2688        allow_duplicates: bool,
2689    ) -> usize {
2690        let id = element.get("number");
2691        let spanners = &mut self.part.importer.spanners;
2692        let found = spanners
2693            .iter()
2694            .position(|held| held.kind == kind && !held.complete && held.id_local.as_deref() == id);
2695        let index = match found {
2696            Some(index) if !allow_duplicates => index,
2697            _ => {
2698                let mut building = Building::new(kind, id);
2699                if let Some(placement) = placement_of(element) {
2700                    building.placement = Some(placement);
2701                }
2702                spanners.push(building);
2703                spanners.len() - 1
2704            }
2705        };
2706        if let Some(target) = target {
2707            spanners[index].add(target);
2708        }
2709        if element.get("type") == Some("stop") {
2710            spanners[index].complete = true;
2711        }
2712        index
2713    }
2714
2715    // ------------------------------------------------------ directions
2716
2717    /// music21's `xmlDirection`.
2718    fn direction(&mut self, direction: &'x Xml) -> Result<()> {
2719        let total = self.offset_of(direction) + self.offset;
2720        let staff = staff_number(direction);
2721        let mut metronome_added = false;
2722        for direction_type in direction.find_all("direction-type") {
2723            for specific in &direction_type.children {
2724                self.direction_type(specific, direction, staff, total)?;
2725                if specific.tag == "metronome" {
2726                    metronome_added = true;
2727                }
2728            }
2729        }
2730        if !metronome_added
2731            && let Some(sound) = direction
2732                .find_all("sound")
2733                .find(|sound| sound.get("tempo").is_some())
2734        {
2735            self.sound_tempo(sound, Some(direction), staff, total);
2736        }
2737        Ok(())
2738    }
2739
2740    /// music21's `setSoundTempo`: how fast the music is played, where no
2741    /// mark says.
2742    fn sound_tempo(&mut self, sound: &Xml, direction: Option<&Xml>, staff: i32, total: FloatType) {
2743        let Some(tempo) = sound
2744            .get("tempo")
2745            .and_then(parse_float)
2746            .filter(|qpm| *qpm != 0.0)
2747        else {
2748            return;
2749        };
2750        let mut mark = MetronomeMark::default().with_number_sounding(tempo);
2751        if let Some(direction) = direction {
2752            mark.set_placement(placement_of(direction));
2753        }
2754        self.insert(total, staff, mark);
2755    }
2756
2757    /// music21's `setDirectionInDirectionType`.
2758    fn direction_type(
2759        &mut self,
2760        specific: &'x Xml,
2761        direction: &'x Xml,
2762        staff: i32,
2763        total: FloatType,
2764    ) -> Result<()> {
2765        match specific.tag.as_str() {
2766            "dynamics" => {
2767                for written in &specific.children {
2768                    let text = match (written.tag.as_str(), written.stripped()) {
2769                        ("other-dynamic", text) if !text.is_empty() => text,
2770                        (tag, _) => tag,
2771                    };
2772                    let mut dynamic = Dynamic::new(text);
2773                    dynamic.set_placement(placement_of(direction));
2774                    self.insert(total, staff, dynamic);
2775                }
2776            }
2777            "wedge" | "bracket" | "dashes" | "octave-shift" | "pedal" => {
2778                // music21 warns and carries on when one cannot be read.
2779                if let Ok(made) = self.direction_spanners(specific, staff, total) {
2780                    for index in made {
2781                        let building = &mut self.part.importer.spanners[index];
2782                        if let Some(placement) = placement_of(specific) {
2783                            building.placement = Some(placement);
2784                        }
2785                        if building.kind != SpannerKind::Crescendo
2786                            && building.kind != SpannerKind::Diminuendo
2787                            && let Some(line_type) = specific.get("line-type")
2788                        {
2789                            building.line_type = Some(line_type.to_string());
2790                        }
2791                    }
2792                }
2793            }
2794            "coda" | "segno" => {
2795                let kind = if specific.tag == "segno" {
2796                    RepeatExpressionKind::Segno
2797                } else {
2798                    RepeatExpressionKind::Coda
2799                };
2800                self.insert(total, staff, RepeatExpression::new(kind));
2801            }
2802            "rehearsal" => {
2803                // music21's `xmlToRehearsalMark`.
2804                let mut mark = RehearsalMark::new(specific.stripped());
2805                if let Some(enclosure) = specific.get("enclosure") {
2806                    let lower = enclosure.to_lowercase();
2807                    if !ENCLOSURES.contains(&lower.as_str()) {
2808                        return Err(import_error(format!(
2809                            "Not a supported enclosure: '{enclosure}'"
2810                        )));
2811                    }
2812                    mark.set_enclosure(Some(lower));
2813                }
2814                mark.set_placement(placement_of(direction));
2815                self.insert(total, staff, mark);
2816            }
2817            "metronome" => {
2818                let tempo = metronome(specific, placement_of(direction))?;
2819                self.insert(total, staff, tempo);
2820            }
2821            "words" => {
2822                let text = specific.stripped();
2823                let placement = placement_of(direction);
2824                // Words that say where to go next are a repeat mark.
2825                let repeat = RepeatExpressionKind::ALL
2826                    .into_iter()
2827                    .find(|kind| RepeatExpression::new(*kind).is_valid_text(text));
2828                match repeat {
2829                    Some(kind) => {
2830                        let mut mark = RepeatExpression::new(kind);
2831                        mark.set_text(text);
2832                        // The words are placed, not the mark: one drawn as
2833                        // its sign, a coda or a segno, stands where music21
2834                        // puts a sign whatever side its words were on.
2835                        if !mark.use_symbol() {
2836                            mark.set_placement(placement);
2837                        }
2838                        self.insert(total, staff, mark);
2839                    }
2840                    None => {
2841                        let mut words = TextExpression::new(text);
2842                        words.set_placement(placement);
2843                        self.insert(total, staff, words);
2844                    }
2845                }
2846            }
2847            _ => {}
2848        }
2849        Ok(())
2850    }
2851
2852    /// music21's `xmlDirectionTypeToSpanners`. Hands back the spanners it
2853    /// started.
2854    fn direction_spanners(
2855        &mut self,
2856        element: &'x Xml,
2857        staff: i32,
2858        total: FloatType,
2859    ) -> Result<Vec<usize>> {
2860        let last = self.last_note;
2861        let id = element.get("number");
2862        let mut made = Vec::new();
2863        let open = |spanners: &[Building], wanted: &dyn Fn(SpannerKind) -> bool| {
2864            spanners.iter().position(|held| {
2865                wanted(held.kind) && !held.complete && held.id_local.as_deref() == id
2866            })
2867        };
2868        match element.tag.as_str() {
2869            "wedge" => {
2870                let kind = match element.get("type") {
2871                    Some("crescendo") => Some(SpannerKind::Crescendo),
2872                    Some("diminuendo") => Some(SpannerKind::Diminuendo),
2873                    Some("stop") => None,
2874                    other => {
2875                        return Err(import_error(format!("Unknown type, {other:?}.")));
2876                    }
2877                };
2878                match kind {
2879                    Some(kind) => {
2880                        let index = self.one_spanner(element, None, kind, true);
2881                        self.part.importer.pending.push_back(index);
2882                        made.push(index);
2883                    }
2884                    None => {
2885                        let spanners = &mut self.part.importer.spanners;
2886                        let index = open(spanners, &SpannerKind::is_wedge)
2887                            .ok_or_else(|| import_error("Error in getting DynamicWedges"))?;
2888                        spanners[index].complete = true;
2889                        if let Some(last) = last {
2890                            spanners[index].add(last);
2891                        }
2892                    }
2893                }
2894            }
2895            "bracket" | "dashes" => {
2896                let dashes = element.tag == "dashes";
2897                let tick = || -> Result<LineEnd> {
2898                    match element.get("line-end") {
2899                        Some(name) => LineEnd::from_name(name),
2900                        None => Ok(LineEnd::Down),
2901                    }
2902                };
2903                let height = element.get("end-length").and_then(parse_float);
2904                let Importer {
2905                    spanners, pending, ..
2906                } = &mut *self.part.importer;
2907                match element.get("type") {
2908                    Some("start") => {
2909                        let mut building = Building::new(SpannerKind::Line, id);
2910                        if dashes {
2911                            building.ends.start = LineEnd::None;
2912                            building.line_type = Some("dashed".to_string());
2913                        } else {
2914                            building.ends.start_height = height;
2915                            building.ends.start = tick()?;
2916                            if let Some(line_type) = element.get("line-type") {
2917                                building.line_type = Some(line_type.to_string());
2918                            }
2919                        }
2920                        spanners.push(building);
2921                        pending.push_back(spanners.len() - 1);
2922                        made.push(spanners.len() - 1);
2923                    }
2924                    Some("stop") => {
2925                        let Some(index) = open(spanners, &|kind| kind == SpannerKind::Line) else {
2926                            return Ok(made);
2927                        };
2928                        let building = &mut spanners[index];
2929                        building.complete = true;
2930                        if dashes {
2931                            building.ends.end = LineEnd::None;
2932                            building.line_type = Some("dashed".to_string());
2933                        } else {
2934                            building.ends.end = tick()?;
2935                            if height.is_some() {
2936                                building.ends.end_height = height;
2937                            }
2938                            if let Some(line_type) = element.get("line-type") {
2939                                building.line_type = Some(line_type.to_string());
2940                            }
2941                        }
2942                        if let Some(last) = last {
2943                            building.add(last);
2944                        }
2945                    }
2946                    other => {
2947                        return Err(import_error(format!(
2948                            "unidentified mxType of mxBracket: {other:?}"
2949                        )));
2950                    }
2951                }
2952            }
2953            "octave-shift" => {
2954                let Importer {
2955                    spanners, pending, ..
2956                } = &mut *self.part.importer;
2957                match element.get("type") {
2958                    Some(direction @ ("up" | "down")) => {
2959                        let size = element.get("size").and_then(parse_int).unwrap_or(8);
2960                        // An `up` shift is a line under the notes.
2961                        let up = direction == "down";
2962                        let name = match (size, up) {
2963                            (15, true) => "15ma",
2964                            (15, false) => "15mb",
2965                            (22, true) => "22da",
2966                            (22, false) => "22db",
2967                            (_, true) => "8va",
2968                            (_, false) => "8vb",
2969                        };
2970                        let mut building = Building::new(SpannerKind::Ottava, id);
2971                        building.shift = Some(OctaveShift::from_name(name, false)?);
2972                        building.placement = Some(if up {
2973                            Placement::Above
2974                        } else {
2975                            Placement::Below
2976                        });
2977                        spanners.push(building);
2978                        pending.push_back(spanners.len() - 1);
2979                        made.push(spanners.len() - 1);
2980                    }
2981                    Some(kind @ ("continue" | "stop")) => {
2982                        let index = open(spanners, &|kind| kind == SpannerKind::Ottava)
2983                            .ok_or_else(|| import_error("Error in getting Ottava"))?;
2984                        if kind == "continue" {
2985                            pending.push_back(index);
2986                        } else {
2987                            spanners[index].complete = true;
2988                            if let Some(last) = last {
2989                                spanners[index].add(last);
2990                            }
2991                        }
2992                    }
2993                    other => {
2994                        return Err(import_error(format!(
2995                            "unidentified mxType of octave-shift: {other:?}"
2996                        )));
2997                    }
2998                }
2999            }
3000            "pedal" => {
3001                let Importer {
3002                    spanners, pending, ..
3003                } = &mut *self.part.importer;
3004                match element.get("type") {
3005                    Some(kind @ ("start" | "sostenuto")) => {
3006                        let mut building = Building::new(SpannerKind::PedalMark, id);
3007                        building.pedal.pedal_type = Some(if kind == "start" {
3008                            PedalType::Sustain
3009                        } else {
3010                            PedalType::Sostenuto
3011                        });
3012                        if element.get("line") == Some("yes") {
3013                            building.pedal.form = Some(PedalForm::Line);
3014                        } else if element.get("line") == Some("no")
3015                            || element.get("sign") == Some("yes")
3016                        {
3017                            building.pedal.form = Some(PedalForm::Symbol);
3018                        }
3019                        building.pedal.abbreviated = element.get("abbreviated") == Some("yes");
3020                        spanners.push(building);
3021                        pending.push_back(spanners.len() - 1);
3022                        self.pedal_starts.push((spanners.len() - 1, total));
3023                        made.push(spanners.len() - 1);
3024                    }
3025                    Some(kind @ ("continue" | "stop" | "discontinue" | "resume" | "change")) => {
3026                        let index = open(spanners, &|kind| kind == SpannerKind::PedalMark)
3027                            .ok_or_else(|| import_error("Error in getting PedalMark"))?;
3028                        match kind {
3029                            "stop" => {
3030                                spanners[index].complete = true;
3031                                if let Some(last) = last {
3032                                    spanners[index].add(last);
3033                                }
3034                            }
3035                            "resume" => {
3036                                let started = self
3037                                    .pedal_starts
3038                                    .iter()
3039                                    .find(|(held, _)| *held == index)
3040                                    .map(|(_, at)| *at);
3041                                if spanners[index].pedal.form == Some(PedalForm::Symbol)
3042                                    && started == Some(total)
3043                                {
3044                                    spanners[index].pedal.form = Some(PedalForm::SymbolLine);
3045                                } else {
3046                                    self.pedal_object(PedalObjectKind::GapEnd, index, staff, total);
3047                                }
3048                            }
3049                            "discontinue" => {
3050                                self.pedal_object(PedalObjectKind::GapStart, index, staff, total);
3051                            }
3052                            "change" => {
3053                                self.pedal_object(PedalObjectKind::Bounce, index, staff, total);
3054                            }
3055                            // music21 remembers nothing of a pedal that
3056                            // carries on.
3057                            _ => {}
3058                        }
3059                    }
3060                    other => {
3061                        return Err(import_error(format!(
3062                            "unidentified mxType of pedal: {other:?}"
3063                        )));
3064                    }
3065                }
3066            }
3067            _ => {}
3068        }
3069        Ok(made)
3070    }
3071
3072    /// A bounce or a gap in a held pedal, put where the direction stands and
3073    /// joined by the pedal mark it belongs to.
3074    fn pedal_object(&mut self, kind: PedalObjectKind, pedal: usize, staff: i32, total: FloatType) {
3075        let uid = self.insert(total, staff, PedalObject::new(kind));
3076        self.part.importer.spanners[pedal].add(uid);
3077    }
3078
3079    /// music21's `xmlHarmony` and `xmlToChordSymbol`.
3080    fn harmony(&mut self, harmony: &'x Xml) -> Result<()> {
3081        let mut kind = harmony.child_text("kind").to_string();
3082        let kind_element = harmony.find("kind");
3083        let mut kind_text = String::new();
3084        if !kind.is_empty() {
3085            for (alias, target) in crate::chordsymbol::CHORD_KIND_ALIASES {
3086                if alias == kind {
3087                    kind = target.to_string();
3088                }
3089            }
3090            let written = kind_element.and_then(|kind| kind.get("text")).unwrap_or("");
3091            if !(written.is_empty() && kind != "none") {
3092                kind_text = written.to_string();
3093            }
3094        }
3095        let pitch_of = |step: &Xml, alter: Option<&Xml>| -> Result<Option<Pitch>> {
3096            let name = match step.text().filter(|text| !text.is_empty()) {
3097                Some(text) => text,
3098                None => match step.get("text") {
3099                    Some(text) => text,
3100                    None => return Ok(None),
3101                },
3102            };
3103            let mut pitch = Pitch::from_name(name.trim())?;
3104            if let Some(alter) = alter.and_then(|alter| parse_float(alter.stripped())) {
3105                pitch.set_written_accidental(Some(Accidental::new(alter)?));
3106            }
3107            Ok(Some(pitch))
3108        };
3109        let bass = match harmony.find("bass") {
3110            Some(bass) => {
3111                let step = bass
3112                    .find("bass-step")
3113                    .ok_or_else(|| import_error("bass-step missing"))?;
3114                pitch_of(step, bass.find("bass-alter"))?
3115            }
3116            None => None,
3117        };
3118        let root = match harmony.find("root") {
3119            Some(root) => match root.find("root-step") {
3120                Some(step) => pitch_of(step, root.find("root-alter"))?,
3121                None => None,
3122            },
3123            None => None,
3124        };
3125
3126        let mut symbol = if kind == "none" {
3127            ChordSymbol::no_chord(Some(kind_text.clone()).filter(|text| !text.is_empty()))
3128        } else {
3129            let root = root.ok_or_else(|| {
3130                import_error("reading a chord symbol given by function is not supported yet")
3131            })?;
3132            let mut symbol = match ChordSymbol::from_kind(root.clone(), &kind, bass.clone()) {
3133                Ok(symbol) => symbol,
3134                // A kind music21 has no notes for is still a symbol of that
3135                // kind on its root.
3136                Err(_) => {
3137                    let mut symbol = ChordSymbol::parse_music21(root.name())?;
3138                    symbol.set_root(root);
3139                    symbol.set_bass(bass);
3140                    symbol.with_kind(&kind)
3141                }
3142            };
3143            symbol.set_kind_text(Some(kind_text));
3144            symbol
3145        };
3146        if !symbol.is_no_chord() {
3147            for degree in harmony.find_all("degree") {
3148                let value = parse_int(degree.child_text("degree-value"))
3149                    .ok_or_else(|| import_error("degree-value missing"))?;
3150                let alter = parse_int(degree.child_text("degree-alter")).unwrap_or(0);
3151                let modification = ChordStepModification::new(
3152                    crate::chordsymbol::ChordStepModificationType::from_music21_name(
3153                        degree.child_text("degree-type"),
3154                    )?,
3155                    value.clamp(0, 255) as u8,
3156                    alter,
3157                )?;
3158                symbol.add_chord_step_modification(modification);
3159            }
3160        }
3161        symbol.set_placement(placement_of(harmony));
3162        let offset = self.offset + self.offset_of(harmony);
3163        self.insert(offset, staff_number(harmony), symbol);
3164        Ok(())
3165    }
3166
3167    /// music21's `xmlBarline`.
3168    fn barline(&mut self, element: &'x Xml) -> Result<()> {
3169        let style = element.child_text("bar-style");
3170        let right = matches!(element.get("location"), Some("right") | None);
3171        let barline = match element.find("repeat") {
3172            Some(repeat) => {
3173                let written = match repeat.get("direction").map(str::to_lowercase).as_deref() {
3174                    Some("forward") => RepeatDirection::Start,
3175                    Some("backward") => RepeatDirection::End,
3176                    Some(other) => {
3177                        return Err(import_error(format!(
3178                            "cannot handle mx direction format: {other}"
3179                        )));
3180                    }
3181                    None => return Err(import_error("Repeat sign direction is required")),
3182                };
3183                // A repeat closing a measure ends the passage, whatever the
3184                // file says; only an end says how many times.
3185                let direction = if right { RepeatDirection::End } else { written };
3186                let times = repeat
3187                    .get("times")
3188                    .and_then(parse_int)
3189                    .map(|times| times.max(0) as u32)
3190                    .filter(|_| written == RepeatDirection::End);
3191                // The sign is drawn as its direction draws it, not as the
3192                // file's bar style says.
3193                Barline::repeat(direction, times)
3194            }
3195            None => {
3196                let mut barline = Barline::default();
3197                if !style.is_empty() {
3198                    barline.set_bar_type(BarlineType::from_name(style)?);
3199                }
3200                barline
3201            }
3202        };
3203
3204        if let Some(ending) = element.find("ending") {
3205            let index = self.part.measures.len();
3206            let open = self
3207                .part
3208                .brackets
3209                .iter()
3210                .position(|bracket| !bracket.complete);
3211            let at = match open {
3212                Some(at) => {
3213                    if !self.part.brackets[at].measures.contains(&index) {
3214                        self.part.brackets[at].measures.push(index);
3215                    }
3216                    at
3217                }
3218                None => {
3219                    self.part.brackets.push(Bracket {
3220                        numbers: vec![0],
3221                        measures: vec![index],
3222                        complete: false,
3223                    });
3224                    self.part.brackets.len() - 1
3225                }
3226            };
3227            let bracket = &mut self.part.brackets[at];
3228            if ending.get("type") == Some("start") {
3229                bracket.numbers = bracket_numbers(ending.get("number").unwrap_or("1"));
3230                if let Some(text) = ending.text() {
3231                    let digits = text.strip_suffix('.').unwrap_or(text);
3232                    if !digits.is_empty()
3233                        && digits.chars().all(|c| c.is_ascii_digit())
3234                        && let Ok(number) = digits.parse::<u32>()
3235                    {
3236                        bracket.numbers = vec![number];
3237                    }
3238                }
3239            }
3240            if matches!(ending.get("type"), Some("stop" | "discontinue")) {
3241                bracket.complete = true;
3242            }
3243        }
3244
3245        // A barline may carry a segno or a coda, which MusicXML allows
3246        // inside `<barline>` as well as inside a `<direction>`. music21
3247        // puts each in the measure where the barline stands, at the offset
3248        // the measure has been read to, exactly as it puts one read from a
3249        // direction -- so a document saying it either way reads alike.
3250        for (tag, kind) in [
3251            ("segno", RepeatExpressionKind::Segno),
3252            ("coda", RepeatExpressionKind::Coda),
3253        ] {
3254            if element.find(tag).is_some() {
3255                self.insert(self.offset, NO_STAFF, RepeatExpression::new(kind));
3256            }
3257        }
3258
3259        match element.get("location") {
3260            Some("left") => self.measure.left = Some(barline),
3261            Some("right") | None => self.measure.right = Some(barline),
3262            // A barline in the middle of a measure is an element of its own,
3263            // which music21 appends: after the last thing read so far ends,
3264            // whatever the file's place for it, and at the very start of a
3265            // measure whose notes are all in voices.
3266            Some(_) => {
3267                let end = self.measure.highest_time_outside_voices();
3268                self.insert(end, NO_STAFF, barline);
3269            }
3270        }
3271        Ok(())
3272    }
3273}
3274
3275/// General MIDI's drum for a key as the instrument music21 has for it:
3276/// its `PercussionMapper.midiPitchToInstrument`, and a bare unpitched
3277/// instrument carrying the key where the map names nothing -- which is
3278/// what music21's reader makes of the exception its mapper raises. The map
3279/// is the crate's one table, read through
3280/// `instrument::for_percussion_key_or_unmapped`.
3281fn percussion_instrument(key: u8) -> Instrument {
3282    crate::instrument::for_percussion_key_or_unmapped(key)
3283}
3284
3285/// What is read of a note before it is known whether it has a pitch.
3286enum ReadNote {
3287    Note(Note),
3288    Unpitched(Unpitched),
3289    Rest(Rest),
3290}
3291
3292impl ReadNote {
3293    fn written(&self) -> &Note {
3294        match self {
3295            Self::Note(note) => note,
3296            Self::Unpitched(stroke) => stroke.written(),
3297            Self::Rest(_) => unreachable!("a rest is not written as a note"),
3298        }
3299    }
3300
3301    fn written_mut(&mut self) -> &mut Note {
3302        match self {
3303            Self::Note(note) => note,
3304            Self::Unpitched(stroke) => stroke.written_mut(),
3305            Self::Rest(_) => unreachable!("a rest is not written as a note"),
3306        }
3307    }
3308}
3309
3310/// What a `<note>` says that a note, a stroke and a rest all take.
3311#[derive(Default)]
3312struct General {
3313    color: Option<String>,
3314    hidden: bool,
3315    volume: Option<Volume>,
3316    size: Option<NoteSize>,
3317    tie: Option<Tie>,
3318    duration: Option<Duration>,
3319    articulations: Vec<Articulation>,
3320    expressions: Vec<Expression>,
3321    arpeggio_spanners: Vec<(String, ArpeggioType)>,
3322}
3323
3324fn reduce(numerator: i64, denominator: i64) -> (i64, i64) {
3325    fn gcd(a: i64, b: i64) -> i64 {
3326        if b == 0 { a.abs() } else { gcd(b, a % b) }
3327    }
3328    let divisor = gcd(numerator, denominator).max(1);
3329    (numerator / divisor, denominator / divisor)
3330}
3331
3332/// music21's `_setAttributeFromTagText` with `_floatOrIntStr`: the length
3333/// a child gives, where it gives one.
3334fn tenths(element: &Xml, tag: &str) -> Option<Tenths> {
3335    element
3336        .find(tag)
3337        .and_then(Xml::text)
3338        .filter(|text| !text.is_empty())
3339        .and_then(Tenths::parse)
3340}
3341
3342/// music21's `xmlPageLayoutToPageLayout`.
3343fn read_page_layout(written: &Xml, read: &mut PageLayout) {
3344    if let Some(height) = tenths(written, "page-height") {
3345        read.page_height = Some(height);
3346    }
3347    if let Some(width) = tenths(written, "page-width") {
3348        read.page_width = Some(width);
3349    }
3350    if let Some(margins) = written.find("page-margins") {
3351        for (tag, place) in [
3352            ("top-margin", &mut read.top_margin),
3353            ("bottom-margin", &mut read.bottom_margin),
3354            ("left-margin", &mut read.left_margin),
3355            ("right-margin", &mut read.right_margin),
3356        ] {
3357            if let Some(margin) = tenths(margins, tag) {
3358                *place = Some(margin);
3359            }
3360        }
3361    }
3362}
3363
3364/// music21's `xmlSystemLayoutToSystemLayout`.
3365fn read_system_layout(written: &Xml, read: &mut SystemLayout) {
3366    if let Some(margins) = written.find("system-margins") {
3367        for (tag, place) in [
3368            ("top-margin", &mut read.top_margin),
3369            ("bottom-margin", &mut read.bottom_margin),
3370            ("left-margin", &mut read.left_margin),
3371            ("right-margin", &mut read.right_margin),
3372        ] {
3373            if let Some(margin) = tenths(margins, tag) {
3374                *place = Some(margin);
3375            }
3376        }
3377    }
3378    if let Some(distance) = tenths(written, "system-distance") {
3379        read.distance = Some(distance);
3380    }
3381    if let Some(distance) = tenths(written, "top-system-distance") {
3382        read.top_distance = Some(distance);
3383    }
3384}
3385
3386/// music21's `xmlStaffLayoutToStaffLayout`.
3387fn staff_layout(written: &Xml) -> StaffLayout {
3388    StaffLayout {
3389        distance: tenths(written, "staff-distance"),
3390        staff_number: written
3391            .get("number")
3392            .and_then(|value| value.trim().parse().ok()),
3393        ..StaffLayout::default()
3394    }
3395}
3396
3397/// music21's `xmlStaffLayoutFromStaffDetails`.
3398fn read_staff_details(details: &Xml, read: &mut StaffLayout) {
3399    if let Some(number) = details
3400        .get("number")
3401        .and_then(|value| value.trim().parse().ok())
3402    {
3403        read.staff_number = Some(number);
3404    }
3405    match details.get("print-object") {
3406        Some("no") => read.hidden = Some(true),
3407        Some("yes") => read.hidden = Some(false),
3408        _ => {}
3409    }
3410    if let Some(lines) = details
3411        .find("staff-lines")
3412        .and_then(Xml::text)
3413        .and_then(|text| text.trim().parse().ok())
3414    {
3415        read.staff_lines = Some(lines);
3416    }
3417    if let Some(kind) = details
3418        .find("staff-type")
3419        .and_then(Xml::text)
3420        .and_then(|text| StaffType::from_name(text.trim()))
3421    {
3422        read.staff_type = kind;
3423    }
3424    if let Some(size) = tenths(details, "staff-size") {
3425        read.staff_size = Some(size.value());
3426    }
3427}
3428
3429/// music21's `setPosition` and `setColor`: where a mark stands and its
3430/// colour, read into a style.
3431fn read_position(written: &Xml, style: &mut TextStyle) {
3432    let value = |name: &str| written.get(name).map(StyleValue::read);
3433    if let Some(read) = value("default-x") {
3434        style.absolute_x = Some(read);
3435    }
3436    if let Some(read) = value("default-y") {
3437        style.absolute_y = Some(read);
3438    }
3439    if let Some(read) = value("relative-x") {
3440        style.relative_x = Some(read);
3441    }
3442    if let Some(read) = value("relative-y") {
3443        style.relative_y = Some(read);
3444    }
3445    if let Some(color) = written.get("color") {
3446        style.color = Some(color.to_string());
3447    }
3448}
3449
3450/// music21's `setPrintStyleAlign`: where text stands, its font and colour,
3451/// and how it is aligned, read into a style.
3452pub(crate) fn read_print_style_align(written: &Xml, style: &mut TextStyle) -> Result<()> {
3453    let value = |name: &str| written.get(name).map(StyleValue::read);
3454    if let Some(read) = value("default-x") {
3455        style.absolute_x = Some(read);
3456    }
3457    if let Some(read) = value("default-y") {
3458        style.absolute_y = Some(read);
3459    }
3460    if let Some(read) = value("relative-x") {
3461        style.relative_x = Some(read);
3462    }
3463    if let Some(read) = value("relative-y") {
3464        style.relative_y = Some(read);
3465    }
3466    if let Some(family) = written.get("font-family") {
3467        style.font_family = Some(
3468            family
3469                .split(',')
3470                .map(|name| name.trim().to_string())
3471                .collect(),
3472        );
3473    }
3474    if let Some(font_style) = written.get("font-style") {
3475        style.set_font_style(Some(font_style))?;
3476    }
3477    if let Some(size) = value("font-size") {
3478        style.font_size = Some(size);
3479    }
3480    if let Some(weight) = written.get("font-weight") {
3481        style.set_font_weight(Some(weight))?;
3482    }
3483    if let Some(color) = written.get("color") {
3484        style.color = Some(color.to_string());
3485    }
3486    if let Some(align) = written.get("valign") {
3487        style.set_align_vertical(Some(align))?;
3488    }
3489    if let Some(align) = written.get("halign") {
3490        style.set_align_horizontal(Some(align))?;
3491    }
3492    Ok(())
3493}
3494
3495/// music21's `xmlCreditToTextBox`: a text box of the credit's words, one
3496/// line each, drawn as its first words say, on the page it names or the
3497/// first. A fresh text box's place, 500 tenths each way, stands where the
3498/// words say none.
3499fn text_box(credit: &Xml) -> Result<TextBox> {
3500    let mut read = TextBox::new("");
3501    read.style.set_align_horizontal(None)?;
3502    read.style.set_align_vertical(None)?;
3503    if let Some(page) = credit.get("page") {
3504        read.page = page.trim().parse().map_err(|_| {
3505            import_error(format!("invalid literal for int() with base 10: {page:?}"))
3506        })?;
3507    }
3508    let words: Vec<String> = credit
3509        .find_all("credit-words")
3510        .filter_map(|words| {
3511            words
3512                .text()
3513                .filter(|text| !text.is_empty())
3514                .map(str::to_string)
3515        })
3516        .collect();
3517    if words.is_empty() {
3518        return Ok(read);
3519    }
3520    read.content = words.join("\n");
3521    if let Some(first) = credit.find("credit-words") {
3522        read_print_style_align(first, &mut read.style)?;
3523        read.style.set_justify(first.get("justify"))?;
3524    }
3525    Ok(read)
3526}
3527
3528/// music21's `xmlDefaultsToScoreLayout`.
3529fn score_layout(defaults: &Xml) -> ScoreLayout {
3530    let mut read = ScoreLayout::default();
3531    if let Some(scaling) = defaults.find("scaling") {
3532        read.scaling_millimeters = tenths(scaling, "millimeters");
3533        read.scaling_tenths = tenths(scaling, "tenths");
3534    }
3535    if let Some(written) = defaults.find("page-layout") {
3536        let mut page = PageLayout::default();
3537        read_page_layout(written, &mut page);
3538        read.page_layout = Some(page);
3539    }
3540    if let Some(written) = defaults.find("system-layout") {
3541        let mut system = SystemLayout::default();
3542        read_system_layout(written, &mut system);
3543        read.system_layout = Some(system);
3544    }
3545    read.staff_layouts = defaults
3546        .find_all("staff-layout")
3547        .map(staff_layout)
3548        .collect();
3549    read
3550}
3551
3552/// music21's `getStaffNumber`.
3553fn staff_number(element: &Xml) -> i32 {
3554    match element.tag.as_str() {
3555        "harmony" | "forward" | "note" | "direction" => {
3556            parse_int(element.child_text("staff")).unwrap_or(NO_STAFF)
3557        }
3558        "clef" | "staff-details" | "staff-layout" => {
3559            element.get("number").and_then(parse_int).unwrap_or(1)
3560        }
3561        "measure-style" | "key" | "time" | "transpose" => element
3562            .get("number")
3563            .and_then(parse_int)
3564            .unwrap_or(NO_STAFF),
3565        _ => NO_STAFF,
3566    }
3567}
3568
3569/// music21's `xmlToPitch`.
3570fn pitch_of(note: &Xml) -> Result<Pitch> {
3571    let Some(written) = note.find("pitch") else {
3572        return Pitch::from_name("C4");
3573    };
3574    let step = written.child_text("step");
3575    let octave = written.child_text("octave");
3576    let mut pitch = Pitch::from_name(format!("{step}{octave}"))?;
3577    let alter = parse_float(written.child_text("alter"));
3578    let accidental = note
3579        .find("accidental")
3580        .filter(|written| !written.stripped().is_empty());
3581    match accidental {
3582        Some(written) => {
3583            if let Ok(mut read) = accidental_of(written) {
3584                read.set_display_status(Some(true));
3585                if let Some(alter) = alter
3586                    && alter != read.alter()
3587                {
3588                    read.set_alter_independently(alter);
3589                }
3590                pitch.set_written_accidental(Some(read));
3591            }
3592        }
3593        None => {
3594            if let Some(alter) = alter {
3595                let mut read = Accidental::new(alter)
3596                    .map_err(|_| import_error(format!("incorrect accidental {alter} for pitch")))?;
3597                read.set_display_status(Some(false));
3598                pitch.set_written_accidental(Some(read));
3599            }
3600        }
3601    }
3602    Ok(pitch)
3603}
3604
3605/// music21's `xmlToAccidental`.
3606fn accidental_of(written: &Xml) -> Result<Accidental> {
3607    let name = written.stripped().to_lowercase();
3608    let name = match name.as_str() {
3609        "quarter-sharp" => "half-sharp",
3610        "three-quarters-sharp" => "one-and-a-half-sharp",
3611        "quarter-flat" => "half-flat",
3612        "three-quarters-flat" => "one-and-a-half-flat",
3613        "flat-flat" => "double-flat",
3614        "sharp-sharp" => "double-sharp",
3615        other => other,
3616    };
3617    let mut accidental = Accidental::natural();
3618    accidental.set_allowing_non_standard_value(name)?;
3619    if let Some(color) = written.get("color") {
3620        accidental.set_color(Some(color.to_string()));
3621    }
3622    match (yes(written.get("parentheses")), yes(written.get("bracket"))) {
3623        (true, true) => accidental.set_display_style("both")?,
3624        (true, false) => accidental.set_display_style("parentheses")?,
3625        (false, true) => accidental.set_display_style("bracket")?,
3626        (false, false) => {}
3627    }
3628    Ok(accidental)
3629}
3630
3631/// music21's `xmlToBeams`.
3632fn beams_of(written: &[&Xml]) -> Result<Beams> {
3633    let mut beams = Beams::new();
3634    for (index, beam) in written.iter().enumerate() {
3635        let text = beam.text().map_or("begin", str::trim);
3636        let (beam_type, direction) = match text {
3637            "begin" => (BeamType::Start, None),
3638            "continue" => (BeamType::Continue, None),
3639            "end" => (BeamType::Stop, None),
3640            "forward hook" => (BeamType::PartialBeam, Some(BeamDirection::Right)),
3641            "backward hook" => (BeamType::PartialBeam, Some(BeamDirection::Left)),
3642            other => {
3643                return Err(import_error(format!(
3644                    "unexpected beam type encountered ({other})"
3645                )));
3646            }
3647        };
3648        let mut read = Beam::new(beam_type, direction);
3649        read.set_number(Some(index as u32 + 1));
3650        beams.beams_mut().push(read);
3651    }
3652    Ok(beams)
3653}
3654
3655/// music21's `xmlToTie`.
3656fn tie_of(note: &Xml) -> Option<Tie> {
3657    let types: Vec<&str> = note
3658        .find_all("tie")
3659        .filter_map(|tie| tie.get("type"))
3660        .collect();
3661    let tie_type = if types.len() == 1 {
3662        TieType::from_name(types[0]).ok()?
3663    } else if types.contains(&"stop") && types.contains(&"start") {
3664        TieType::Continue
3665    } else {
3666        TieType::Start
3667    };
3668    let mut tie = Tie::new(tie_type);
3669    if let Some(first) = note
3670        .find("notations")
3671        .and_then(|notations| notations.find("tied"))
3672    {
3673        if let Some(style) = first.get("line-type").filter(|style| *style != "wavy")
3674            && let Ok(style) = TieStyle::from_name(style)
3675        {
3676            tie.set_style(style);
3677        }
3678        match first.get("placement") {
3679            Some(placement) => tie.set_placement(Placement::from_name(placement).ok()),
3680            None => match first.get("orientation") {
3681                Some("over") => tie.set_placement(Some(Placement::Above)),
3682                Some("under") => tie.set_placement(Some(Placement::Below)),
3683                _ => {}
3684            },
3685        }
3686    }
3687    Some(tie)
3688}
3689
3690/// music21's `updateLyricsFromList` and `xmlToLyric`.
3691fn lyrics_from<'x>(written: impl Iterator<Item = &'x Xml>) -> Result<Vec<Lyric>> {
3692    let mut out = Vec::new();
3693    let mut current = 1;
3694    for lyric in written {
3695        let texts: Vec<&Xml> = lyric.find_all("text").collect();
3696        let syllabics: Vec<&Xml> = lyric.find_all("syllabic").collect();
3697        let elisions: Vec<&Xml> = lyric.find_all("elision").collect();
3698        let mut read = Lyric::unsung();
3699        if texts.is_empty() {
3700            // A lyric that only extends one: music21 hands back a bare one.
3701            out.push(read);
3702            current += 1;
3703            continue;
3704        }
3705        if texts.len() == 1 {
3706            if let Some(text) = texts[0].text() {
3707                read.set_text(text.trim());
3708            }
3709            if let Some(syllabic) = syllabics.first()
3710                && let Ok(syllabic) = Syllabic::from_name(syllabic.stripped())
3711            {
3712                read.set_syllabic(syllabic);
3713            }
3714        } else if texts.len() > 1 {
3715            let mut components = Vec::new();
3716            for (index, text) in texts.iter().enumerate() {
3717                let mut component = Lyric::unsung();
3718                if let Some(text) = text.text() {
3719                    component.set_text(text.trim());
3720                }
3721                if let Some(syllabic) = syllabics.get(index) {
3722                    if let Ok(syllabic) = Syllabic::from_name(syllabic.stripped()) {
3723                        component.set_syllabic(syllabic);
3724                    }
3725                    if index >= 1
3726                        && let Some(elision) = elisions.get(index - 1)
3727                    {
3728                        component.set_elision_before(elision.text().unwrap_or(""));
3729                    }
3730                }
3731                components.push(component);
3732            }
3733            read.set_components(components);
3734        }
3735        match lyric.get("number") {
3736            Some(number) => match number.trim().parse::<IntegerType>() {
3737                Ok(number) => read.set_number(number),
3738                Err(_) => {
3739                    read.set_number(0);
3740                    read.set_identifier(Some(number.to_string()));
3741                }
3742            },
3743            None => read.set_number(0),
3744        }
3745        if let Some(name) = lyric.get("name") {
3746            read.set_identifier(Some(name.to_string()));
3747        }
3748        if let Some(justify) = lyric.get("justify") {
3749            read.set_justify(Some(Justification::from_name(justify)?));
3750        }
3751        if let Some(placement) = lyric.get("placement") {
3752            read.set_placement(Placement::from_name(placement).ok());
3753        }
3754        if lyric.get("print-object") == Some("no") {
3755            read.set_hidden(true);
3756        }
3757        if let Some(color) = lyric.get("color") {
3758            read.set_color(Some(color.to_string()));
3759        }
3760        if read.number() == 0 {
3761            read.set_number(current);
3762        }
3763        out.push(read);
3764        current += 1;
3765    }
3766    Ok(out)
3767}
3768
3769/// music21's `xmlToArticulation`.
3770fn articulation_of(mark: &Xml) -> Option<Articulation> {
3771    let class = match mark.tag.as_str() {
3772        "accent" => "Accent",
3773        "strong-accent" => "StrongAccent",
3774        "staccato" => "Staccato",
3775        "staccatissimo" => "Staccatissimo",
3776        "spiccato" => "Spiccato",
3777        "tenuto" => "Tenuto",
3778        "detached-legato" => "DetachedLegato",
3779        "scoop" => "Scoop",
3780        "plop" => "Plop",
3781        "doit" => "Doit",
3782        "falloff" => "Falloff",
3783        "breath-mark" => "BreathMark",
3784        "caesura" => "Caesura",
3785        "stress" => "Stress",
3786        "unstress" => "Unstress",
3787        "other-articulation" => "Articulation",
3788        _ => return None,
3789    };
3790    let mut articulation = Articulation::of_kind(ArticulationKind::from_class_name(class)?);
3791    if let Some(placement) = mark.get("placement") {
3792        articulation.set_placement(Some(placement.to_string()));
3793    }
3794    let text = mark.stripped();
3795    match mark.tag.as_str() {
3796        "strong-accent" => {
3797            if let Some(direction) = mark.get("type") {
3798                articulation.set_point_direction(Some(direction.to_string()));
3799            }
3800        }
3801        "breath-mark" if !text.is_empty() => articulation.set_symbol(Some(text.to_string())),
3802        "other-articulation" if !text.is_empty() => {
3803            articulation.set_display_text(Some(text.to_string()));
3804        }
3805        _ => {}
3806    }
3807    Some(articulation)
3808}
3809
3810/// music21's `xmlTechnicalToArticulation`.
3811fn technical_of(mark: &Xml) -> Option<Articulation> {
3812    let class = match mark.tag.as_str() {
3813        "up-bow" => "UpBow",
3814        "down-bow" => "DownBow",
3815        "harmonic" => "StringHarmonic",
3816        "open-string" => "OpenString",
3817        "thumb-position" => "StringThumbPosition",
3818        "fingering" => "Fingering",
3819        "pluck" => "FrettedPluck",
3820        "double-tongue" => "DoubleTongue",
3821        "triple-tongue" => "TripleTongue",
3822        "stopped" => "Stopped",
3823        "snap-pizzicato" => "SnapPizzicato",
3824        "string" => "StringIndication",
3825        "bend" => "FretBend",
3826        "tap" => "FretTap",
3827        "fret" => "FretIndication",
3828        "heel" => "OrganHeel",
3829        "toe" => "OrganToe",
3830        "fingernails" => "HarpFingerNails",
3831        "handbell" => "HandbellIndication",
3832        "other-technical" => "TechnicalIndication",
3833        _ => return None,
3834    };
3835    let mut technical = Articulation::of_kind(ArticulationKind::from_class_name(class)?);
3836    let text = mark.stripped();
3837    match mark.tag.as_str() {
3838        "fingering" => {
3839            technical.set_finger(Some(Finger::from_written(mark.text().unwrap_or(""))));
3840            if let Some(substitution) = mark.get("substitution") {
3841                technical.set_substitution(substitution == "yes");
3842            }
3843            if let Some(alternate) = mark.get("alternate") {
3844                technical.set_alternate(alternate == "yes");
3845            }
3846        }
3847        "handbell" | "other-technical" if !text.is_empty() => {
3848            technical.set_display_text(Some(text.to_string()));
3849        }
3850        "fret" | "string" => {
3851            if let Some(number) = parse_int(text) {
3852                technical.set_number(number);
3853            }
3854        }
3855        "harmonic" => {
3856            if mark.find("artificial").is_some() {
3857                technical.set_harmonic_type(Some("artificial".to_string()));
3858            } else if mark.find("natural").is_some() {
3859                technical.set_harmonic_type(Some("natural".to_string()));
3860            }
3861        }
3862        "heel" | "toe" => {
3863            if let Some(substitution) = mark.get("substitution") {
3864                technical.set_substitution(substitution == "yes");
3865            }
3866        }
3867        _ => {}
3868    }
3869    if let Some(placement) = mark.get("placement") {
3870        technical.set_placement(Some(placement.to_string()));
3871    }
3872    Some(technical)
3873}
3874
3875/// music21's `xmlToTempoIndication`, for a mark with one note value.
3876/// music21's `xmlToTempoIndication`: a `<metronome>` is a tempo mark, or
3877/// with two note values a metric modulation, placed as its direction says.
3878fn metronome(written: &Xml, placement: Option<Placement>) -> Result<StreamElement> {
3879    let mut referents: Vec<Duration> = Vec::new();
3880    let mut numbers: Vec<FloatType> = Vec::new();
3881    for child in &written.children {
3882        match child.tag.as_str() {
3883            "beat-unit" => {
3884                referents.push(Duration::from_type(duration_type(child.stripped())?));
3885            }
3886            "beat-unit-dot" => {
3887                let last = referents
3888                    .last_mut()
3889                    .ok_or_else(|| import_error("encountered metronome components out of order"))?;
3890                let (kind, dots) = last
3891                    .type_and_dots()
3892                    .ok_or_else(|| import_error("a beat unit with no written value"))?;
3893                *last = Duration::from_type_with_dots(kind, dots + 1);
3894            }
3895            "per-minute" => {
3896                if let Some(number) = parse_float(child.stripped()) {
3897                    numbers.push(number);
3898                }
3899            }
3900            _ => {}
3901        }
3902    }
3903    let parentheses = written.get("parentheses") == Some("yes");
3904    if referents.len() > 1 {
3905        // All a file gives are the two note values: neither side has a
3906        // number until the mark in force before it is known.
3907        let mut sides = referents.into_iter();
3908        let mut modulation = MetricModulation::new();
3909        if let Some(old) = sides.next() {
3910            modulation.set_old_referent(old, None);
3911        }
3912        if let Some(new) = sides.next() {
3913            modulation.set_new_referent(new);
3914        }
3915        modulation.set_parentheses(parentheses);
3916        modulation.set_placement(placement);
3917        return Ok(modulation.into());
3918    }
3919    let mut mark = MetronomeMark::default();
3920    if let Some(number) = numbers.first() {
3921        mark.set_number(Some(*number));
3922    }
3923    if let Some(referent) = referents.into_iter().next() {
3924        mark.set_referent(referent);
3925    }
3926    if parentheses {
3927        mark.set_parentheses(true);
3928    }
3929    mark.set_placement(placement);
3930    Ok(mark.into())
3931}
3932
3933/// music21's `xmlToTimeSignature`.
3934fn time_signature(written: &Xml) -> Result<Option<TimeSignature>> {
3935    if written.find("senza-misura").is_some() {
3936        return Err(import_error(
3937            "reading a senza misura time signature is not supported yet",
3938        ));
3939    }
3940    let mut numerators = Vec::new();
3941    let mut denominators = Vec::new();
3942    for child in &written.children {
3943        match child.tag.as_str() {
3944            "beats" => numerators.push(child.stripped()),
3945            "beat-type" => denominators.push(child.stripped()),
3946            "interchangeable" => break,
3947            _ => {}
3948        }
3949    }
3950    let ratio = numerators
3951        .iter()
3952        .zip(&denominators)
3953        .map(|(numerator, denominator)| format!("{numerator}/{denominator}"))
3954        .collect::<Vec<_>>()
3955        .join("+");
3956    let mut meter = TimeSignature::from_ratio_string(&ratio)
3957        .map_err(|_| import_error(format!("Cannot process time signature {ratio}")))?;
3958    if let Some(color) = written.get("color") {
3959        meter.set_color(Some(color.to_string()));
3960    }
3961    meter.set_hidden(written.get("print-object") == Some("no"));
3962    match written.get("symbol") {
3963        Some(symbol @ ("common" | "cut" | "single-number" | "normal")) => {
3964            meter.set_symbol(Some(symbol.to_string()));
3965        }
3966        Some("note") => meter.set_symbolize_denominator(true),
3967        _ => {}
3968    }
3969    Ok(Some(meter))
3970}
3971
3972/// music21's `xmlToClef`.
3973fn clef_of(written: &Xml) -> Result<Clef> {
3974    let sign = written.child_text("sign");
3975    let mut clef = if matches!(
3976        sign.to_lowercase().as_str(),
3977        "tab" | "percussion" | "none" | "jianpu"
3978    ) {
3979        Clef::from_string(sign, 0)?
3980    } else {
3981        let line = match written.find("line") {
3982            Some(line) => line.stripped(),
3983            None if sign == "G" => "2",
3984            None => "4",
3985        };
3986        let octave_change = parse_int(written.child_text("clef-octave-change")).unwrap_or(0);
3987        Clef::from_string(&format!("{sign}{line}"), octave_change)?
3988    };
3989    if let Some(color) = written.get("color") {
3990        clef.set_color(Some(color.to_string()));
3991    }
3992    Ok(clef)
3993}
3994
3995/// music21's `xmlToKeySignature`: a key where the mode is said and is one
3996/// music21 knows, a signature otherwise.
3997fn key_signature(written: &Xml) -> Result<StreamElement> {
3998    let color = written.get("color").map(str::to_string);
3999    if written.find("fifths").is_none() {
4000        let mut steps = Vec::new();
4001        let mut alters = Vec::new();
4002        for child in &written.children {
4003            match child.tag.as_str() {
4004                "key-step" if !child.stripped().is_empty() => steps.push(child.stripped()),
4005                "key-alter" => {
4006                    if let Some(alter) = parse_float(child.stripped()) {
4007                        alters.push(alter);
4008                    }
4009                }
4010                _ => {}
4011            }
4012        }
4013        if steps.len() != alters.len() {
4014            return Err(import_error(
4015                "For non traditional signatures each step must have an alter",
4016            ));
4017        }
4018        let mut pitches = Vec::new();
4019        for (step, alter) in steps.into_iter().zip(alters) {
4020            let mut pitch = Pitch::from_name(step)?;
4021            pitch.set_written_accidental(Some(Accidental::new(alter)?));
4022            pitches.push(pitch);
4023        }
4024        let mut signature = KeySignature::from_altered_pitches(pitches);
4025        signature.set_color(color);
4026        return Ok(signature.into());
4027    }
4028    let sharps = parse_int(written.child_text("fifths")).unwrap_or(0);
4029    let mut signature = KeySignature::new(sharps);
4030    let mode = written.find("mode").and_then(Xml::text).unwrap_or("");
4031    if !mode.is_empty()
4032        && let Ok(mut key) = signature.try_as_key(Some(mode), None)
4033    {
4034        key.set_color(color);
4035        return Ok(key.into());
4036    }
4037    signature.set_color(color);
4038    Ok(signature.into())
4039}
4040
4041/// music21's `xmlTransposeToInterval`.
4042fn transpose_interval(written: &Xml) -> Result<Interval> {
4043    let mut diatonic = written
4044        .find("diatonic")
4045        .and_then(|d| parse_int(d.stripped()));
4046    let chromatic = written
4047        .find("chromatic")
4048        .and_then(|c| parse_int(c.stripped()));
4049    let too_far = || import_error("a <transpose> too far for an interval");
4050    let mut octave_change = 0;
4051    if let Some(octaves) = written
4052        .find("octave-change")
4053        .and_then(|o| parse_int(o.stripped()))
4054    {
4055        octave_change = octaves.checked_mul(12).ok_or_else(too_far)?;
4056        if let Some(diatonic) = &mut diatonic {
4057            *diatonic = octaves
4058                .checked_mul(7)
4059                .and_then(|steps| diatonic.checked_add(steps))
4060                .ok_or_else(too_far)?;
4061        }
4062    }
4063    let semitones =
4064        |chromatic: IntegerType| chromatic.checked_add(octave_change).ok_or_else(too_far);
4065    match (diatonic, chromatic) {
4066        (Some(diatonic), Some(chromatic)) => {
4067            let actual = |steps: IntegerType| {
4068                let generic = if steps < 0 {
4069                    steps.checked_sub(1)
4070                } else {
4071                    steps.checked_add(1)
4072                };
4073                generic.ok_or_else(too_far)
4074            };
4075            let semitones = semitones(chromatic)?;
4076            Interval::from_generic_and_chromatic(actual(diatonic)?, semitones).or_else(|_| {
4077                let steps = diatonic - octave_change / 12 * 7;
4078                Interval::from_generic_and_chromatic(actual(steps)?, semitones)
4079            })
4080        }
4081        (None, Some(chromatic)) => Interval::from_semitones(semitones(chromatic)?),
4082        _ => Interval::from_name("P1"),
4083    }
4084}
4085
4086#[cfg(test)]
4087mod tests {
4088    use super::*;
4089    use crate::musicxml::{ExportOptions, to_musicxml};
4090
4091    /// A score of one part holding these measures.
4092    fn score(measures: &str) -> String {
4093        format!(
4094            "<?xml version=\"1.0\"?><score-partwise version=\"4.0\"><part-list>             <score-part id=\"P1\"><part-name>Flute</part-name></score-part></part-list>             <part id=\"P1\">{measures}</part></score-partwise>"
4095        )
4096    }
4097
4098    const NOTE: &str = "<note><pitch><step>C</step><octave>4</octave></pitch>                        <duration>1</duration><type>quarter</type></note>";
4099
4100    #[test]
4101    fn a_document_nested_too_deep_is_refused() {
4102        // The score and its part are two levels of their own.
4103        for depth in [crate::xml::MAX_DEPTH - 2, 100_000] {
4104            let document = score(&format!(
4105                "<measure number=\"1\">{NOTE}</measure>{}{}",
4106                "<a>".repeat(depth),
4107                "</a>".repeat(depth)
4108            ));
4109            assert_eq!(
4110                from_musicxml(&document).is_err(),
4111                depth > crate::xml::MAX_DEPTH - 2,
4112                "{depth}"
4113            );
4114        }
4115    }
4116
4117    #[test]
4118    fn a_midi_program_below_any_integer_reads_as_the_first() {
4119        let document = score(&format!("<measure number=\"1\">{NOTE}</measure>")).replace(
4120            "<part-name>Flute</part-name>",
4121            "<part-name>Flute</part-name><midi-instrument id=\"P1-I1\">\
4122             <midi-program>-2147483648</midi-program></midi-instrument>",
4123        );
4124        assert!(from_musicxml(&document).is_ok());
4125    }
4126
4127    #[test]
4128    fn a_rest_shown_past_the_last_octave_is_refused() {
4129        let rest = "<note><rest><display-step>C</display-step>\
4130                    <display-octave>2147483647</display-octave></rest>\
4131                    <duration>1</duration><type>quarter</type></note>";
4132        assert!(from_musicxml(&score(&format!("<measure number=\"1\">{rest}</measure>"))).is_err());
4133    }
4134
4135    #[test]
4136    fn a_refusal_names_its_measure_and_part() {
4137        let rest = "<note><rest><display-step>C</display-step>\
4138                    <display-octave>2147483647</display-octave></rest>\
4139                    <duration>1</duration><type>quarter</type></note>";
4140        let measures = format!(
4141            "<measure number=\"1\">{NOTE}</measure><measure number=\"2a\">{rest}</measure>"
4142        );
4143        let error = from_musicxml(&score(&measures)).unwrap_err();
4144        assert!(
4145            error
4146                .to_string()
4147                .starts_with("MusicXML export error: a rest shown at")
4148        );
4149        assert_eq!(
4150            error.position(),
4151            Some(&Position::measure("2a").in_part("P1"))
4152        );
4153    }
4154
4155    #[test]
4156    fn a_measure_after_the_largest_number_is_read() {
4157        let measures = format!(
4158            "<measure number=\"2147483647\">{NOTE}</measure><measure number=\"5X\">{NOTE}</measure>"
4159        );
4160        assert!(from_musicxml(&score(&measures)).is_ok());
4161    }
4162
4163    #[test]
4164    fn a_transposition_of_too_many_octaves_is_refused() {
4165        for change in [
4166            "<diatonic>1</diatonic><chromatic>2</chromatic>",
4167            "<chromatic>2</chromatic>",
4168        ] {
4169            let transpose = Xml::parse(&format!(
4170                "<transpose>{change}<octave-change>2147483647</octave-change></transpose>"
4171            ))
4172            .unwrap();
4173            assert!(transpose_interval(&transpose).is_err(), "{change}");
4174        }
4175    }
4176
4177    #[test]
4178    fn offsets_are_snapped_as_music21_snaps_them() {
4179        // music21's `opFrac`: a third is a third, not the float nearest it.
4180        assert_eq!(op_frac(0.333_333_333_333_3), 1.0 / 3.0);
4181        assert_eq!(op_frac(0.25), 0.25);
4182        assert_eq!(op_frac(1.0 / 3.0 + 1.0 / 3.0 + 1.0 / 3.0), 1.0);
4183        assert_eq!(op_frac(-2.0 / 3.0), -2.0 / 3.0);
4184    }
4185
4186    #[test]
4187    fn an_ending_is_numbered_as_music21_numbers_it() {
4188        assert_eq!(bracket_numbers(""), [0]);
4189        assert_eq!(bracket_numbers("2"), [2]);
4190        assert_eq!(bracket_numbers("1, 2"), [1, 2]);
4191        assert_eq!(bracket_numbers("1-3"), [1, 2, 3]);
4192        assert_eq!(bracket_numbers("first"), [1]);
4193    }
4194
4195    #[test]
4196    fn a_part_and_its_notes_are_read() {
4197        let read = from_musicxml(&score(&format!(
4198            "<measure number=\"1\"><attributes><divisions>1</divisions>             <key><fifths>-1</fifths><mode>major</mode></key>             <time><beats>2</beats><beat-type>4</beat-type></time>             <clef><sign>G</sign><line>2</line></clef></attributes>{NOTE}             <note><rest/><duration>1</duration><type>quarter</type></note></measure>"
4199        )))
4200        .unwrap();
4201        let part = read.parts()[0];
4202        assert_eq!(part.name(), Some("Flute"));
4203        let measure = part.measures()[0];
4204        assert_eq!(measure.number(), 1);
4205        let kinds: Vec<&str> = measure
4206            .events()
4207            .iter()
4208            .map(|event| match event.element() {
4209                StreamElement::Clef(_) => "clef",
4210                StreamElement::Key(_) => "key",
4211                StreamElement::TimeSignature(_) => "time",
4212                StreamElement::Note(_) => "note",
4213                StreamElement::Rest(_) => "rest",
4214                _ => "other",
4215            })
4216            .collect();
4217        // Sorted as music21 sorts a measure, whatever order the file had.
4218        assert_eq!(kinds, ["clef", "key", "time", "note", "rest"]);
4219        assert_eq!(measure.events()[4].offset(), 1.0);
4220    }
4221
4222    #[test]
4223    fn notes_sharing_a_stem_are_a_chord() {
4224        let read = from_musicxml(&score(&format!(
4225            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>{NOTE}             <note><chord/><pitch><step>E</step><octave>4</octave></pitch>             <duration>1</duration><type>quarter</type></note></measure>"
4226        )))
4227        .unwrap();
4228        let measure = read.parts()[0].measures()[0];
4229        let chords: Vec<_> = measure
4230            .events()
4231            .iter()
4232            .filter_map(|event| match event.element() {
4233                StreamElement::Chord(chord) => Some(chord),
4234                _ => None,
4235            })
4236            .collect();
4237        assert_eq!(chords.len(), 1);
4238        assert_eq!(chords[0].notes().len(), 2);
4239    }
4240
4241    #[test]
4242    fn two_voices_are_two_streams() {
4243        let voiced = |voice: u8| {
4244            format!(
4245                "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>                 <voice>{voice}</voice><type>quarter</type></note>"
4246            )
4247        };
4248        let read = from_musicxml(&score(&format!(
4249            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>{}             <backup><duration>1</duration></backup>{}</measure>",
4250            voiced(1),
4251            voiced(2)
4252        )))
4253        .unwrap();
4254        let measure = read.parts()[0].measures()[0];
4255        let voices = measure.voices();
4256        assert_eq!(voices.len(), 2);
4257        assert_eq!(voices[1].id(), Some("2"));
4258        // The second voice was backed up to the start of the measure.
4259        assert_eq!(voices[1].events()[0].offset(), 0.0);
4260    }
4261
4262    #[test]
4263    fn a_slur_joins_the_notes_it_is_written_on() {
4264        let slurred = |kind: &str| {
4265            format!(
4266                "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>                 <type>quarter</type><notations><slur number=\"1\" type=\"{kind}\"/>                 </notations></note>"
4267            )
4268        };
4269        let read = from_musicxml(&score(&format!(
4270            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>{}{NOTE}{}             </measure>",
4271            slurred("start"),
4272            slurred("stop")
4273        )))
4274        .unwrap();
4275        let spanners = read.spanners();
4276        assert_eq!(spanners.len(), 1);
4277        assert_eq!(spanners[0].kind(), SpannerKind::Slur);
4278        let leaves = read.leaves();
4279        let ends: Vec<usize> = spanners[0].spanned().iter().flatten().copied().collect();
4280        assert_eq!(ends.len(), 2);
4281        assert!(matches!(leaves[ends[0]].1, StreamElement::Note(_)));
4282        assert_eq!(leaves[ends[1]].0, 2.0);
4283    }
4284
4285    #[test]
4286    fn a_hairpin_started_after_a_part_ends_takes_the_next_part_first_note() {
4287        let wedge = |kind: &str| {
4288            format!(
4289                "<direction><direction-type><wedge type=\"{kind}\"/></direction-type></direction>"
4290            )
4291        };
4292        let measure = |step: &str, after: &str| {
4293            format!(
4294                "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>\
4295                 <note><pitch><step>{step}</step><octave>4</octave></pitch><duration>1</duration>\
4296                 <type>quarter</type></note>{after}</measure>"
4297            )
4298        };
4299        let document = format!(
4300            "<?xml version=\"1.0\"?><score-partwise version=\"4.0\"><part-list>\
4301             <score-part id=\"P1\"><part-name>One</part-name></score-part>\
4302             <score-part id=\"P2\"><part-name>Two</part-name></score-part></part-list>\
4303             <part id=\"P1\">{}</part><part id=\"P2\">{}</part></score-partwise>",
4304            measure("C", &(wedge("diminuendo") + &wedge("stop"))),
4305            measure("D", ""),
4306        );
4307        let read = from_musicxml(&document).unwrap();
4308        let spanners = read.spanners();
4309        assert_eq!(spanners.len(), 1);
4310        assert_eq!(spanners[0].kind(), SpannerKind::Diminuendo);
4311        let leaves = read.leaves();
4312        let named: Vec<String> = spanners[0]
4313            .spanned()
4314            .iter()
4315            .flatten()
4316            .map(|position| match leaves[*position].1 {
4317                StreamElement::Note(note) => note.pitch().name_with_octave(),
4318                _ => String::new(),
4319            })
4320            .collect();
4321        // The stop names the note it follows, and the queue then puts the
4322        // next note read, in the next part, first.
4323        assert_eq!(named, ["D4", "C4"]);
4324    }
4325
4326    #[test]
4327    fn a_part_on_two_staves_is_two_parts_in_a_brace() {
4328        let on_staff = |staff: u8| {
4329            format!(
4330                "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>                 <type>quarter</type><staff>{staff}</staff></note>"
4331            )
4332        };
4333        let read = from_musicxml(&score(&format!(
4334            "<measure number=\"1\"><attributes><divisions>1</divisions><staves>2</staves>             </attributes>{}<backup><duration>1</duration></backup>{}</measure>",
4335            on_staff(1),
4336            on_staff(2)
4337        )))
4338        .unwrap();
4339        let parts = read.parts();
4340        assert_eq!(parts.len(), 2);
4341        assert_eq!(parts[0].kind(), StreamKind::PartStaff);
4342        assert_eq!(parts[1].id(), Some("P1-Staff2"));
4343        let groups = read.staff_groups();
4344        assert_eq!(groups.len(), 1);
4345        assert_eq!(groups[0].parts(), [0, 1]);
4346        assert_eq!(groups[0].symbol(), Some("brace"));
4347    }
4348
4349    #[test]
4350    fn what_is_read_is_written_back() {
4351        let document = score(&format!(
4352            "<measure number=\"1\"><attributes><divisions>1</divisions>             <time><beats>1</beats><beat-type>4</beat-type></time>             <clef><sign>G</sign><line>2</line></clef></attributes>{NOTE}</measure>"
4353        ));
4354        let written = to_musicxml(
4355            &from_musicxml(&document).unwrap(),
4356            &ExportOptions::default(),
4357        )
4358        .unwrap();
4359        assert!(written.contains("<part-name>Flute</part-name>"));
4360        assert!(written.contains("<step>C</step>"));
4361        assert!(written.contains("<type>quarter</type>"));
4362        assert!(written.contains("<beat-type>4</beat-type>"));
4363    }
4364
4365    /// A quarter note carrying these notations.
4366    fn noted(notations: &str) -> String {
4367        format!(
4368            "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>\
4369             <type>quarter</type><notations>{notations}</notations></note>"
4370        )
4371    }
4372
4373    /// One measure of a quarter to the division holding these.
4374    fn measure_of(contents: &str) -> String {
4375        score(&format!(
4376            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>\
4377             {contents}</measure>"
4378        ))
4379    }
4380
4381    fn written_back(read: &Stream) -> String {
4382        to_musicxml(read, &ExportOptions::default()).unwrap()
4383    }
4384
4385    #[test]
4386    fn a_glissando_and_a_slide_are_read() {
4387        let read = from_musicxml(&measure_of(&format!(
4388            "{}{}{}",
4389            noted("<glissando number=\"1\" type=\"start\" line-type=\"dashed\">gl.</glissando>"),
4390            noted("<glissando number=\"1\" type=\"stop\"/><slide number=\"2\" type=\"start\"/>"),
4391            noted("<slide number=\"2\" type=\"stop\"/>"),
4392        )))
4393        .unwrap();
4394        let spanners = read.spanners();
4395        assert_eq!(spanners.len(), 2);
4396        assert!(
4397            spanners
4398                .iter()
4399                .all(|spanner| spanner.kind() == SpannerKind::Glissando && spanner.len() == 2)
4400        );
4401        // music21 reads the line and how it is played, and not the words.
4402        assert_eq!(spanners[0].line_type(), Some("dashed"));
4403        assert_eq!(
4404            spanners[0].glissando_details(),
4405            Some(&Glissando {
4406                slide_type: SlideType::Chromatic,
4407                label: None
4408            })
4409        );
4410        // A slide is drawn solid where nothing says.
4411        assert_eq!(spanners[1].line_type(), Some("solid"));
4412        assert_eq!(
4413            spanners[1]
4414                .glissando_details()
4415                .map(|slide| slide.slide_type),
4416            Some(SlideType::Continuous)
4417        );
4418        let written = written_back(&read);
4419        assert!(written.contains("<glissando line-type=\"dashed\" number=\"1\" type=\"start\" />"));
4420        assert!(written.contains("<slide line-type=\"solid\" number=\"2\" type=\"stop\" />"));
4421    }
4422
4423    #[test]
4424    fn a_glissando_drawn_no_way_music21_knows_is_refused() {
4425        let read = from_musicxml(&measure_of(&noted(
4426            "<glissando number=\"1\" type=\"start\" line-type=\"zigzag\"/>",
4427        )));
4428        assert!(read.is_err());
4429    }
4430
4431    #[test]
4432    fn a_tremolo_that_starts_and_stops_is_a_spanner() {
4433        let read = from_musicxml(&measure_of(&format!(
4434            "{}{}{}",
4435            noted("<ornaments><tremolo type=\"start\" placement=\"above\">2</tremolo></ornaments>"),
4436            noted("<ornaments><tremolo type=\"stop\">2</tremolo></ornaments>"),
4437            noted("<ornaments><tremolo type=\"single\">1</tremolo></ornaments>"),
4438        )))
4439        .unwrap();
4440        let spanners = read.spanners();
4441        assert_eq!(spanners.len(), 1);
4442        assert_eq!(spanners[0].kind(), SpannerKind::TremoloSpanner);
4443        assert_eq!(spanners[0].number_of_marks(), Some(2));
4444        assert_eq!(spanners[0].placement(), Some(Placement::Above));
4445        assert_eq!(spanners[0].len(), 2);
4446        // The tremolo on one note is an ornament of that note still.
4447        let ornamented = read
4448            .leaves()
4449            .iter()
4450            .filter(|(_, element)| match element {
4451                StreamElement::Note(note) => !note.expressions().is_empty(),
4452                _ => false,
4453            })
4454            .count();
4455        assert_eq!(ornamented, 1);
4456        let written = written_back(&read);
4457        assert!(written.contains("<tremolo placement=\"above\" type=\"start\">2</tremolo>"));
4458        assert!(written.contains("<tremolo type=\"stop\">2</tremolo>"));
4459    }
4460
4461    #[test]
4462    fn a_multiple_rest_takes_the_rests_it_counts() {
4463        let rest = "<note><rest/><duration>4</duration></note>";
4464        let styled = |count: u32| {
4465            format!(
4466                "<attributes><measure-style><multiple-rest>{count}</multiple-rest>\
4467                 </measure-style></attributes>{rest}"
4468            )
4469        };
4470        let read = from_musicxml(&score(&format!(
4471            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>{}</measure>\
4472             <measure number=\"2\">{rest}</measure>\
4473             <measure number=\"3\">{}</measure>",
4474            styled(2),
4475            styled(3),
4476        )))
4477        .unwrap();
4478        let spanners = read.spanners();
4479        assert_eq!(spanners.len(), 2);
4480        assert_eq!(spanners[0].kind(), SpannerKind::MultiMeasureRest);
4481        assert_eq!(spanners[0].num_rests(), 2);
4482        assert!(
4483            spanners[0]
4484                .multi_rest()
4485                .is_some_and(|said| !said.use_symbols)
4486        );
4487        // The last took one rest of its three before the part ended, and
4488        // music21's writer writes it as one.
4489        assert_eq!(spanners[1].num_rests(), 1);
4490        let written = written_back(&read);
4491        assert_eq!(written.matches("<multiple-rest").count(), 2);
4492        assert!(written.contains("<multiple-rest use-symbols=\"no\">2</multiple-rest>"));
4493        assert!(written.contains("<multiple-rest use-symbols=\"no\">1</multiple-rest>"));
4494    }
4495
4496    fn pedal(attributes: &str) -> String {
4497        format!("<direction><direction-type><pedal {attributes}/></direction-type></direction>")
4498    }
4499
4500    #[test]
4501    fn a_pedal_keeps_its_bounces_and_gaps() {
4502        let read = from_musicxml(&measure_of(&format!(
4503            "{}{NOTE}{}{NOTE}{}{NOTE}{}{NOTE}{}",
4504            pedal("type=\"start\" line=\"yes\" number=\"1\""),
4505            pedal("type=\"change\" line=\"yes\" number=\"1\""),
4506            pedal("type=\"discontinue\" line=\"yes\" number=\"1\""),
4507            pedal("type=\"resume\" line=\"yes\" number=\"1\""),
4508            pedal("type=\"stop\" line=\"yes\" number=\"1\""),
4509        )))
4510        .unwrap();
4511        let spanners = read.spanners();
4512        assert_eq!(spanners.len(), 1);
4513        assert_eq!(spanners[0].kind(), SpannerKind::PedalMark);
4514        let leaves = read.leaves();
4515        let joined: Vec<String> = spanners[0]
4516            .spanned()
4517            .iter()
4518            .flatten()
4519            .map(|position| match leaves[*position] {
4520                (offset, StreamElement::PedalObject(object)) => {
4521                    format!("{} at {offset}", object.kind().class_name())
4522                }
4523                (offset, _) => format!("note at {offset}"),
4524            })
4525            .collect();
4526        assert_eq!(
4527            joined,
4528            [
4529                "note at 0",
4530                "PedalBounce at 1",
4531                "PedalGapStart at 2",
4532                "PedalGapEnd at 3",
4533                "note at 3"
4534            ]
4535        );
4536        let written = written_back(&read);
4537        for kind in ["change", "discontinue", "resume"] {
4538            assert!(written.contains(&format!("<pedal line=\"yes\" type=\"{kind}\" />")));
4539        }
4540    }
4541
4542    #[test]
4543    fn a_sign_with_a_line_resumed_at_once_is_one_pedal() {
4544        let read = from_musicxml(&measure_of(&format!(
4545            "{NOTE}{}{}{NOTE}{}",
4546            pedal("type=\"start\" sign=\"yes\" number=\"1\""),
4547            pedal("type=\"resume\" line=\"yes\" number=\"1\""),
4548            pedal("type=\"stop\" line=\"yes\" number=\"1\""),
4549        )))
4550        .unwrap();
4551        let spanners = read.spanners();
4552        assert_eq!(
4553            spanners[0].pedal().and_then(|pedal| pedal.form),
4554            Some(PedalForm::SymbolLine)
4555        );
4556        assert!(
4557            read.leaves()
4558                .iter()
4559                .all(|(_, element)| !matches!(element, StreamElement::PedalObject(_)))
4560        );
4561        // The line resumes where the pedal mark stands, the start of the
4562        // part, a quarter before the note it goes down on.
4563        let written = written_back(&read);
4564        assert!(written.contains(
4565            "<pedal line=\"yes\" type=\"resume\" />\n        </direction-type>\n        \
4566             <offset sound=\"yes\">-10080</offset>"
4567        ));
4568    }
4569
4570    #[test]
4571    fn a_barline_inside_a_measure_stands_where_music21_appends_it() {
4572        let barline = "<barline location=\"middle\"><bar-style>dashed</bar-style></barline>";
4573        let inner = |document: &str| -> Vec<FloatType> {
4574            let read = from_musicxml(document).unwrap();
4575            let measure = read.parts()[0].measures()[0];
4576            measure
4577                .events()
4578                .iter()
4579                .filter(|event| matches!(event.element(), StreamElement::Barline(_)))
4580                .map(StreamEvent::offset)
4581                .collect()
4582        };
4583        // After what has been read, wherever the file would have it.
4584        assert_eq!(
4585            inner(&measure_of(&format!(
4586                "{NOTE}{NOTE}{barline}{NOTE}{NOTE}{barline}"
4587            ))),
4588            [2.0, 4.0]
4589        );
4590        // And at the start of a measure whose notes are all in voices.
4591        let voiced = |voice: u8| {
4592            format!(
4593                "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>\
4594                 <voice>{voice}</voice><type>quarter</type></note>"
4595            )
4596        };
4597        assert_eq!(
4598            inner(&measure_of(&format!(
4599                "{}{barline}<backup><duration>1</duration></backup>{}",
4600                voiced(1),
4601                voiced(2)
4602            ))),
4603            [0.0]
4604        );
4605        // Nothing is written for one.
4606        let read = from_musicxml(&measure_of(&format!("{NOTE}{barline}{NOTE}"))).unwrap();
4607        assert!(!written_back(&read).contains("<barline"));
4608    }
4609
4610    #[test]
4611    fn a_lyric_says_how_it_is_drawn() {
4612        let read = from_musicxml(&measure_of(
4613            "<note><pitch><step>C</step><octave>4</octave></pitch><duration>1</duration>\
4614             <type>quarter</type><lyric number=\"1\" justify=\"left\" placement=\"below\" \
4615             print-object=\"yes\"><syllabic>single</syllabic><text>la</text></lyric></note>",
4616        ))
4617        .unwrap();
4618        let leaves = read.leaves();
4619        let note = leaves
4620            .iter()
4621            .find_map(|(_, element)| match element {
4622                StreamElement::Note(note) => Some(note),
4623                _ => None,
4624            })
4625            .expect("the measure holds a note");
4626        let lyric = &note.lyrics()[0];
4627        assert_eq!(lyric.justify(), Some(Justification::Left));
4628        assert_eq!(lyric.placement(), Some(Placement::Below));
4629        assert!(!lyric.is_hidden());
4630        assert!(
4631            written_back(&read)
4632                .contains("<lyric justify=\"left\" name=\"1\" number=\"1\" placement=\"below\">")
4633        );
4634    }
4635
4636    #[test]
4637    fn a_rehearsal_mark_is_read_and_written_centred() {
4638        let read = from_musicxml(&measure_of(&format!(
4639            "<direction placement=\"above\"><direction-type>             <rehearsal enclosure=\"square\">A</rehearsal></direction-type></direction>{NOTE}"
4640        )))
4641        .unwrap();
4642        let leaves = read.leaves();
4643        let mark = leaves
4644            .iter()
4645            .find_map(|(_, element)| match element {
4646                StreamElement::RehearsalMark(mark) => Some(mark),
4647                _ => None,
4648            })
4649            .expect("the measure holds a rehearsal mark");
4650        assert_eq!(mark.content(), "A");
4651        assert_eq!(mark.enclosure(), Some("square"));
4652        assert_eq!(mark.placement(), Some(Placement::Above));
4653        assert!(written_back(&read).contains(
4654            "<rehearsal enclosure=\"square\" halign=\"center\" valign=\"middle\">A</rehearsal>"
4655        ));
4656        // music21 raises on an enclosure it has no name for.
4657        assert!(
4658            from_musicxml(&measure_of(
4659                "<direction><direction-type><rehearsal enclosure=\"star\">A</rehearsal>                 </direction-type></direction>"
4660            ))
4661            .is_err()
4662        );
4663    }
4664
4665    #[test]
4666    fn a_rests_colour_is_read_and_written_back() {
4667        let read = from_musicxml(&score(
4668            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>             <note color=\"#FF0000\"><rest/><duration>1</duration><type>quarter</type></note>             </measure>",
4669        ))
4670        .unwrap();
4671        let leaves = read.leaves();
4672        let rest = leaves
4673            .iter()
4674            .find_map(|(_, element)| match element {
4675                StreamElement::Rest(rest) => Some(rest),
4676                _ => None,
4677            })
4678            .expect("the measure holds a rest");
4679        assert_eq!(rest.color(), Some("#FF0000"));
4680        assert!(written_back(&read).contains("<note color=\"#FF0000\">"));
4681    }
4682
4683    #[test]
4684    fn a_time_signature_may_be_left_unprinted() {
4685        let read = from_musicxml(&score(
4686            "<measure number=\"1\"><attributes><divisions>1</divisions>             <time print-object=\"no\"><beats>3</beats><beat-type>4</beat-type></time>             </attributes></measure>",
4687        ))
4688        .unwrap();
4689        let leaves = read.leaves();
4690        let meter = leaves
4691            .iter()
4692            .find_map(|(_, element)| match element {
4693                StreamElement::TimeSignature(meter) => Some(meter),
4694                _ => None,
4695            })
4696            .expect("the measure holds a time signature");
4697        assert!(meter.is_hidden());
4698        assert!(written_back(&read).contains("<time print-object=\"no\">"));
4699    }
4700
4701    #[test]
4702    fn a_metric_modulation_takes_its_numbers_from_the_tempo_before_it() {
4703        // Read off music21: a dotted quarter at 110, then a quarter becoming
4704        // a dotted quarter, sounds at 247.5 quarters a minute.
4705        let read = from_musicxml(&score(&format!(
4706            "<measure number=\"1\"><attributes><divisions>1</divisions></attributes>\
4707             <direction><direction-type><metronome><beat-unit>quarter</beat-unit>\
4708             <beat-unit-dot/><per-minute>110</per-minute></metronome></direction-type>\
4709             </direction>{NOTE}</measure><measure number=\"2\">\
4710             <direction placement=\"above\"><direction-type><metronome>\
4711             <beat-unit>quarter</beat-unit><beat-unit>quarter</beat-unit><beat-unit-dot/>\
4712             </metronome></direction-type></direction>{NOTE}</measure>"
4713        )))
4714        .unwrap();
4715        let leaves = read.leaves();
4716        let modulation = leaves
4717            .iter()
4718            .find_map(|(_, element)| match element {
4719                StreamElement::MetricModulation(modulation) => Some(modulation),
4720                _ => None,
4721            })
4722            .expect("the second measure holds a metric modulation");
4723        // As read, neither side has a number: a file gives the note values.
4724        assert_eq!(
4725            modulation.old_metronome().and_then(|old| old.number()),
4726            None
4727        );
4728        assert_eq!(modulation.placement(), Some(Placement::Above));
4729        let written = written_back(&read);
4730        assert!(written.contains(
4731            "<metronome parentheses=\"no\">\n            <beat-unit>quarter</beat-unit>\n            \
4732             <beat-unit>quarter</beat-unit>\n            <beat-unit-dot />\n          \
4733             </metronome>\n        </direction-type>\n        <sound tempo=\"247.5\" />"
4734        ));
4735    }
4736
4737    #[test]
4738    fn words_naming_a_coda_place_no_sign() {
4739        let words = |text: &str| {
4740            format!(
4741                "<direction placement=\"above\"><direction-type><words>{text}</words>                 </direction-type></direction>"
4742            )
4743        };
4744        let read = from_musicxml(&measure_of(&format!(
4745            "{}{NOTE}{}",
4746            words("Coda"),
4747            words("Fine")
4748        )))
4749        .unwrap();
4750        let placed: Vec<Option<Placement>> = read
4751            .leaves()
4752            .iter()
4753            .filter_map(|(_, element)| match element {
4754                StreamElement::RepeatExpression(mark) => Some(mark.placement()),
4755                _ => None,
4756            })
4757            .collect();
4758        // The coda is drawn as its sign, which music21 places nowhere; the
4759        // words of a fine stay where they were written.
4760        assert_eq!(placed, [None, Some(Placement::Above)]);
4761    }
4762
4763    #[test]
4764    fn a_document_that_is_not_a_score_is_refused() {
4765        assert!(from_musicxml("<opus/>").is_err());
4766        assert!(from_musicxml("not xml at all").is_err());
4767    }
4768
4769    #[test]
4770    fn a_group_symbol_keeps_where_it_is_drawn() {
4771        let document = format!(
4772            "<?xml version=\"1.0\"?><score-partwise version=\"4.0\"><part-list>\
4773             <part-group number=\"1\" type=\"start\"><group-symbol default-x=\"-8\" \
4774             color=\"#FF0000\">bracket</group-symbol></part-group>\
4775             <score-part id=\"P1\"><part-name>Flute</part-name></score-part>\
4776             <part-group number=\"1\" type=\"stop\"/></part-list>\
4777             <part id=\"P1\"><measure number=\"1\"><attributes><divisions>1\
4778             </divisions></attributes>{NOTE}</measure></part></score-partwise>"
4779        );
4780        let read = from_musicxml(&document).unwrap();
4781        let group = &read.staff_groups()[0];
4782        assert_eq!(group.symbol(), Some("bracket"));
4783        assert_eq!(group.style.absolute_x, Some(StyleValue::Whole(-8)));
4784        assert_eq!(group.style.color.as_deref(), Some("#FF0000"));
4785    }
4786
4787    #[test]
4788    fn credits_are_read_and_written_as_music21_keeps_them() {
4789        let document = format!(
4790            "<?xml version=\"1.0\"?><score-partwise version=\"4.0\">\
4791             <credit page=\"2\"><credit-words default-x=\"600.5\" default-y=\"1500\" \
4792             font-size=\"22.0\" font-style=\"bolditalic\" justify=\"Center\" valign=\"top\">\
4793             Sonata</credit-words><credit-words>in G</credit-words></credit>\
4794             <credit><credit-words halign=\"right\">Anon</credit-words></credit>\
4795             <part-list><score-part id=\"P1\"><part-name>Flute</part-name></score-part>\
4796             </part-list><part id=\"P1\"><measure number=\"1\"><attributes><divisions>1\
4797             </divisions></attributes>{NOTE}</measure></part></score-partwise>"
4798        );
4799        let read = from_musicxml(&document).unwrap();
4800        let boxes: Vec<&TextBox> = read
4801            .events()
4802            .iter()
4803            .filter_map(|event| match event.element() {
4804                StreamElement::TextBox(text) => Some(&**text),
4805                _ => None,
4806            })
4807            .collect();
4808        assert_eq!(boxes.len(), 2);
4809        assert_eq!(boxes[0].content, "Sonata\nin G");
4810        assert_eq!(boxes[0].page, 2);
4811        assert_eq!(boxes[0].style.font_size, Some(StyleValue::Whole(22)));
4812        assert_eq!(boxes[0].style.justify(), Some("center"));
4813        // A credit that says no place stands where a fresh text box does.
4814        assert_eq!(boxes[1].style.absolute_x, Some(StyleValue::Whole(500)));
4815        assert_eq!(boxes[1].page, 1);
4816
4817        let written = written_back(&read);
4818        for expected in [
4819            "<credit page=\"2\">",
4820            "default-x=\"600.5\"",
4821            "font-size=\"22\"",
4822            "font-style=\"italic\"",
4823            "font-weight=\"bold\"",
4824            "justify=\"center\"",
4825            "xml:space=\"preserve\"",
4826            "<credit-words default-x=\"500\" default-y=\"500\" halign=\"right\">Anon</credit-words>",
4827        ] {
4828            assert!(written.contains(expected), "{expected} in\n{written}");
4829        }
4830        assert!(written.find("<credit").unwrap() < written.find("<part-list>").unwrap());
4831        // A page that is no number is refused, as music21's `int` refuses it.
4832        assert!(from_musicxml("<score-partwise><credit page=\"x\"/></score-partwise>").is_err());
4833    }
4834
4835    #[test]
4836    fn layouts_are_read_and_written_as_music21_keeps_them() {
4837        let document = format!(
4838            "<?xml version=\"1.0\"?><score-partwise version=\"4.0\">             <defaults><scaling><millimeters>6.5</millimeters><tenths>40</tenths></scaling>             <page-layout><page-height>1800.0</page-height><page-width>1400</page-width>             <page-margins><left-margin>80</left-margin><right-margin>80</right-margin>             </page-margins></page-layout><staff-layout><staff-distance>90</staff-distance>             </staff-layout></defaults><part-list><score-part id=\"P1\"><part-name>Flute             </part-name></score-part></part-list><part id=\"P1\"><measure number=\"1\">             <print new-system=\"yes\"><system-layout><system-distance>120</system-distance>             </system-layout><staff-layout number=\"1\"><staff-distance>70.5</staff-distance>             </staff-layout><measure-numbering>system</measure-numbering></print>             <attributes><divisions>1</divisions><staff-details number=\"1\" print-object=\"no\">             <staff-lines>4</staff-lines></staff-details></attributes>{NOTE}</measure></part>             </score-partwise>"
4839        );
4840        let read = from_musicxml(&document).unwrap();
4841        let score_layout = read
4842            .events()
4843            .iter()
4844            .find_map(|event| match event.element() {
4845                StreamElement::Layout(Layout::Score(layout)) => Some(layout),
4846                _ => None,
4847            })
4848            .expect("the defaults are a score layout");
4849        // `1800.0` is kept whole, as music21's `_floatOrIntStr` keeps it.
4850        assert_eq!(
4851            score_layout.page_layout.as_ref().unwrap().page_height,
4852            Some(Tenths::Whole(1800))
4853        );
4854        assert_eq!(
4855            score_layout.scaling_millimeters,
4856            Some(Tenths::Fraction(6.5))
4857        );
4858        let measure = &read.parts()[0].measures()[0];
4859        assert_eq!(measure.measure_numbering(), Some("system"));
4860        let staff = measure
4861            .events()
4862            .iter()
4863            .find_map(|event| match event.element() {
4864                StreamElement::Layout(Layout::Staff(layout)) => Some(layout),
4865                _ => None,
4866            })
4867            .expect("a staff layout");
4868        // The details are read into the staff layout the print made.
4869        assert_eq!(staff.distance, Some(Tenths::Fraction(70.5)));
4870        assert_eq!(staff.staff_lines, Some(4));
4871        assert_eq!(staff.hidden, Some(true));
4872
4873        let written = written_back(&read);
4874        for expected in [
4875            "<millimeters>6.5</millimeters>",
4876            "<page-height>1800</page-height>",
4877            "<staff-distance>90</staff-distance>",
4878            "<print new-system=\"yes\">",
4879            "<system-distance>120</system-distance>",
4880            "<staff-layout number=\"1\">",
4881            "<measure-numbering>system</measure-numbering>",
4882            "<staff-details print-object=\"no\">",
4883            "<staff-lines>4</staff-lines>",
4884        ] {
4885            assert!(
4886                written.contains(expected),
4887                "{expected} in
4888{written}"
4889            );
4890        }
4891    }
4892}