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)]
372pub struct ChordSymbol {
373 pub root: String,
375 pub kind: String,
377 pub bass: Option<String>,
379}
380
381impl ChordSymbol {
382 pub fn display_text(&self) -> String {
383 let kind_str = match self.kind.as_str() {
384 "major" | "" => "",
385 "minor" => "m",
386 "dominant" => "7",
387 "major-seventh" => "maj7",
388 "minor-seventh" => "m7",
389 "diminished" => "dim",
390 "diminished-seventh" => "dim7",
391 "augmented" => "aug",
392 "suspended-second" => "sus2",
393 "suspended-fourth" => "sus4",
394 "half-diminished" => "m7b5",
395 "major-sixth" => "6",
396 "minor-sixth" => "m6",
397 other => other,
398 };
399 let bass_str = match &self.bass {
400 Some(b) => format!("/{}", b),
401 None => String::new(),
402 };
403 format!("{}{}{}", self.root, kind_str, bass_str)
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn key_alter_c_major_all_natural() {
413 let key = KeySignature {
414 fifths: 0,
415 mode: "major".into(),
416 };
417 for step in [
418 Step::C,
419 Step::D,
420 Step::E,
421 Step::F,
422 Step::G,
423 Step::A,
424 Step::B,
425 ] {
426 assert_eq!(key.alter_for_step(&step), 0, "step {:?}", step);
427 }
428 }
429
430 #[test]
431 fn key_alter_g_major_fsharp() {
432 let key = KeySignature {
433 fifths: 1,
434 mode: "major".into(),
435 };
436 assert_eq!(key.alter_for_step(&Step::F), 1);
437 assert_eq!(key.alter_for_step(&Step::G), 0);
438 }
439
440 #[test]
441 fn key_alter_f_major_bflat() {
442 let key = KeySignature {
443 fifths: -1,
444 mode: "major".into(),
445 };
446 assert_eq!(key.alter_for_step(&Step::B), -1);
447 assert_eq!(key.alter_for_step(&Step::C), 0);
448 }
449
450 #[test]
451 fn key_alter_bb_major() {
452 let key = KeySignature {
453 fifths: -2,
454 mode: "major".into(),
455 };
456 assert_eq!(key.alter_for_step(&Step::B), -1);
457 assert_eq!(key.alter_for_step(&Step::E), -1);
458 assert_eq!(key.alter_for_step(&Step::A), 0);
459 }
460
461 #[test]
462 fn key_contains_pitch_g_major() {
463 let key = KeySignature {
464 fifths: 1,
465 mode: "major".into(),
466 };
467 assert!(key.contains_pitch(&Pitch::new(Step::G, 4)));
469 assert!(key.contains_pitch(&Pitch::new(Step::D, 4)));
470 assert!(key.contains_pitch(&Pitch::with_alter(Step::F, 4, 1))); assert!(!key.contains_pitch(&Pitch::new(Step::F, 4)));
473 }
474
475 #[test]
476 fn key_display_name_c_major() {
477 let key = KeySignature {
478 fifths: 0,
479 mode: "major".into(),
480 };
481 assert_eq!(key.display_name(), "C major");
482 }
483
484 #[test]
485 fn key_display_name_bb_major() {
486 let key = KeySignature {
487 fifths: -2,
488 mode: "major".into(),
489 };
490 assert_eq!(key.display_name(), "Bb major");
491 }
492
493 #[test]
494 fn key_display_name_fsharp_minor() {
495 let key = KeySignature {
496 fifths: 3,
497 mode: "minor".into(),
498 };
499 assert_eq!(key.display_name(), "F# minor");
500 }
501
502 #[test]
503 fn key_tonic_d_major() {
504 let key = KeySignature {
505 fifths: 2,
506 mode: "major".into(),
507 };
508 let (step, alter) = key.tonic();
509 assert_eq!(step, Step::D);
510 assert_eq!(alter, 0);
511 }
512
513 #[test]
514 fn key_tonic_a_minor() {
515 let key = KeySignature {
517 fifths: 0,
518 mode: "minor".into(),
519 };
520 let (step, alter) = key.tonic();
521 assert_eq!(step, Step::A);
522 assert_eq!(alter, 0);
523 }
524
525 #[test]
526 fn chord_display_major() {
527 let c = ChordSymbol {
528 root: "C".into(),
529 kind: "major".into(),
530 bass: None,
531 };
532 assert_eq!(c.display_text(), "C");
533 }
534
535 #[test]
536 fn chord_display_minor_seventh_slash() {
537 let c = ChordSymbol {
538 root: "D".into(),
539 kind: "minor-seventh".into(),
540 bass: Some("F".into()),
541 };
542 assert_eq!(c.display_text(), "Dm7/F");
543 }
544
545 #[test]
546 fn time_sig_total_beats_three_four() {
547 let ts = TimeSignature {
548 numerator: 3,
549 denominator: 4,
550 };
551 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
552 }
553
554 #[test]
555 fn time_sig_total_beats_six_eight() {
556 let ts = TimeSignature {
557 numerator: 6,
558 denominator: 8,
559 };
560 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
561 }
562
563 #[test]
564 fn clef_treble_middle_b4() {
565 assert_eq!(Clef::Treble.middle_line_midi(), 71);
566 }
567
568 #[test]
569 fn clef_bass_middle_d3() {
570 assert_eq!(Clef::Bass.middle_line_midi(), 50);
571 }
572
573 #[test]
574 fn clef_alto_middle_c4() {
575 assert_eq!(Clef::Alto.middle_line_midi(), 60);
576 }
577
578 #[test]
579 fn clef_tenor_middle_a3() {
580 assert_eq!(Clef::Tenor.middle_line_midi(), 57);
581 }
582}