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