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