1use super::score::NoteAddr;
2use serde::{Deserialize, Serialize};
3
4use super::pitch::{Pitch, Step};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub enum Clef {
8 Treble,
9 Bass,
10 Alto,
11 Tenor,
12 Percussion,
13}
14
15impl Clef {
16 pub fn to_musicxml_sign(&self) -> &'static str {
17 match self {
18 Clef::Treble => "G",
19 Clef::Bass => "F",
20 Clef::Alto => "C",
21 Clef::Tenor => "C",
22 Clef::Percussion => "percussion",
23 }
24 }
25
26 pub fn musicxml_line(&self) -> u8 {
27 match self {
28 Clef::Treble => 2,
29 Clef::Bass => 4,
30 Clef::Alto => 3,
31 Clef::Tenor => 4,
32 Clef::Percussion => 2,
33 }
34 }
35
36 pub fn middle_line_midi(&self) -> u8 {
38 match self {
39 Clef::Treble => 71, Clef::Bass => 50, Clef::Alto => 60, Clef::Tenor => 57, Clef::Percussion => 71, }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49pub struct KeySignature {
50 pub fifths: i8,
52 pub mode: String,
54}
55
56impl Default for KeySignature {
57 fn default() -> Self {
58 Self {
59 fifths: 0,
60 mode: "major".to_string(),
61 }
62 }
63}
64
65impl KeySignature {
66 const SHARP_ORDER: [Step; 7] = [
68 Step::F,
69 Step::C,
70 Step::G,
71 Step::D,
72 Step::A,
73 Step::E,
74 Step::B,
75 ];
76 const FLAT_ORDER: [Step; 7] = [
78 Step::B,
79 Step::E,
80 Step::A,
81 Step::D,
82 Step::G,
83 Step::C,
84 Step::F,
85 ];
86
87 pub fn alter_for_step(&self, step: &Step) -> i8 {
89 if self.fifths > 0 {
90 let count = self.fifths.min(7) as usize;
91 if Self::SHARP_ORDER[..count].contains(step) {
92 1
93 } else {
94 0
95 }
96 } else if self.fifths < 0 {
97 let count = (-self.fifths).min(7) as usize;
98 if Self::FLAT_ORDER[..count].contains(step) {
99 -1
100 } else {
101 0
102 }
103 } else {
104 0
105 }
106 }
107
108 pub fn contains_pitch(&self, pitch: &Pitch) -> bool {
110 pitch.alter == self.alter_for_step(&pitch.step)
111 }
112
113 pub fn tonic(&self) -> (Step, i8) {
117 if self.mode == "minor" {
118 let (maj_step, maj_alter) = Self::major_tonic_from_fifths(self.fifths);
119 let major_midi = Pitch::with_alter(maj_step, 4, maj_alter).to_midi();
121 let minor_midi = (major_midi - 3).clamp(0, 127) as u8;
122 let p = Pitch::from_midi(minor_midi, self.fifths < 0);
123 (p.step, p.alter)
124 } else {
125 Self::major_tonic_from_fifths(self.fifths)
126 }
127 }
128
129 pub fn display_name(&self) -> String {
131 let (step, alter) = self.tonic();
132 let acc = match alter {
133 1 => "#",
134 -1 => "b",
135 _ => "",
136 };
137 format!("{}{} {}", step.to_char(), acc, self.mode)
138 }
139
140 fn major_tonic_from_fifths(fifths: i8) -> (Step, i8) {
141 const TONICS: [(Step, i8); 15] = [
144 (Step::C, -1), (Step::G, -1), (Step::D, -1), (Step::A, -1), (Step::E, -1), (Step::B, -1), (Step::F, 0), (Step::C, 0), (Step::G, 0), (Step::D, 0), (Step::A, 0), (Step::E, 0), (Step::B, 0), (Step::F, 1), (Step::C, 1), ];
160 let idx = (fifths.clamp(-7, 7) + 7) as usize;
161 TONICS[idx].clone()
162 }
163}
164
165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
166pub struct TimeSignature {
167 pub numerator: u8,
168 pub denominator: u8,
169}
170
171impl Default for TimeSignature {
172 fn default() -> Self {
173 Self {
174 numerator: 4,
175 denominator: 4,
176 }
177 }
178}
179
180impl TimeSignature {
181 pub fn beats_per_measure(&self) -> f64 {
182 self.numerator as f64
183 }
184
185 pub fn beat_unit_beats(&self) -> f64 {
186 4.0 / self.denominator as f64
187 }
188
189 pub fn total_beats(&self) -> f64 {
190 self.beats_per_measure() * self.beat_unit_beats()
191 }
192}
193
194#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub enum Dynamic {
196 Pppp,
197 Ppp,
198 Pp,
199 P,
200 Mp,
201 Mf,
202 F,
203 Ff,
204 Fff,
205 Ffff,
206 Sfz,
207 Rfz,
208 Fz,
209 Sf,
210}
211
212impl Dynamic {
213 pub fn to_musicxml_str(&self) -> &'static str {
214 match self {
215 Dynamic::Pppp => "pppp",
216 Dynamic::Ppp => "ppp",
217 Dynamic::Pp => "pp",
218 Dynamic::P => "p",
219 Dynamic::Mp => "mp",
220 Dynamic::Mf => "mf",
221 Dynamic::F => "f",
222 Dynamic::Ff => "ff",
223 Dynamic::Fff => "fff",
224 Dynamic::Ffff => "ffff",
225 Dynamic::Sfz => "sfz",
226 Dynamic::Rfz => "rfz",
227 Dynamic::Fz => "fz",
228 Dynamic::Sf => "sf",
229 }
230 }
231
232 pub fn to_velocity(&self) -> u8 {
233 match self {
234 Dynamic::Pppp => 16,
235 Dynamic::Ppp => 24,
236 Dynamic::Pp => 36,
237 Dynamic::P => 48,
238 Dynamic::Mp => 60,
239 Dynamic::Mf => 72,
240 Dynamic::F => 84,
241 Dynamic::Ff => 96,
242 Dynamic::Fff => 108,
243 Dynamic::Ffff => 120,
244 Dynamic::Sfz => 112,
245 Dynamic::Rfz => 104,
246 Dynamic::Fz => 100,
247 Dynamic::Sf => 96,
248 }
249 }
250}
251
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub enum Articulation {
254 Staccato,
255 Staccatissimo,
256 Accent,
257 Tenuto,
258 Marcato,
259 Fermata,
260 Trill,
261 Mordent,
262 InvertedMordent,
263 Turn,
264 InvertedTurn,
265 Shake,
266 Tremolo(u8),
267 BreathMark,
268 Caesura,
269}
270
271#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
273#[serde(rename_all = "kebab-case")]
274pub enum GuitarTechnique {
275 Bend,
276 Slide,
277 HammerOn,
278 PullOff,
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
283pub enum FingeringSelectionPolicy {
284 #[default]
286 SourceOrder,
287 LowestNumber,
289 HighestNumber,
291}
292
293#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
294pub enum Barline {
295 #[default]
296 Normal,
297 Double,
298 Final,
299 RepeatStart,
300 RepeatEnd,
301 RepeatBoth,
302 Dashed,
303 Dotted,
304 Invisible,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
308pub enum HairpinKind {
309 Crescendo,
310 Decrescendo,
311}
312
313#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
314pub struct TupletInfo {
315 pub actual_notes: u8,
317 pub normal_notes: u8,
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
322pub enum BeamState {
323 #[default]
324 None,
325 Begin,
326 Continue,
327 End,
328 BeginEnd,
329 BackwardHook,
330 ForwardHook,
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
335pub enum OttavaKind {
336 Va8,
338 Vb8,
340 Ma15,
342 Mb15,
344}
345
346impl OttavaKind {
347 pub fn musicxml_type(&self) -> &'static str {
348 match self {
349 OttavaKind::Va8 | OttavaKind::Ma15 => "up",
350 OttavaKind::Vb8 | OttavaKind::Mb15 => "down",
351 }
352 }
353
354 pub fn musicxml_size(&self) -> u8 {
355 match self {
356 OttavaKind::Va8 | OttavaKind::Vb8 => 8,
357 OttavaKind::Ma15 | OttavaKind::Mb15 => 15,
358 }
359 }
360}
361
362#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
364pub enum NoteHead {
365 #[default]
366 Normal,
367 Diamond, X, Slash, Cross, Triangle, }
373
374#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
376pub struct Lyric {
377 pub text: String,
379 pub syllabic: String,
381}
382
383#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
386pub struct StyledText {
387 pub style: TextStyle,
388 pub text: String,
389 #[serde(default)]
391 pub placement: Option<String>,
392 #[serde(default)]
394 pub offset_x: Option<f64>,
395 #[serde(default)]
397 pub offset_y: Option<f64>,
398 #[serde(default)]
400 pub relative_x: Option<f64>,
401 #[serde(default)]
403 pub relative_y: Option<f64>,
404}
405
406#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
407pub enum TextStyle {
408 Expression,
409 Technique,
410 Lyrics,
411 ChordSymbol,
412 FiguredBass,
413 RehearsalMark,
414 Generic,
415}
416
417#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
420pub struct CrossStaff {
421 pub target_staff: usize,
422 #[serde(default)]
423 pub target_voice: Option<usize>,
424}
425
426#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
428pub struct TabPosition {
429 pub string: u8,
431 pub fret: u8,
432}
433
434#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
436pub struct TablatureConfig {
437 pub lines: u8,
438 pub tuning_midi: Vec<i16>,
439 #[serde(default)]
440 pub capo: u8,
441}
442
443#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
445pub struct ChordSymbol {
446 pub root: String,
448 pub kind: String,
450 pub bass: Option<String>,
452 #[serde(default)]
454 pub placement: Option<String>,
455 #[serde(default)]
457 pub extender: bool,
458 #[serde(default)]
460 pub harmonic_degree: Option<String>,
461 #[serde(default)]
463 pub harmony_function: Option<String>,
464 #[serde(default)]
466 pub harmony_type: Option<String>,
467 #[serde(default)]
469 pub chord_ref: Option<String>,
470 #[serde(default)]
476 pub range_end: Option<NoteAddr>,
477 #[serde(default)]
479 pub degrees: Vec<ChordDegree>,
480}
481
482#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
488pub struct ChordDefinition {
489 #[serde(default)]
490 pub id: Option<String>,
491 #[serde(default)]
492 pub label: Option<String>,
493 #[serde(default)]
494 pub kind: Option<String>,
495 #[serde(default)]
496 pub fret_position: Option<u32>,
497 #[serde(default)]
498 pub tab_strings: Option<String>,
499 #[serde(default)]
500 pub tab_courses: Option<String>,
501 #[serde(default)]
502 pub members: Vec<ChordDefinitionMember>,
503 #[serde(default)]
504 pub barres: Vec<ChordBarre>,
505}
506
507#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
509pub struct ChordDefinitionMember {
510 #[serde(default)]
511 pub id: Option<String>,
512 #[serde(default)]
513 pub pitch: Option<Pitch>,
514 #[serde(default)]
515 pub tab_string: Option<u8>,
516 #[serde(default)]
517 pub tab_course: Option<u8>,
518 #[serde(default)]
519 pub tab_fret: Option<u16>,
520 #[serde(default)]
521 pub fingering: Option<u8>,
522}
523
524#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
526pub struct ChordBarre {
527 #[serde(default)]
528 pub start_member: Option<String>,
529 #[serde(default)]
530 pub end_member: Option<String>,
531 #[serde(default)]
532 pub fret: Option<u16>,
533 #[serde(default)]
534 pub label: Option<String>,
535 #[serde(default)]
536 pub kind: Option<String>,
537}
538
539#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
541pub struct ChordDegree {
542 pub value: u8,
544 pub alter: i8,
546 pub kind: String,
548}
549
550#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
552pub struct FiguredBassFigure {
553 pub number: String,
555 #[serde(default)]
557 pub alter: Option<String>,
558 #[serde(default)]
560 pub prefix: Option<String>,
561 #[serde(default)]
563 pub suffix: Option<String>,
564 #[serde(default)]
566 pub extender: bool,
567}
568
569impl ChordDegree {
570 pub fn display_text(&self) -> String {
572 let accidental = match self.alter {
573 -2 => "bb",
574 -1 => "b",
575 1 => "#",
576 2 => "##",
577 _ => "",
578 };
579 match self.kind.as_str() {
580 "subtract" => format!("no{}{}", accidental, self.value),
581 "alter" => format!("{}{}", accidental, self.value),
582 _ => format!("add{}{}", accidental, self.value),
583 }
584 }
585}
586
587impl ChordSymbol {
588 pub fn display_text(&self) -> String {
589 let kind_str = match self.kind.as_str() {
590 "major" | "" => "",
591 "minor" => "m",
592 "dominant" => "7",
593 "major-seventh" => "maj7",
594 "minor-seventh" => "m7",
595 "diminished" => "dim",
596 "diminished-seventh" => "dim7",
597 "augmented" => "aug",
598 "suspended-second" => "sus2",
599 "suspended-fourth" => "sus4",
600 "half-diminished" => "m7b5",
601 "major-sixth" => "6",
602 "minor-sixth" => "m6",
603 "power" => "5",
604 "major-add9" => "add9",
605 "minor-add9" => "madd9",
606 "minor-major-seventh" => "mMaj7",
607 "dominant-flat-five" => "7b5",
608 "dominant-sharp-five" => "7#5",
609 "dominant-ninth" => "9",
610 "major-ninth" => "maj9",
611 "minor-ninth" => "m9",
612 other => other,
613 };
614 let bass_str = match &self.bass {
615 Some(b) => format!("/{}", b),
616 None => String::new(),
617 };
618 let degree_str = self
619 .degrees
620 .iter()
621 .map(ChordDegree::display_text)
622 .collect::<String>();
623 format!("{}{}{}{}", self.root, kind_str, degree_str, bass_str)
624 }
625}
626
627#[cfg(test)]
628mod tests {
629 use super::*;
630
631 #[test]
632 fn key_alter_c_major_all_natural() {
633 let key = KeySignature {
634 fifths: 0,
635 mode: "major".into(),
636 };
637 for step in [
638 Step::C,
639 Step::D,
640 Step::E,
641 Step::F,
642 Step::G,
643 Step::A,
644 Step::B,
645 ] {
646 assert_eq!(key.alter_for_step(&step), 0, "step {:?}", step);
647 }
648 }
649
650 #[test]
651 fn key_alter_g_major_fsharp() {
652 let key = KeySignature {
653 fifths: 1,
654 mode: "major".into(),
655 };
656 assert_eq!(key.alter_for_step(&Step::F), 1);
657 assert_eq!(key.alter_for_step(&Step::G), 0);
658 }
659
660 #[test]
661 fn key_alter_f_major_bflat() {
662 let key = KeySignature {
663 fifths: -1,
664 mode: "major".into(),
665 };
666 assert_eq!(key.alter_for_step(&Step::B), -1);
667 assert_eq!(key.alter_for_step(&Step::C), 0);
668 }
669
670 #[test]
671 fn key_alter_bb_major() {
672 let key = KeySignature {
673 fifths: -2,
674 mode: "major".into(),
675 };
676 assert_eq!(key.alter_for_step(&Step::B), -1);
677 assert_eq!(key.alter_for_step(&Step::E), -1);
678 assert_eq!(key.alter_for_step(&Step::A), 0);
679 }
680
681 #[test]
682 fn key_contains_pitch_g_major() {
683 let key = KeySignature {
684 fifths: 1,
685 mode: "major".into(),
686 };
687 assert!(key.contains_pitch(&Pitch::new(Step::G, 4)));
689 assert!(key.contains_pitch(&Pitch::new(Step::D, 4)));
690 assert!(key.contains_pitch(&Pitch::with_alter(Step::F, 4, 1))); assert!(!key.contains_pitch(&Pitch::new(Step::F, 4)));
693 }
694
695 #[test]
696 fn key_display_name_c_major() {
697 let key = KeySignature {
698 fifths: 0,
699 mode: "major".into(),
700 };
701 assert_eq!(key.display_name(), "C major");
702 }
703
704 #[test]
705 fn key_display_name_bb_major() {
706 let key = KeySignature {
707 fifths: -2,
708 mode: "major".into(),
709 };
710 assert_eq!(key.display_name(), "Bb major");
711 }
712
713 #[test]
714 fn key_display_name_fsharp_minor() {
715 let key = KeySignature {
716 fifths: 3,
717 mode: "minor".into(),
718 };
719 assert_eq!(key.display_name(), "F# minor");
720 }
721
722 #[test]
723 fn key_tonic_d_major() {
724 let key = KeySignature {
725 fifths: 2,
726 mode: "major".into(),
727 };
728 let (step, alter) = key.tonic();
729 assert_eq!(step, Step::D);
730 assert_eq!(alter, 0);
731 }
732
733 #[test]
734 fn key_tonic_a_minor() {
735 let key = KeySignature {
737 fifths: 0,
738 mode: "minor".into(),
739 };
740 let (step, alter) = key.tonic();
741 assert_eq!(step, Step::A);
742 assert_eq!(alter, 0);
743 }
744
745 #[test]
746 fn chord_display_major() {
747 let c = ChordSymbol {
748 root: "C".into(),
749 kind: "major".into(),
750 bass: None,
751 placement: None,
752 extender: false,
753 harmonic_degree: None,
754 harmony_function: None,
755 harmony_type: None,
756 chord_ref: None,
757 range_end: None,
758 degrees: Vec::new(),
759 };
760 assert_eq!(c.display_text(), "C");
761 }
762
763 #[test]
764 fn chord_display_minor_seventh_slash() {
765 let c = ChordSymbol {
766 root: "D".into(),
767 kind: "minor-seventh".into(),
768 bass: Some("F".into()),
769 placement: None,
770 extender: false,
771 harmonic_degree: None,
772 harmony_function: None,
773 harmony_type: None,
774 chord_ref: None,
775 range_end: None,
776 degrees: Vec::new(),
777 };
778 assert_eq!(c.display_text(), "Dm7/F");
779 }
780
781 #[test]
782 fn chord_display_structured_degrees() {
783 let c = ChordSymbol {
784 root: "C".into(),
785 kind: "dominant".into(),
786 bass: None,
787 placement: None,
788 extender: false,
789 harmonic_degree: None,
790 harmony_function: None,
791 harmony_type: None,
792 chord_ref: None,
793 range_end: None,
794 degrees: vec![
795 ChordDegree {
796 value: 9,
797 alter: 1,
798 kind: "add".into(),
799 },
800 ChordDegree {
801 value: 5,
802 alter: -1,
803 kind: "alter".into(),
804 },
805 ChordDegree {
806 value: 3,
807 alter: 0,
808 kind: "subtract".into(),
809 },
810 ],
811 };
812 assert_eq!(c.display_text(), "C7add#9b5no3");
813 }
814
815 #[test]
816 fn chord_symbol_legacy_json_defaults_degrees() {
817 let chord: ChordSymbol =
818 serde_json::from_str(r#"{"root":"C","kind":"major","bass":null,"placement":null}"#)
819 .expect("legacy chord symbol JSON deserializes");
820 assert!(chord.degrees.is_empty());
821 assert!(!chord.extender);
822 assert!(chord.harmonic_degree.is_none());
823 assert!(chord.harmony_function.is_none());
824 assert!(chord.harmony_type.is_none());
825 }
826
827 #[test]
828 fn time_sig_total_beats_three_four() {
829 let ts = TimeSignature {
830 numerator: 3,
831 denominator: 4,
832 };
833 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
834 }
835
836 #[test]
837 fn time_sig_total_beats_six_eight() {
838 let ts = TimeSignature {
839 numerator: 6,
840 denominator: 8,
841 };
842 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
843 }
844
845 #[test]
846 fn clef_treble_middle_b4() {
847 assert_eq!(Clef::Treble.middle_line_midi(), 71);
848 }
849
850 #[test]
851 fn clef_bass_middle_d3() {
852 assert_eq!(Clef::Bass.middle_line_midi(), 50);
853 }
854
855 #[test]
856 fn clef_alto_middle_c4() {
857 assert_eq!(Clef::Alto.middle_line_midi(), 60);
858 }
859
860 #[test]
861 fn clef_tenor_middle_a3() {
862 assert_eq!(Clef::Tenor.middle_line_midi(), 57);
863 }
864}