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