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)]
385pub struct VerseLyric {
386 pub verse: u8,
388 pub lyric: Lyric,
389}
390
391impl VerseLyric {
392 pub const MAX_VERSE: u8 = 32;
394}
395
396#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
399pub struct StyledText {
400 pub style: TextStyle,
401 pub text: String,
402 #[serde(default)]
404 pub placement: Option<String>,
405 #[serde(default)]
407 pub offset_x: Option<f64>,
408 #[serde(default)]
410 pub offset_y: Option<f64>,
411 #[serde(default)]
413 pub relative_x: Option<f64>,
414 #[serde(default)]
416 pub relative_y: Option<f64>,
417}
418
419#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
420pub enum TextStyle {
421 Expression,
422 Technique,
423 Lyrics,
424 ChordSymbol,
425 FiguredBass,
426 RehearsalMark,
427 Generic,
428}
429
430#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
433pub struct CrossStaff {
434 pub target_staff: usize,
435 #[serde(default)]
436 pub target_voice: Option<usize>,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
441pub struct TabPosition {
442 pub string: u8,
444 pub fret: u8,
445}
446
447#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
449pub struct TablatureConfig {
450 pub lines: u8,
451 pub tuning_midi: Vec<i16>,
452 #[serde(default)]
453 pub capo: u8,
454}
455
456#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
458pub struct ChordSymbol {
459 pub root: String,
461 pub kind: String,
463 pub bass: Option<String>,
465 #[serde(default)]
467 pub placement: Option<String>,
468 #[serde(default)]
470 pub extender: bool,
471 #[serde(default)]
473 pub harmonic_degree: Option<String>,
474 #[serde(default)]
476 pub harmony_function: Option<String>,
477 #[serde(default)]
479 pub harmony_type: Option<String>,
480 #[serde(default)]
482 pub chord_ref: Option<String>,
483 #[serde(default)]
489 pub range_end: Option<NoteAddr>,
490 #[serde(default)]
492 pub degrees: Vec<ChordDegree>,
493}
494
495#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
501pub struct ChordDefinition {
502 #[serde(default)]
503 pub id: Option<String>,
504 #[serde(default)]
505 pub label: Option<String>,
506 #[serde(default)]
507 pub kind: Option<String>,
508 #[serde(default)]
509 pub fret_position: Option<u32>,
510 #[serde(default)]
511 pub tab_strings: Option<String>,
512 #[serde(default)]
513 pub tab_courses: Option<String>,
514 #[serde(default)]
515 pub members: Vec<ChordDefinitionMember>,
516 #[serde(default)]
517 pub barres: Vec<ChordBarre>,
518}
519
520#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
522pub struct ChordDefinitionMember {
523 #[serde(default)]
524 pub id: Option<String>,
525 #[serde(default)]
526 pub pitch: Option<Pitch>,
527 #[serde(default)]
528 pub tab_string: Option<u8>,
529 #[serde(default)]
530 pub tab_course: Option<u8>,
531 #[serde(default)]
532 pub tab_fret: Option<u16>,
533 #[serde(default)]
534 pub fingering: Option<u8>,
535}
536
537#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
539pub struct ChordBarre {
540 #[serde(default)]
541 pub start_member: Option<String>,
542 #[serde(default)]
543 pub end_member: Option<String>,
544 #[serde(default)]
545 pub fret: Option<u16>,
546 #[serde(default)]
547 pub label: Option<String>,
548 #[serde(default)]
549 pub kind: Option<String>,
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
554pub struct ChordDegree {
555 pub value: u8,
557 pub alter: i8,
559 pub kind: String,
561}
562
563#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
565pub struct FiguredBassFigure {
566 pub number: String,
568 #[serde(default)]
570 pub alter: Option<String>,
571 #[serde(default)]
573 pub prefix: Option<String>,
574 #[serde(default)]
576 pub suffix: Option<String>,
577 #[serde(default)]
579 pub extender: bool,
580}
581
582impl ChordDegree {
583 pub fn display_text(&self) -> String {
585 let accidental = match self.alter {
586 -2 => "bb",
587 -1 => "b",
588 1 => "#",
589 2 => "##",
590 _ => "",
591 };
592 match self.kind.as_str() {
593 "subtract" => format!("no{}{}", accidental, self.value),
594 "alter" => format!("{}{}", accidental, self.value),
595 _ => format!("add{}{}", accidental, self.value),
596 }
597 }
598}
599
600impl ChordSymbol {
601 pub fn display_text(&self) -> String {
602 let kind_str = match self.kind.as_str() {
603 "major" | "" => "",
604 "minor" => "m",
605 "dominant" => "7",
606 "major-seventh" => "maj7",
607 "minor-seventh" => "m7",
608 "diminished" => "dim",
609 "diminished-seventh" => "dim7",
610 "augmented" => "aug",
611 "suspended-second" => "sus2",
612 "suspended-fourth" => "sus4",
613 "half-diminished" => "m7b5",
614 "major-sixth" => "6",
615 "minor-sixth" => "m6",
616 "power" => "5",
617 "major-add9" => "add9",
618 "minor-add9" => "madd9",
619 "minor-major-seventh" => "mMaj7",
620 "dominant-flat-five" => "7b5",
621 "dominant-sharp-five" => "7#5",
622 "dominant-ninth" => "9",
623 "major-ninth" => "maj9",
624 "minor-ninth" => "m9",
625 other => other,
626 };
627 let bass_str = match &self.bass {
628 Some(b) => format!("/{}", b),
629 None => String::new(),
630 };
631 let degree_str = self
632 .degrees
633 .iter()
634 .map(ChordDegree::display_text)
635 .collect::<String>();
636 format!("{}{}{}{}", self.root, kind_str, degree_str, bass_str)
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[test]
645 fn key_alter_c_major_all_natural() {
646 let key = KeySignature {
647 fifths: 0,
648 mode: "major".into(),
649 };
650 for step in [
651 Step::C,
652 Step::D,
653 Step::E,
654 Step::F,
655 Step::G,
656 Step::A,
657 Step::B,
658 ] {
659 assert_eq!(key.alter_for_step(&step), 0, "step {:?}", step);
660 }
661 }
662
663 #[test]
664 fn key_alter_g_major_fsharp() {
665 let key = KeySignature {
666 fifths: 1,
667 mode: "major".into(),
668 };
669 assert_eq!(key.alter_for_step(&Step::F), 1);
670 assert_eq!(key.alter_for_step(&Step::G), 0);
671 }
672
673 #[test]
674 fn key_alter_f_major_bflat() {
675 let key = KeySignature {
676 fifths: -1,
677 mode: "major".into(),
678 };
679 assert_eq!(key.alter_for_step(&Step::B), -1);
680 assert_eq!(key.alter_for_step(&Step::C), 0);
681 }
682
683 #[test]
684 fn key_alter_bb_major() {
685 let key = KeySignature {
686 fifths: -2,
687 mode: "major".into(),
688 };
689 assert_eq!(key.alter_for_step(&Step::B), -1);
690 assert_eq!(key.alter_for_step(&Step::E), -1);
691 assert_eq!(key.alter_for_step(&Step::A), 0);
692 }
693
694 #[test]
695 fn key_contains_pitch_g_major() {
696 let key = KeySignature {
697 fifths: 1,
698 mode: "major".into(),
699 };
700 assert!(key.contains_pitch(&Pitch::new(Step::G, 4)));
702 assert!(key.contains_pitch(&Pitch::new(Step::D, 4)));
703 assert!(key.contains_pitch(&Pitch::with_alter(Step::F, 4, 1))); assert!(!key.contains_pitch(&Pitch::new(Step::F, 4)));
706 }
707
708 #[test]
709 fn key_display_name_c_major() {
710 let key = KeySignature {
711 fifths: 0,
712 mode: "major".into(),
713 };
714 assert_eq!(key.display_name(), "C major");
715 }
716
717 #[test]
718 fn key_display_name_bb_major() {
719 let key = KeySignature {
720 fifths: -2,
721 mode: "major".into(),
722 };
723 assert_eq!(key.display_name(), "Bb major");
724 }
725
726 #[test]
727 fn key_display_name_fsharp_minor() {
728 let key = KeySignature {
729 fifths: 3,
730 mode: "minor".into(),
731 };
732 assert_eq!(key.display_name(), "F# minor");
733 }
734
735 #[test]
736 fn key_tonic_d_major() {
737 let key = KeySignature {
738 fifths: 2,
739 mode: "major".into(),
740 };
741 let (step, alter) = key.tonic();
742 assert_eq!(step, Step::D);
743 assert_eq!(alter, 0);
744 }
745
746 #[test]
747 fn key_tonic_a_minor() {
748 let key = KeySignature {
750 fifths: 0,
751 mode: "minor".into(),
752 };
753 let (step, alter) = key.tonic();
754 assert_eq!(step, Step::A);
755 assert_eq!(alter, 0);
756 }
757
758 #[test]
759 fn chord_display_major() {
760 let c = ChordSymbol {
761 root: "C".into(),
762 kind: "major".into(),
763 bass: None,
764 placement: None,
765 extender: false,
766 harmonic_degree: None,
767 harmony_function: None,
768 harmony_type: None,
769 chord_ref: None,
770 range_end: None,
771 degrees: Vec::new(),
772 };
773 assert_eq!(c.display_text(), "C");
774 }
775
776 #[test]
777 fn chord_display_minor_seventh_slash() {
778 let c = ChordSymbol {
779 root: "D".into(),
780 kind: "minor-seventh".into(),
781 bass: Some("F".into()),
782 placement: None,
783 extender: false,
784 harmonic_degree: None,
785 harmony_function: None,
786 harmony_type: None,
787 chord_ref: None,
788 range_end: None,
789 degrees: Vec::new(),
790 };
791 assert_eq!(c.display_text(), "Dm7/F");
792 }
793
794 #[test]
795 fn chord_display_structured_degrees() {
796 let c = ChordSymbol {
797 root: "C".into(),
798 kind: "dominant".into(),
799 bass: None,
800 placement: None,
801 extender: false,
802 harmonic_degree: None,
803 harmony_function: None,
804 harmony_type: None,
805 chord_ref: None,
806 range_end: None,
807 degrees: vec![
808 ChordDegree {
809 value: 9,
810 alter: 1,
811 kind: "add".into(),
812 },
813 ChordDegree {
814 value: 5,
815 alter: -1,
816 kind: "alter".into(),
817 },
818 ChordDegree {
819 value: 3,
820 alter: 0,
821 kind: "subtract".into(),
822 },
823 ],
824 };
825 assert_eq!(c.display_text(), "C7add#9b5no3");
826 }
827
828 #[test]
829 fn chord_symbol_legacy_json_defaults_degrees() {
830 let chord: ChordSymbol =
831 serde_json::from_str(r#"{"root":"C","kind":"major","bass":null,"placement":null}"#)
832 .expect("legacy chord symbol JSON deserializes");
833 assert!(chord.degrees.is_empty());
834 assert!(!chord.extender);
835 assert!(chord.harmonic_degree.is_none());
836 assert!(chord.harmony_function.is_none());
837 assert!(chord.harmony_type.is_none());
838 }
839
840 #[test]
841 fn time_sig_total_beats_three_four() {
842 let ts = TimeSignature {
843 numerator: 3,
844 denominator: 4,
845 };
846 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
847 }
848
849 #[test]
850 fn time_sig_total_beats_six_eight() {
851 let ts = TimeSignature {
852 numerator: 6,
853 denominator: 8,
854 };
855 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
856 }
857
858 #[test]
859 fn clef_treble_middle_b4() {
860 assert_eq!(Clef::Treble.middle_line_midi(), 71);
861 }
862
863 #[test]
864 fn clef_bass_middle_d3() {
865 assert_eq!(Clef::Bass.middle_line_midi(), 50);
866 }
867
868 #[test]
869 fn clef_alto_middle_c4() {
870 assert_eq!(Clef::Alto.middle_line_midi(), 60);
871 }
872
873 #[test]
874 fn clef_tenor_middle_a3() {
875 assert_eq!(Clef::Tenor.middle_line_midi(), 57);
876 }
877}