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