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