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 { fifths: 0, mode: "major".to_string() }
58 }
59}
60
61impl KeySignature {
62 const SHARP_ORDER: [Step; 7] = [Step::F, Step::C, Step::G, Step::D, Step::A, Step::E, Step::B];
64 const FLAT_ORDER: [Step; 7] = [Step::B, Step::E, Step::A, Step::D, Step::G, Step::C, Step::F];
66
67 pub fn alter_for_step(&self, step: &Step) -> i8 {
69 if self.fifths > 0 {
70 let count = self.fifths.min(7) as usize;
71 if Self::SHARP_ORDER[..count].contains(step) { 1 } else { 0 }
72 } else if self.fifths < 0 {
73 let count = (-self.fifths).min(7) as usize;
74 if Self::FLAT_ORDER[..count].contains(step) { -1 } else { 0 }
75 } else {
76 0
77 }
78 }
79
80 pub fn contains_pitch(&self, pitch: &Pitch) -> bool {
82 pitch.alter == self.alter_for_step(&pitch.step)
83 }
84
85 pub fn tonic(&self) -> (Step, i8) {
89 if self.mode == "minor" {
90 let (maj_step, maj_alter) = Self::major_tonic_from_fifths(self.fifths);
91 let major_midi = Pitch::with_alter(maj_step, 4, maj_alter).to_midi();
93 let minor_midi = (major_midi - 3).clamp(0, 127) as u8;
94 let p = Pitch::from_midi(minor_midi, self.fifths < 0);
95 (p.step, p.alter)
96 } else {
97 Self::major_tonic_from_fifths(self.fifths)
98 }
99 }
100
101 pub fn display_name(&self) -> String {
103 let (step, alter) = self.tonic();
104 let acc = match alter { 1 => "#", -1 => "b", _ => "" };
105 format!("{}{} {}", step.to_char(), acc, self.mode)
106 }
107
108 fn major_tonic_from_fifths(fifths: i8) -> (Step, i8) {
109 const TONICS: [(Step, i8); 15] = [
112 (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), ];
128 let idx = (fifths.clamp(-7, 7) + 7) as usize;
129 TONICS[idx].clone()
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub struct TimeSignature {
135 pub numerator: u8,
136 pub denominator: u8,
137}
138
139impl Default for TimeSignature {
140 fn default() -> Self {
141 Self { numerator: 4, denominator: 4 }
142 }
143}
144
145impl TimeSignature {
146 pub fn beats_per_measure(&self) -> f64 {
147 self.numerator as f64
148 }
149
150 pub fn beat_unit_beats(&self) -> f64 {
151 4.0 / self.denominator as f64
152 }
153
154 pub fn total_beats(&self) -> f64 {
155 self.beats_per_measure() * self.beat_unit_beats()
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub enum Dynamic {
161 Pppp, Ppp, Pp, P, Mp, Mf, F, Ff, Fff, Ffff,
162 Sfz, Rfz, Fz, Sf,
163}
164
165impl Dynamic {
166 pub fn to_musicxml_str(&self) -> &'static str {
167 match self {
168 Dynamic::Pppp => "pppp",
169 Dynamic::Ppp => "ppp",
170 Dynamic::Pp => "pp",
171 Dynamic::P => "p",
172 Dynamic::Mp => "mp",
173 Dynamic::Mf => "mf",
174 Dynamic::F => "f",
175 Dynamic::Ff => "ff",
176 Dynamic::Fff => "fff",
177 Dynamic::Ffff => "ffff",
178 Dynamic::Sfz => "sfz",
179 Dynamic::Rfz => "rfz",
180 Dynamic::Fz => "fz",
181 Dynamic::Sf => "sf",
182 }
183 }
184
185 pub fn to_velocity(&self) -> u8 {
186 match self {
187 Dynamic::Pppp => 16,
188 Dynamic::Ppp => 24,
189 Dynamic::Pp => 36,
190 Dynamic::P => 48,
191 Dynamic::Mp => 60,
192 Dynamic::Mf => 72,
193 Dynamic::F => 84,
194 Dynamic::Ff => 96,
195 Dynamic::Fff => 108,
196 Dynamic::Ffff => 120,
197 Dynamic::Sfz => 112,
198 Dynamic::Rfz => 104,
199 Dynamic::Fz => 100,
200 Dynamic::Sf => 96,
201 }
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206pub enum Articulation {
207 Staccato,
208 Staccatissimo,
209 Accent,
210 Tenuto,
211 Marcato,
212 Fermata,
213 Trill,
214 Mordent,
215 InvertedMordent,
216 Turn,
217 InvertedTurn,
218 Shake,
219 Tremolo(u8),
220 BreathMark,
221 Caesura,
222}
223
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
226#[serde(rename_all = "kebab-case")]
227pub enum GuitarTechnique {
228 Bend,
229 Slide,
230 HammerOn,
231 PullOff,
232}
233
234#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
235pub enum Barline {
236 #[default]
237 Normal,
238 Double,
239 Final,
240 RepeatStart,
241 RepeatEnd,
242 RepeatBoth,
243 Dashed,
244 Dotted,
245 Invisible,
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
249pub enum HairpinKind {
250 Crescendo,
251 Decrescendo,
252}
253
254#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
255pub struct TupletInfo {
256 pub actual_notes: u8,
258 pub normal_notes: u8,
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
263pub enum BeamState {
264 #[default]
265 None,
266 Begin,
267 Continue,
268 End,
269 BeginEnd,
270 BackwardHook,
271 ForwardHook,
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
276pub enum OttavaKind {
277 Va8,
279 Vb8,
281 Ma15,
283 Mb15,
285}
286
287impl OttavaKind {
288 pub fn musicxml_type(&self) -> &'static str {
289 match self {
290 OttavaKind::Va8 | OttavaKind::Ma15 => "up",
291 OttavaKind::Vb8 | OttavaKind::Mb15 => "down",
292 }
293 }
294
295 pub fn musicxml_size(&self) -> u8 {
296 match self {
297 OttavaKind::Va8 | OttavaKind::Vb8 => 8,
298 OttavaKind::Ma15 | OttavaKind::Mb15 => 15,
299 }
300 }
301}
302
303#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
305pub enum NoteHead {
306 #[default]
307 Normal,
308 Diamond, X, Slash, Cross, Triangle, }
314
315#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
317pub struct Lyric {
318 pub text: String,
320 pub syllabic: String,
322}
323
324#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
326pub struct ChordSymbol {
327 pub root: String,
329 pub kind: String,
331 pub bass: Option<String>,
333}
334
335impl ChordSymbol {
336 pub fn display_text(&self) -> String {
337 let kind_str = match self.kind.as_str() {
338 "major" | "" => "",
339 "minor" => "m",
340 "dominant" => "7",
341 "major-seventh" => "maj7",
342 "minor-seventh" => "m7",
343 "diminished" => "dim",
344 "diminished-seventh" => "dim7",
345 "augmented" => "aug",
346 "suspended-second" => "sus2",
347 "suspended-fourth" => "sus4",
348 "half-diminished" => "m7b5",
349 "major-sixth" => "6",
350 "minor-sixth" => "m6",
351 other => other,
352 };
353 let bass_str = match &self.bass {
354 Some(b) => format!("/{}", b),
355 None => String::new(),
356 };
357 format!("{}{}{}", self.root, kind_str, bass_str)
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364
365 #[test]
366 fn key_alter_c_major_all_natural() {
367 let key = KeySignature { fifths: 0, mode: "major".into() };
368 for step in [Step::C, Step::D, Step::E, Step::F, Step::G, Step::A, Step::B] {
369 assert_eq!(key.alter_for_step(&step), 0, "step {:?}", step);
370 }
371 }
372
373 #[test]
374 fn key_alter_g_major_fsharp() {
375 let key = KeySignature { fifths: 1, mode: "major".into() };
376 assert_eq!(key.alter_for_step(&Step::F), 1);
377 assert_eq!(key.alter_for_step(&Step::G), 0);
378 }
379
380 #[test]
381 fn key_alter_f_major_bflat() {
382 let key = KeySignature { fifths: -1, mode: "major".into() };
383 assert_eq!(key.alter_for_step(&Step::B), -1);
384 assert_eq!(key.alter_for_step(&Step::C), 0);
385 }
386
387 #[test]
388 fn key_alter_bb_major() {
389 let key = KeySignature { fifths: -2, mode: "major".into() };
390 assert_eq!(key.alter_for_step(&Step::B), -1);
391 assert_eq!(key.alter_for_step(&Step::E), -1);
392 assert_eq!(key.alter_for_step(&Step::A), 0);
393 }
394
395 #[test]
396 fn key_contains_pitch_g_major() {
397 let key = KeySignature { fifths: 1, mode: "major".into() };
398 assert!(key.contains_pitch(&Pitch::new(Step::G, 4)));
400 assert!(key.contains_pitch(&Pitch::new(Step::D, 4)));
401 assert!(key.contains_pitch(&Pitch::with_alter(Step::F, 4, 1))); assert!(!key.contains_pitch(&Pitch::new(Step::F, 4)));
404 }
405
406 #[test]
407 fn key_display_name_c_major() {
408 let key = KeySignature { fifths: 0, mode: "major".into() };
409 assert_eq!(key.display_name(), "C major");
410 }
411
412 #[test]
413 fn key_display_name_bb_major() {
414 let key = KeySignature { fifths: -2, mode: "major".into() };
415 assert_eq!(key.display_name(), "Bb major");
416 }
417
418 #[test]
419 fn key_display_name_fsharp_minor() {
420 let key = KeySignature { fifths: 3, mode: "minor".into() };
421 assert_eq!(key.display_name(), "F# minor");
422 }
423
424 #[test]
425 fn key_tonic_d_major() {
426 let key = KeySignature { fifths: 2, mode: "major".into() };
427 let (step, alter) = key.tonic();
428 assert_eq!(step, Step::D);
429 assert_eq!(alter, 0);
430 }
431
432 #[test]
433 fn key_tonic_a_minor() {
434 let key = KeySignature { fifths: 0, mode: "minor".into() };
436 let (step, alter) = key.tonic();
437 assert_eq!(step, Step::A);
438 assert_eq!(alter, 0);
439 }
440
441 #[test]
442 fn chord_display_major() {
443 let c = ChordSymbol { root: "C".into(), kind: "major".into(), bass: None };
444 assert_eq!(c.display_text(), "C");
445 }
446
447 #[test]
448 fn chord_display_minor_seventh_slash() {
449 let c = ChordSymbol { root: "D".into(), kind: "minor-seventh".into(), bass: Some("F".into()) };
450 assert_eq!(c.display_text(), "Dm7/F");
451 }
452
453 #[test]
454 fn time_sig_total_beats_three_four() {
455 let ts = TimeSignature { numerator: 3, denominator: 4 };
456 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
457 }
458
459 #[test]
460 fn time_sig_total_beats_six_eight() {
461 let ts = TimeSignature { numerator: 6, denominator: 8 };
462 assert!((ts.total_beats() - 3.0).abs() < 1e-9);
463 }
464
465 #[test]
466 fn clef_treble_middle_b4() {
467 assert_eq!(Clef::Treble.middle_line_midi(), 71);
468 }
469
470 #[test]
471 fn clef_bass_middle_d3() {
472 assert_eq!(Clef::Bass.middle_line_midi(), 50);
473 }
474
475 #[test]
476 fn clef_alto_middle_c4() {
477 assert_eq!(Clef::Alto.middle_line_midi(), 60);
478 }
479
480 #[test]
481 fn clef_tenor_middle_a3() {
482 assert_eq!(Clef::Tenor.middle_line_midi(), 57);
483 }
484}