1use crate::{Diagnostic, Span};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct Tune {
5 pub reference: String,
6 pub title: String,
7 pub meter: String,
8 pub key: String,
9 pub divisions: u32,
10 pub events: Vec<Event>,
11 pub voices: Vec<VoiceTimeline>,
12 pub score_directives: Vec<ScoreDirectiveModel>,
13 pub preserved_directives: Vec<PreservedDirective>,
14 pub post_tune_lyrics: Vec<TextLine>,
15 pub score: Score,
16}
17
18pub type Rational = Fraction;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Score {
24 pub metadata: ScoreMetadata,
25 pub parts: Vec<Part>,
26 pub diagnostics: Vec<Diagnostic>,
27 pub divisions: u32,
28 pub source_span: Span,
29 pub accidental_policy: AccidentalPolicy,
30}
31
32impl Score {
33 pub(crate) fn empty(span: Span, diagnostics: Vec<Diagnostic>) -> Self {
36 Self {
37 metadata: ScoreMetadata::empty(span),
38 parts: Vec::new(),
39 diagnostics,
40 divisions: 1,
41 source_span: span,
42 accidental_policy: AccidentalPolicy::abc_default(span),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ScoreMetadata {
49 pub reference: TextLine,
50 pub title: Option<TextLine>,
51 pub composers: Vec<TextLine>,
52 pub tempo: Option<TextLine>,
53 pub tempo_model: Option<TempoModel>,
55 pub meter: Option<MeterModel>,
56 pub key: Option<KeySignatureModel>,
57 pub directives: Vec<ScoreDirectiveModel>,
58 pub preserved_directives: Vec<PreservedDirective>,
59 pub post_tune_lyrics: Vec<TextLine>,
60 pub source_span: Span,
61}
62
63impl ScoreMetadata {
64 pub(crate) fn empty(span: Span) -> Self {
66 Self {
67 reference: TextLine {
68 text: String::new(),
69 span,
70 },
71 title: None,
72 composers: Vec::new(),
73 tempo: None,
74 tempo_model: None,
75 meter: None,
76 key: None,
77 directives: Vec::new(),
78 preserved_directives: Vec::new(),
79 post_tune_lyrics: Vec::new(),
80 source_span: span,
81 }
82 }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct PreservedDirective {
87 pub name: TextLine,
88 pub value: TextLine,
89 pub span: Span,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct MeterModel {
94 pub display: String,
95 pub time_symbol: Option<String>,
99 pub duration: Option<Rational>,
100 pub free_meter: bool,
101 pub preserve_restatement: bool,
104 pub source_span: Span,
105}
106
107impl MeterModel {
108 pub(crate) fn structurally_matches(&self, other: &Self) -> bool {
109 self.display == other.display && self.time_symbol == other.time_symbol
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct TempoModel {
120 pub text: Option<String>,
122 pub beat: Option<TempoBeat>,
124 pub beat_role: TempoBeatRole,
129 pub source_span: Span,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum TempoBeatRole {
134 PrintedMetronome,
135 PlaybackSoundOnly,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub struct TempoBeat {
141 pub beat_numerator: u32,
143 pub beat_denominator: u32,
145 pub bpm: u32,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
150pub struct KeySignatureModel {
151 pub display: String,
152 pub fifths: i8,
153 pub explicit_accidentals: Vec<KeyAccidentalModel>,
154 pub preserve_restatement: bool,
158 pub source_span: Span,
159}
160
161impl KeySignatureModel {
162 pub(crate) fn structurally_matches(&self, other: &Self) -> bool {
163 self.fifths == other.fifths && self.explicit_accidentals == other.explicit_accidentals
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct ClefChangeModel {
169 pub clef: TextLine,
170 pub source_span: Span,
171 pub musicxml_cursor_pre_backup: Option<Fraction>,
172 pub musicxml_cursor_back: Option<Fraction>,
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct KeyAccidentalModel {
177 pub step: char,
178 pub accidental: Accidental,
179 pub source_span: Span,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct AccidentalPolicy {
189 pub preserve_explicit_accidentals: bool,
190 pub reset_at_barlines: bool,
191 pub scope: AccidentalScope,
192 pub source_span: Span,
193}
194
195impl AccidentalPolicy {
196 pub(crate) fn abc_default(span: Span) -> Self {
199 Self {
200 preserve_explicit_accidentals: true,
201 reset_at_barlines: true,
202 scope: AccidentalScope::PitchAndOctave,
203 source_span: span,
204 }
205 }
206}
207
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209pub enum AccidentalScope {
210 PitchAndOctave,
211}
212
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct Part {
215 pub id: PartId,
216 pub name: Option<TextLine>,
217 pub instruments: Vec<MusicXmlPartInstrumentModel>,
218 pub staves: Vec<Staff>,
219 pub voices: Vec<Voice>,
220 pub source_span: Span,
221}
222
223#[derive(Debug, Clone, PartialEq, Eq)]
224pub struct PartId {
225 pub value: String,
226 pub span: Span,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct Staff {
231 pub id: StaffId,
232 pub voices: Vec<VoiceId>,
233 pub source_span: Span,
234}
235
236#[derive(Debug, Clone, Copy, PartialEq, Eq)]
237pub struct StaffId {
238 pub value: u32,
239 pub span: Span,
240}
241
242#[derive(Debug, Clone, PartialEq, Eq)]
243pub struct Voice {
244 pub id: VoiceId,
245 pub staff: StaffId,
246 pub initial_key: Option<KeySignatureModel>,
249 pub initial_meter: Option<MeterModel>,
253 pub initial_properties: VoicePropertiesModel,
254 pub properties: VoicePropertiesModel,
255 pub measures: Vec<Measure>,
256 pub events: Vec<TimedEvent>,
257 pub midi_instrument: Option<MidiInstrumentModel>,
261 pub midi_transpose: Option<i16>,
268 pub source_span: Span,
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub struct MidiInstrumentModel {
282 pub program: Option<u8>,
286 pub channel: Option<u8>,
290 pub volume_cc: Option<u8>,
293 pub pan_cc: Option<u8>,
296 pub midi_unpitched: Option<u8>,
299 pub span: Span,
301}
302
303#[derive(Debug, Clone, PartialEq, Eq)]
304pub struct MusicXmlPartInstrumentModel {
305 pub id: String,
306 pub name: Option<TextLine>,
307 pub midi: Option<MidiInstrumentModel>,
308 pub span: Span,
309}
310
311#[derive(Debug, Clone, PartialEq, Eq)]
312pub struct MusicXmlInstrumentRef {
313 pub id: String,
314 pub span: Span,
315}
316
317impl MidiInstrumentModel {
318 pub fn has_content(&self) -> bool {
321 self.program.is_some()
322 || self.channel.is_some()
323 || self.volume_cc.is_some()
324 || self.pan_cc.is_some()
325 || self.midi_unpitched.is_some()
326 }
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub struct MeasureId {
331 pub index: u32,
332 pub number: u32,
333}
334
335#[derive(Debug, Clone, PartialEq, Eq)]
336pub struct Measure {
337 pub id: MeasureId,
338 pub display_number: Option<String>,
342 pub source_span: Span,
343 pub expected_duration: Option<Rational>,
344 pub actual_duration: Rational,
345 pub multiple_rest: Option<u32>,
349 pub pickup: bool,
350 pub complete: bool,
351 pub barlines: Vec<MeasureBarline>,
352 pub repeat_endings: Vec<RepeatEndingModel>,
353 pub repeat_ending_closes: Vec<RepeatEndingCloseModel>,
357 pub overlays: Vec<OverlaySegment>,
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
361pub struct MeasureBarline {
362 pub kind: BarlineKind,
363 pub span: Span,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq)]
367pub struct RepeatEndingModel {
368 pub span: Span,
369 pub endings: Vec<RepeatEndingPartModel>,
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
373pub struct RepeatEndingCloseModel {
374 pub span: Span,
375 pub close_type: RepeatEndingCloseType,
376 pub location: RepeatEndingCloseLocation,
377 pub endings: Vec<RepeatEndingPartModel>,
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
381pub enum RepeatEndingCloseType {
382 Stop,
383 Discontinue,
384}
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
387pub enum RepeatEndingCloseLocation {
388 Left,
389 Right,
390}
391
392#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum RepeatEndingPartModel {
394 Single(u32),
395 Range { start: u32, end: u32 },
396 Text(String),
397}
398
399#[derive(Debug, Clone, PartialEq, Eq)]
400pub struct TimedEvent {
401 pub measure: MeasureId,
402 pub onset: Rational,
403 pub duration: Rational,
404 pub source: Span,
405 pub kind: TimedEventKind,
406 pub attachments: EventAttachments,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq)]
410pub enum TimedEventKind {
411 Note(NoteEvent),
412 Chord(ChordEvent),
413 Rest(RestEvent),
414 Spacer,
415 Barline(MeasureBarline),
416 RepeatEnding(RepeatEndingModel),
417 RepeatEndingClose(RepeatEndingCloseModel),
418 KeyChange(KeySignatureModel),
422 MeterChange(MeterModel),
424 ClefChange(ClefChangeModel),
428 TempoChange(TempoModel),
430 SectionLabel(String),
434}
435
436#[derive(Debug, Clone, PartialEq, Eq)]
437pub struct NoteEvent {
438 pub pitch: Pitch,
439 pub written_accidental: Option<AccidentalMark>,
440 pub chord_member: bool,
441}
442
443#[derive(Debug, Clone, PartialEq, Eq)]
444pub struct ChordEvent {
445 pub members: Vec<ChordMemberEvent>,
446 pub source_span: Span,
447}
448
449#[derive(Debug, Clone, PartialEq, Eq)]
450pub struct ChordMemberEvent {
451 pub pitch: Pitch,
452 pub duration: Rational,
453 pub written_accidental: Option<AccidentalMark>,
454 pub source_span: Span,
455 pub attachments: EventAttachments,
456}
457
458#[derive(Debug, Clone, PartialEq, Eq)]
459pub struct RestEvent {
460 pub visibility: RestVisibility,
461}
462
463#[derive(Debug, Clone, Copy, PartialEq, Eq)]
464pub struct Pitch {
465 pub step: char,
466 pub alter: i8,
467 pub octave: i8,
468 pub spelling_source: Span,
469}
470
471#[derive(Debug, Clone, Copy, PartialEq, Eq)]
472pub struct AccidentalMark {
473 pub kind: Accidental,
474 pub explicit: bool,
475 pub courtesy: bool,
476 pub source: Span,
477}
478
479#[derive(Debug, Clone, PartialEq, Eq, Default)]
480pub struct EventAttachments {
481 pub grace_groups: Vec<GraceGroupAttachment>,
482 pub after_grace_groups: Vec<GraceGroupAttachment>,
483 pub chord_symbols: Vec<TextAttachment>,
484 pub annotations: Vec<TextAttachment>,
485 pub decorations: Vec<DecorationAttachment>,
486 pub instrument: Option<MusicXmlInstrumentRef>,
487 pub lyrics: Vec<AlignedLyric>,
488 pub lyric_same_note_extends: Vec<u32>,
489 pub lyric_same_note_duplicates: Vec<AlignedLyric>,
490 pub musicxml_forward: bool,
491 pub musicxml_sequence_backup: Option<Fraction>,
492 pub symbols: Vec<AlignedSymbol>,
493 pub ties: Vec<TieAttachment>,
494 pub slurs: Vec<SlurAttachment>,
495 pub tuplets: Vec<TupletAttachment>,
496}
497
498impl EventAttachments {
499 pub fn is_empty(&self) -> bool {
500 self.grace_groups.is_empty()
501 && self.after_grace_groups.is_empty()
502 && self.chord_symbols.is_empty()
503 && self.annotations.is_empty()
504 && self.decorations.is_empty()
505 && self.instrument.is_none()
506 && self.lyrics.is_empty()
507 && self.lyric_same_note_extends.is_empty()
508 && self.lyric_same_note_duplicates.is_empty()
509 && !self.musicxml_forward
510 && self.musicxml_sequence_backup.is_none()
511 && self.symbols.is_empty()
512 && self.ties.is_empty()
513 && self.slurs.is_empty()
514 && self.tuplets.is_empty()
515 }
516
517 pub(crate) fn extend(&mut self, other: EventAttachments) {
518 self.grace_groups.extend(other.grace_groups);
519 self.after_grace_groups.extend(other.after_grace_groups);
520 self.chord_symbols.extend(other.chord_symbols);
521 self.annotations.extend(other.annotations);
522 self.decorations.extend(other.decorations);
523 if self.instrument.is_none() {
524 self.instrument = other.instrument;
525 }
526 self.lyrics.extend(other.lyrics);
527 self.lyric_same_note_extends
528 .extend(other.lyric_same_note_extends);
529 self.lyric_same_note_duplicates
530 .extend(other.lyric_same_note_duplicates);
531 self.musicxml_forward |= other.musicxml_forward;
532 if self.musicxml_sequence_backup.is_none() {
533 self.musicxml_sequence_backup = other.musicxml_sequence_backup;
534 }
535 self.symbols.extend(other.symbols);
536 self.ties.extend(other.ties);
537 self.slurs.extend(other.slurs);
538 self.tuplets.extend(other.tuplets);
539 }
540}
541
542#[derive(Debug, Clone, PartialEq, Eq)]
543pub struct GraceGroupAttachment {
544 pub span: Span,
545 pub slash: Option<Span>,
546 pub note_count: u32,
547 pub events: Vec<GraceEvent>,
548 pub slurs: Vec<SlurAttachment>,
552}
553
554#[derive(Debug, Clone, PartialEq, Eq)]
555pub struct GraceEvent {
556 pub source_span: Span,
557 pub kind: GraceEventKind,
558 pub slurs: Vec<SlurAttachment>,
559}
560
561#[derive(Debug, Clone, PartialEq, Eq)]
562pub enum GraceEventKind {
563 Note(GraceNoteEvent),
564 Rest(RestEvent),
565 Chord(Vec<GraceNoteEvent>),
566}
567
568#[derive(Debug, Clone, PartialEq, Eq)]
569pub struct GraceNoteEvent {
570 pub pitch: Pitch,
571 pub written_accidental: Option<AccidentalMark>,
572 pub decorations: Vec<DecorationAttachment>,
573 pub length_multiplier: Fraction,
576}
577
578#[derive(Debug, Clone, PartialEq, Eq)]
579pub struct TextAttachment {
580 pub text: String,
581 pub span: Span,
582 pub placement: Option<AnnotationPlacementModel>,
583 pub musicxml_harmony_text: HarmonyKindText,
584}
585
586#[derive(Debug, Clone, Default, PartialEq, Eq)]
591pub enum HarmonyKindText {
592 #[default]
595 AbcNative,
596 Textless,
599 Text(String),
601}
602
603#[derive(Debug, Clone, Copy, PartialEq, Eq)]
604pub enum AnnotationPlacementModel {
605 Above,
606 Below,
607 Left,
608 Right,
609 Free,
610}
611
612#[derive(Debug, Clone, PartialEq, Eq)]
613pub struct DecorationAttachment {
614 pub name: String,
615 pub span: Span,
616 pub source_kind: DecorationSourceKind,
617 pub placement: Option<AnnotationPlacementModel>,
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq)]
621pub enum DecorationSourceKind {
622 Named,
623 LegacyNamed,
624 Shorthand,
625 UserDefined,
626}
627
628#[derive(Debug, Clone, Copy, PartialEq, Eq)]
629pub struct TieAttachment {
630 pub pair_id: u32,
631 pub role: TieRole,
632 pub span: Span,
633 pub dotted: bool,
634}
635
636#[derive(Debug, Clone, Copy, PartialEq, Eq)]
637pub enum TieRole {
638 Start,
639 Stop,
640}
641
642#[derive(Debug, Clone, Copy, PartialEq, Eq)]
643pub struct SlurAttachment {
644 pub pair_id: u32,
645 pub role: SlurRole,
646 pub span: Span,
647 pub dotted: bool,
648}
649
650#[derive(Debug, Clone, Copy, PartialEq, Eq)]
651pub enum SlurRole {
652 Start,
653 Stop,
654}
655
656pub(crate) const XVOICE_SLUR_PAIR_ID_BASE: u32 = 1_000_000;
664
665#[derive(Debug, Clone, Copy, PartialEq, Eq)]
666pub struct TupletAttachment {
667 pub pair_id: u32,
668 pub actual_notes: u32,
669 pub normal_notes: u32,
670 pub role: TupletRole,
671 pub span: Span,
672}
673
674#[derive(Debug, Clone, Copy, PartialEq, Eq)]
675pub enum TupletRole {
676 Start,
677 Continue,
678 Stop,
679}
680
681#[derive(Debug, Clone, PartialEq, Eq)]
682pub enum Event {
683 Note {
684 step: char,
685 octave: i8,
686 accidental: Option<Accidental>,
687 chord: bool,
688 duration: u32,
689 span: Span,
690 },
691 Rest {
692 visibility: RestVisibility,
693 duration: u32,
694 span: Span,
695 },
696 Spacer {
697 span: Span,
698 },
699 Barline {
700 kind: BarlineKind,
701 span: Span,
702 },
703}
704
705impl Event {
706 pub fn span(&self) -> Span {
707 match self {
708 Self::Note { span, .. }
709 | Self::Rest { span, .. }
710 | Self::Spacer { span }
711 | Self::Barline { span, .. } => *span,
712 }
713 }
714
715 pub(crate) fn is_time_bearing(&self) -> bool {
716 matches!(self, Self::Note { .. } | Self::Rest { .. })
717 }
718}
719
720#[derive(Debug, Clone, PartialEq, Eq)]
721pub struct VoiceTimeline {
722 pub id: VoiceId,
723 pub initial_key: Option<KeySignatureModel>,
724 pub initial_meter: Option<MeterModel>,
725 pub initial_properties: VoicePropertiesModel,
726 pub properties: VoicePropertiesModel,
727 pub measures: Vec<VoiceMeasureTimeline>,
728 pub musicxml_instruments: Vec<MusicXmlPartInstrumentModel>,
731 pub midi_instrument: Option<MidiInstrumentModel>,
734 pub midi_transpose: Option<i16>,
737 pub source_span: Span,
738}
739
740#[derive(Debug, Clone, PartialEq, Eq)]
741pub struct VoiceId {
742 pub value: String,
743 pub span: Span,
744}
745
746#[derive(Debug, Clone, PartialEq, Eq, Default)]
747pub struct VoicePropertiesModel {
748 pub name: Option<TextLine>,
749 pub nm: Option<TextLine>,
750 pub subname: Option<TextLine>,
751 pub snm: Option<TextLine>,
752 pub clef: Option<TextLine>,
753 pub stem: Option<StemDirectionModel>,
754 pub octave: Option<TextLine>,
755 pub transpose: Option<TextLine>,
756 pub middle: Option<TextLine>,
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq)]
760pub enum StemDirectionModel {
761 Up,
762 Down,
763}
764
765#[derive(Debug, Clone, PartialEq, Eq)]
766pub struct VoiceMeasureTimeline {
767 pub index: u32,
768 pub display_number: Option<String>,
769 pub span: Span,
770 pub events: Vec<VoiceTimedEvent>,
771 pub overlays: Vec<OverlaySegment>,
772}
773
774#[derive(Debug, Clone, PartialEq, Eq)]
775pub struct VoiceTimedEvent {
776 pub onset: Fraction,
777 pub duration: Fraction,
778 pub span: Span,
779 pub line_index: usize,
780 pub source_order: u32,
781 pub alignable: bool,
782 pub kind: TimelineEventKind,
783 pub attachments: EventAttachments,
784 pub lyrics: Vec<AlignedLyric>,
785 pub symbols: Vec<AlignedSymbol>,
786}
787
788#[derive(Debug, Clone, PartialEq, Eq)]
789pub enum TimelineEventKind {
790 Note {
791 step: char,
792 octave: i8,
793 accidental: Option<Accidental>,
794 effective_accidental: Option<Accidental>,
795 accidental_source: Option<Span>,
796 chord: bool,
797 },
798 Rest {
799 visibility: RestVisibility,
800 multiple_rest: Option<u32>,
801 },
802 Spacer,
803 Barline {
804 kind: BarlineKind,
805 },
806 VariantEnding {
807 endings: Vec<RepeatEndingPartModel>,
808 },
809 VariantEndingClose {
810 close_type: RepeatEndingCloseType,
811 location: RepeatEndingCloseLocation,
812 endings: Vec<RepeatEndingPartModel>,
813 },
814 KeyChange(KeySignatureModel),
815 MeterChange(MeterModel),
816 ClefChange(ClefChangeModel),
817 TempoChange(TempoModel),
818 SectionLabel(String),
821}
822
823#[derive(Debug, Clone, PartialEq, Eq)]
824pub struct OverlaySegment {
825 pub id: VoiceId,
826 pub span: Span,
827 pub measure_index: u32,
828 pub expected_duration: Fraction,
829 pub actual_duration: Fraction,
830 pub events: Vec<VoiceTimedEvent>,
831}
832
833#[derive(Debug, Clone, PartialEq, Eq)]
834pub struct AlignedLyric {
835 pub verse: u32,
836 pub text: String,
837 pub span: Span,
838 pub control: LyricControl,
839 pub same_note_extend: bool,
843}
844
845#[derive(Debug, Clone, Copy, PartialEq, Eq)]
846pub enum LyricControl {
847 Syllable,
848 Hyphen,
849 Extender,
850 Skip,
851}
852
853#[derive(Debug, Clone, PartialEq, Eq)]
854pub struct AlignedSymbol {
855 pub layer: u32,
856 pub text: String,
857 pub span: Span,
858 pub kind: AlignedSymbolKind,
859}
860
861#[derive(Debug, Clone, Copy, PartialEq, Eq)]
862pub enum AlignedSymbolKind {
863 Decoration,
864 ChordSymbol,
865 Annotation,
866 Raw,
867}
868
869#[derive(Debug, Clone, PartialEq, Eq)]
870pub struct ScoreDirectiveModel {
871 pub span: Span,
872 pub value: TextLine,
873 pub tokens: Vec<ScoreDirectiveTokenModel>,
874}
875
876#[derive(Debug, Clone, PartialEq, Eq)]
877pub struct ScoreDirectiveTokenModel {
878 pub span: Span,
879 pub kind: ScoreDirectiveTokenKindModel,
880}
881
882#[derive(Debug, Clone, PartialEq, Eq)]
883pub enum ScoreDirectiveTokenKindModel {
884 Voice(String),
885 GroupStart(char),
886 GroupEnd(char),
887 StaffSeparator,
888 MeasureSeparator,
889 FloatingVoiceMarker,
890}
891
892#[derive(Debug, Clone, PartialEq, Eq)]
893pub struct TextLine {
894 pub text: String,
895 pub span: Span,
896}
897
898#[derive(Debug, Clone, Copy, PartialEq, Eq)]
899pub enum RestVisibility {
900 Visible,
901 Invisible,
902}
903
904#[derive(Debug, Clone, Copy, PartialEq, Eq)]
905pub enum Accidental {
906 DoubleFlat,
907 Flat,
908 Natural,
909 Sharp,
910 DoubleSharp,
911}
912
913impl Accidental {
914 pub(crate) fn from_alter(alter: i8) -> Option<Self> {
916 match alter {
917 -2 => Some(Self::DoubleFlat),
918 -1 => Some(Self::Flat),
919 0 => Some(Self::Natural),
920 1 => Some(Self::Sharp),
921 2 => Some(Self::DoubleSharp),
922 _ => None,
923 }
924 }
925
926 pub(crate) fn abc_sign(self) -> &'static str {
928 match self {
929 Self::DoubleFlat => "__",
930 Self::Flat => "_",
931 Self::Natural => "=",
932 Self::Sharp => "^",
933 Self::DoubleSharp => "^^",
934 }
935 }
936
937 pub(crate) fn alter(self) -> i8 {
938 match self {
939 Self::DoubleFlat => -2,
940 Self::Flat => -1,
941 Self::Natural => 0,
942 Self::Sharp => 1,
943 Self::DoubleSharp => 2,
944 }
945 }
946
947 pub(crate) fn musicxml_name(self) -> &'static str {
948 match self {
949 Self::DoubleFlat => "flat-flat",
950 Self::Flat => "flat",
951 Self::Natural => "natural",
952 Self::Sharp => "sharp",
953 Self::DoubleSharp => "double-sharp",
954 }
955 }
956}
957
958#[derive(Debug, Clone, Copy, PartialEq, Eq)]
959pub enum BarlineKind {
960 Regular,
961 Double,
962 Final,
963 Initial,
964 RepeatStart,
965 RepeatEnd,
966 RepeatBoth,
967 Dotted,
968 Dashed,
969 Invisible,
970 Liberal,
971}
972
973#[derive(Debug, Clone, Copy, PartialEq, Eq)]
974pub(crate) struct LoweredEventAtom {
975 pub kind: LoweredEventAtomKind,
976 pub duration: Fraction,
977}
978
979#[derive(Debug, Clone, Copy, PartialEq, Eq)]
980pub(crate) enum LoweredEventAtomKind {
981 Note {
982 step: char,
983 octave: i8,
984 accidental: Option<Accidental>,
985 effective_accidental: Option<Accidental>,
986 accidental_source: Option<Span>,
987 chord: bool,
988 span: Span,
989 },
990 Rest {
991 visibility: RestVisibility,
992 multiple_rest: Option<u32>,
993 span: Span,
994 },
995 Spacer {
996 span: Span,
997 },
998}
999
1000impl LoweredEventAtom {
1001 pub(crate) fn span(&self) -> Span {
1002 match self.kind {
1003 LoweredEventAtomKind::Note { span, .. }
1004 | LoweredEventAtomKind::Rest { span, .. }
1005 | LoweredEventAtomKind::Spacer { span } => span,
1006 }
1007 }
1008
1009 pub(crate) fn into_event(self, divisions: u32) -> Event {
1010 let duration = self.duration.to_divisions(divisions);
1011 match self.kind {
1012 LoweredEventAtomKind::Note {
1013 step,
1014 octave,
1015 accidental,
1016 effective_accidental: _,
1017 accidental_source: _,
1018 chord,
1019 span,
1020 } => Event::Note {
1021 step,
1022 octave,
1023 accidental,
1024 chord,
1025 duration,
1026 span,
1027 },
1028 LoweredEventAtomKind::Rest {
1029 visibility, span, ..
1030 } => Event::Rest {
1031 visibility,
1032 duration,
1033 span,
1034 },
1035 LoweredEventAtomKind::Spacer { span } => Event::Spacer { span },
1036 }
1037 }
1038}
1039
1040#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1041pub struct Fraction {
1042 pub numerator: u32,
1043 pub denominator: u32,
1044}
1045
1046impl Fraction {
1047 pub fn zero() -> Self {
1048 Self {
1049 numerator: 0,
1050 denominator: 1,
1051 }
1052 }
1053
1054 pub fn new(numerator: u32, denominator: u32) -> Self {
1055 let denominator = denominator.max(1);
1056 let gcd = gcd(numerator, denominator);
1057 Self {
1058 numerator: numerator / gcd,
1059 denominator: denominator / gcd,
1060 }
1061 }
1062
1063 pub fn one() -> Self {
1064 Self {
1065 numerator: 1,
1066 denominator: 1,
1067 }
1068 }
1069
1070 pub(crate) const MAX: Self = Self {
1072 numerator: u32::MAX,
1073 denominator: 1,
1074 };
1075
1076 pub(crate) fn checked_mul(self, other: Self) -> Option<Self> {
1078 Self::reduced_wide(
1079 u128::from(self.numerator) * u128::from(other.numerator),
1080 u128::from(self.denominator) * u128::from(other.denominator),
1081 )
1082 }
1083
1084 pub(crate) fn checked_add(self, other: Self) -> Option<Self> {
1086 Self::reduced_wide(
1087 u128::from(self.numerator) * u128::from(other.denominator)
1088 + u128::from(other.numerator) * u128::from(self.denominator),
1089 u128::from(self.denominator) * u128::from(other.denominator),
1090 )
1091 }
1092
1093 pub(crate) fn saturating_add(self, other: Self) -> Self {
1097 self.checked_add(other).unwrap_or(Self::MAX)
1098 }
1099
1100 fn reduced_wide(numerator: u128, denominator: u128) -> Option<Self> {
1101 let denominator = denominator.max(1);
1102 let divisor = gcd_u128(numerator, denominator);
1103 Some(Self {
1104 numerator: u32::try_from(numerator / divisor).ok()?,
1105 denominator: u32::try_from(denominator / divisor).ok()?,
1106 })
1107 }
1108
1109 pub(crate) fn dotted(self, dots: usize) -> Self {
1112 let mut duration = self;
1113 let mut dot = self;
1114 for _ in 0..dots {
1115 dot = Self::new(dot.numerator, dot.denominator.saturating_mul(2));
1116 duration = duration.saturating_add(dot);
1117 }
1118 duration
1119 }
1120
1121 pub(crate) fn less_than(self, other: Self) -> bool {
1122 u64::from(self.numerator) * u64::from(other.denominator)
1123 < u64::from(other.numerator) * u64::from(self.denominator)
1124 }
1125
1126 pub(crate) fn divisions_requirement(self) -> u32 {
1127 let denominator = u64::from(self.denominator);
1128 let scaled_numerator = u64::from(self.numerator) * 4;
1129 let gcd = gcd_u64(denominator, scaled_numerator);
1130 u32::try_from(denominator / gcd).unwrap_or(u32::MAX)
1131 }
1132
1133 pub(crate) fn to_divisions(self, divisions: u32) -> u32 {
1134 let numerator = u128::from(self.numerator) * 4 * u128::from(divisions);
1136 let denominator = u128::from(self.denominator.max(1));
1137 let value = numerator / denominator;
1138 u32::try_from(value.max(1)).unwrap_or(u32::MAX)
1139 }
1140}
1141
1142pub(crate) const SHARP_ORDER: [char; 7] = ['F', 'C', 'G', 'D', 'A', 'E', 'B'];
1144pub(crate) const FLAT_ORDER: [char; 7] = ['B', 'E', 'A', 'D', 'G', 'C', 'F'];
1145
1146pub(crate) fn checked_lcm(left: u32, right: u32) -> Option<u32> {
1148 if left == 0 || right == 0 {
1149 return Some(left.max(right).max(1));
1150 }
1151 (left / gcd(left, right)).checked_mul(right)
1152}
1153
1154pub(crate) fn gcd(mut left: u32, mut right: u32) -> u32 {
1155 while right != 0 {
1156 let remainder = left % right;
1157 left = right;
1158 right = remainder;
1159 }
1160 left.max(1)
1161}
1162
1163pub(crate) fn gcd_u64(mut left: u64, mut right: u64) -> u64 {
1164 while right != 0 {
1165 let remainder = left % right;
1166 left = right;
1167 right = remainder;
1168 }
1169 left.max(1)
1170}
1171
1172pub(crate) fn checked_ratio_product(
1176 actual: u32,
1177 normal: u32,
1178 factor_actual: u32,
1179 factor_normal: u32,
1180) -> Option<(u32, u32)> {
1181 let actual = u64::from(actual) * u64::from(factor_actual);
1182 let normal = u64::from(normal) * u64::from(factor_normal);
1183 ratio_to_u32(actual, normal)
1184}
1185
1186pub(crate) fn ratio_to_u32(numerator: u64, denominator: u64) -> Option<(u32, u32)> {
1189 if numerator <= u64::from(u32::MAX) && denominator <= u64::from(u32::MAX) {
1190 return Some((numerator as u32, denominator as u32));
1191 }
1192 let gcd = gcd_u64(numerator, denominator);
1193 let numerator = numerator / gcd;
1194 let denominator = denominator / gcd;
1195 (numerator <= u64::from(u32::MAX) && denominator <= u64::from(u32::MAX))
1196 .then_some((numerator as u32, denominator as u32))
1197}
1198
1199fn gcd_u128(mut left: u128, mut right: u128) -> u128 {
1200 while right != 0 {
1201 let remainder = left % right;
1202 left = right;
1203 right = remainder;
1204 }
1205 left.max(1)
1206}
1207
1208#[cfg(test)]
1209#[path = "model_tests.rs"]
1210mod tests;