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, PartialEq, Serialize, Deserialize, Default)]
281pub enum Barline {
282 #[default]
283 Normal,
284 Double,
285 Final,
286 RepeatStart,
287 RepeatEnd,
288 RepeatBoth,
289 Dashed,
290 Dotted,
291 Invisible,
292}
293
294#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
295pub enum HairpinKind {
296 Crescendo,
297 Decrescendo,
298}
299
300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
301pub struct TupletInfo {
302 pub actual_notes: u8,
304 pub normal_notes: u8,
306}
307
308#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
309pub enum BeamState {
310 #[default]
311 None,
312 Begin,
313 Continue,
314 End,
315 BeginEnd,
316 BackwardHook,
317 ForwardHook,
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
322pub enum OttavaKind {
323 Va8,
325 Vb8,
327 Ma15,
329 Mb15,
331}
332
333impl OttavaKind {
334 pub fn musicxml_type(&self) -> &'static str {
335 match self {
336 OttavaKind::Va8 | OttavaKind::Ma15 => "up",
337 OttavaKind::Vb8 | OttavaKind::Mb15 => "down",
338 }
339 }
340
341 pub fn musicxml_size(&self) -> u8 {
342 match self {
343 OttavaKind::Va8 | OttavaKind::Vb8 => 8,
344 OttavaKind::Ma15 | OttavaKind::Mb15 => 15,
345 }
346 }
347}
348
349#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
351pub enum NoteHead {
352 #[default]
353 Normal,
354 Diamond, X, Slash, Cross, Triangle, }
360
361#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
363pub struct Lyric {
364 pub text: String,
366 pub syllabic: String,
368}
369
370#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
373pub struct StyledText {
374 pub style: TextStyle,
375 pub text: String,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
379pub enum TextStyle {
380 Expression,
381 Technique,
382 Lyrics,
383 ChordSymbol,
384 RehearsalMark,
385 Generic,
386}
387
388#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
391pub struct CrossStaff {
392 pub target_staff: usize,
393 #[serde(default)]
394 pub target_voice: Option<usize>,
395}
396
397#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
399pub struct TabPosition {
400 pub string: u8,
402 pub fret: u8,
403}
404
405#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
407pub struct TablatureConfig {
408 pub lines: u8,
409 pub tuning_midi: Vec<i16>,
410 #[serde(default)]
411 pub capo: u8,
412}
413
414#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
416pub struct ChordSymbol {
417 pub root: String,
419 pub kind: String,
421 pub bass: Option<String>,
423}
424
425impl ChordSymbol {
426 pub fn display_text(&self) -> String {
427 let kind_str = match self.kind.as_str() {
428 "major" | "" => "",
429 "minor" => "m",
430 "dominant" => "7",
431 "major-seventh" => "maj7",
432 "minor-seventh" => "m7",
433 "diminished" => "dim",
434 "diminished-seventh" => "dim7",
435 "augmented" => "aug",
436 "suspended-second" => "sus2",
437 "suspended-fourth" => "sus4",
438 "half-diminished" => "m7b5",
439 "major-sixth" => "6",
440 "minor-sixth" => "m6",
441 other => other,
442 };
443 let bass_str = match &self.bass {
444 Some(b) => format!("/{}", b),
445 None => String::new(),
446 };
447 format!("{}{}{}", self.root, kind_str, bass_str)
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn key_alter_c_major_all_natural() {
457 let key = KeySignature {
458 fifths: 0,
459 mode: "major".into(),
460 };
461 for step in [
462 Step::C,
463 Step::D,
464 Step::E,
465 Step::F,
466 Step::G,
467 Step::A,
468 Step::B,
469 ] {
470 assert_eq!(key.alter_for_step(&step), 0, "step {:?}", step);
471 }
472 }
473
474 #[test]
475 fn key_alter_g_major_fsharp() {
476 let key = KeySignature {
477 fifths: 1,
478 mode: "major".into(),
479 };
480 assert_eq!(key.alter_for_step(&Step::F), 1);
481 assert_eq!(key.alter_for_step(&Step::G), 0);
482 }
483
484 #[test]
485 fn key_alter_f_major_bflat() {
486 let key = KeySignature {
487 fifths: -1,
488 mode: "major".into(),
489 };
490 assert_eq!(key.alter_for_step(&Step::B), -1);
491 assert_eq!(key.alter_for_step(&Step::C), 0);
492 }
493
494 #[test]
495 fn key_alter_bb_major() {
496 let key = KeySignature {
497 fifths: -2,
498 mode: "major".into(),
499 };
500 assert_eq!(key.alter_for_step(&Step::B), -1);
501 assert_eq!(key.alter_for_step(&Step::E), -1);
502 assert_eq!(key.alter_for_step(&Step::A), 0);
503 }
504
505 #[test]
506 fn key_contains_pitch_g_major() {
507 let key = KeySignature {
508 fifths: 1,
509 mode: "major".into(),
510 };
511 assert!(key.contains_pitch(&Pitch::new(Step::G, 4)));
513 assert!(key.contains_pitch(&Pitch::new(Step::D, 4)));
514 assert!(key.contains_pitch(&Pitch::with_alter(Step::F, 4, 1))); assert!(!key.contains_pitch(&Pitch::new(Step::F, 4)));
517 }
518
519 #[test]
520 fn key_display_name_c_major() {
521 let key = KeySignature {
522 fifths: 0,
523 mode: "major".into(),
524 };
525 assert_eq!(key.display_name(), "C major");
526 }
527
528 #[test]
529 fn key_display_name_bb_major() {
530 let key = KeySignature {
531 fifths: -2,
532 mode: "major".into(),
533 };
534 assert_eq!(key.display_name(), "Bb major");
535 }
536
537 #[test]
538 fn key_display_name_fsharp_minor() {
539 let key = KeySignature {
540 fifths: 3,
541 mode: "minor".into(),
542 };
543 assert_eq!(key.display_name(), "F# minor");
544 }
545
546 #[test]
547 fn key_tonic_d_major() {
548 let key = KeySignature {
549 fifths: 2,
550 mode: "major".into(),
551 };
552 let (step, alter) = key.tonic();
553 assert_eq!(step, Step::D);
554 assert_eq!(alter, 0);
555 }
556
557 #[test]
558 fn key_tonic_a_minor() {
559 let key = KeySignature {
561 fifths: 0,
562 mode: "minor".into(),
563 };
564 let (step, alter) = key.tonic();
565 assert_eq!(step, Step::A);
566 assert_eq!(alter, 0);
567 }
568
569 #[test]
570 fn chord_display_major() {
571 let c = ChordSymbol {
572 root: "C".into(),
573 kind: "major".into(),
574 bass: None,
575 };
576 assert_eq!(c.display_text(), "C");
577 }
578
579 #[test]
580 fn chord_display_minor_seventh_slash() {
581 let c = ChordSymbol {
582 root: "D".into(),
583 kind: "minor-seventh".into(),
584 bass: Some("F".into()),
585 };
586 assert_eq!(c.display_text(), "Dm7/F");
587 }
588
589 #[test]
590 fn time_sig_total_beats_three_four() {
591 let ts = TimeSignature {
592 numerator: 3,
593 denominator: 4,
594 };
595 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
596 }
597
598 #[test]
599 fn time_sig_total_beats_six_eight() {
600 let ts = TimeSignature {
601 numerator: 6,
602 denominator: 8,
603 };
604 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
605 }
606
607 #[test]
608 fn clef_treble_middle_b4() {
609 assert_eq!(Clef::Treble.middle_line_midi(), 71);
610 }
611
612 #[test]
613 fn clef_bass_middle_d3() {
614 assert_eq!(Clef::Bass.middle_line_midi(), 50);
615 }
616
617 #[test]
618 fn clef_alto_middle_c4() {
619 assert_eq!(Clef::Alto.middle_line_midi(), 60);
620 }
621
622 #[test]
623 fn clef_tenor_middle_a3() {
624 assert_eq!(Clef::Tenor.middle_line_midi(), 57);
625 }
626}