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