1use std::fmt::{Display, Error, Formatter};
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8use pest::Parser;
9
10use crate::core::{
11 base::{HasDescription, HasName, HasPreciseName, HasStaticName, Parsable, Res},
12 chord::HasRoot,
13 interval::{HasIntervals, Interval},
14 note::Note,
15 parser::{note_str_to_note, scale_name_str_to_scale_kind, ChordParser, Rule},
16 scale_kind::ScaleKind,
17};
18
19pub trait HasScaleKind {
23 fn kind(&self) -> ScaleKind;
25}
26
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[derive(PartialEq, Eq, Copy, Clone, Debug)]
35pub struct Scale {
36 root: Note,
38 kind: ScaleKind,
40}
41
42impl Scale {
45 pub fn new(root: Note, kind: ScaleKind) -> Self {
47 Self { root, kind }
48 }
49
50 pub fn intervals(&self) -> &'static [Interval] {
52 self.kind.intervals()
53 }
54
55 pub fn notes(&self) -> Vec<Note> {
57 self.intervals().iter().map(|&interval| self.root + interval).collect()
58 }
59}
60
61impl HasRoot for Scale {
62 fn root(&self) -> Note {
63 self.root
64 }
65}
66
67impl HasScaleKind for Scale {
68 fn kind(&self) -> ScaleKind {
69 self.kind
70 }
71}
72
73impl HasIntervals for Scale {
74 fn intervals(&self) -> &'static [Interval] {
75 self.kind.intervals()
76 }
77}
78
79impl HasStaticName for Scale {
80 fn static_name(&self) -> &'static str {
81 self.kind.static_name()
82 }
83}
84
85impl HasName for Scale {
86 fn name(&self) -> String {
87 format!("{} {}", self.root.static_name(), self.kind.static_name())
88 }
89}
90
91impl HasPreciseName for Scale {
92 fn precise_name(&self) -> String {
93 format!("{} {}", self.root.name(), self.kind.static_name())
94 }
95}
96
97impl HasDescription for Scale {
98 fn description(&self) -> &'static str {
99 self.kind.description()
100 }
101}
102
103impl Display for Scale {
104 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
105 let notes = self.notes().iter().map(|n| n.static_name()).collect::<Vec<_>>().join(", ");
106 write!(f, "{}\n {}\n {}", self.name(), self.description(), notes)
107 }
108}
109
110impl Parsable for Scale {
111 fn parse(input: &str) -> Res<Self>
112 where
113 Self: Sized,
114 {
115 let root = ChordParser::parse(Rule::scale, input)?.next().unwrap();
116
117 assert_eq!(Rule::scale, root.as_rule());
118
119 let mut components = root.into_inner();
120
121 let note = components.next().unwrap();
122 assert_eq!(Rule::note_atomic, note.as_rule());
123 let root_note = note_str_to_note(note.as_str().trim())?;
124
125 let scale_name = components.next().unwrap();
126 assert_eq!(Rule::scale_name, scale_name.as_rule());
127 let scale_kind = scale_name_str_to_scale_kind(scale_name.as_str())?;
128
129 Ok(Scale::new(root_note, scale_kind))
130 }
131}
132
133#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::core::named_pitch::{HasLetter, HasNamedPitch};
139 use crate::core::note::*;
140 use pretty_assertions::assert_eq;
141
142 impl Scale {
143 pub(crate) fn validate_spelling(&self) -> Result<(), String> {
152 use crate::core::named_pitch::{HasLetter, HasNamedPitch};
153 use std::collections::HashMap;
154
155 let notes = self.notes();
156 let intervals_count = self.intervals().len();
157
158 if intervals_count == 12 || intervals_count == 8 || self.kind() == ScaleKind::Blues {
160 return Ok(());
161 }
162
163 let mut letter_counts: HashMap<&str, usize> = HashMap::new();
165 for note in ¬es {
166 let letter = note.named_pitch().letter();
167 *letter_counts.entry(letter).or_insert(0) += 1;
168 }
169
170 if intervals_count == 7 {
172 if letter_counts.len() != 7 {
173 return Err(format!(
174 "{} {} has {} unique letters, expected 7. Letters: {:?}",
175 self.root().static_name(),
176 self.kind().static_name(),
177 letter_counts.len(),
178 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
179 ));
180 }
181
182 for (letter, count) in &letter_counts {
183 if *count != 1 {
184 return Err(format!(
185 "{} {} has letter {} appearing {} times, expected 1. Notes: {:?}",
186 self.root().static_name(),
187 self.kind().static_name(),
188 letter,
189 count,
190 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
191 ));
192 }
193 }
194 } else {
195 for (letter, count) in &letter_counts {
197 if *count > 1 {
198 return Err(format!(
199 "{} {} has letter {} appearing {} times. Notes: {:?}",
200 self.root().static_name(),
201 self.kind().static_name(),
202 letter,
203 count,
204 notes.iter().map(|n| n.static_name()).collect::<Vec<_>>()
205 ));
206 }
207 }
208 }
209
210 Ok(())
211 }
212 }
213
214 #[test]
215 fn test_scale_creation() {
216 let scale = Scale::new(C, ScaleKind::Major);
217 assert_eq!(scale.root(), C);
218 assert_eq!(scale.kind(), ScaleKind::Major);
219 }
220
221 #[test]
222 fn test_scale_intervals() {
223 let scale = Scale::new(C, ScaleKind::Major);
224 assert_eq!(scale.intervals().len(), 7);
225 assert_eq!(
226 scale.intervals(),
227 &[
228 Interval::PerfectUnison,
229 Interval::MajorSecond,
230 Interval::MajorThird,
231 Interval::PerfectFourth,
232 Interval::PerfectFifth,
233 Interval::MajorSixth,
234 Interval::MajorSeventh,
235 ]
236 );
237 }
238
239 #[test]
240 fn test_scale_notes() {
241 let scale = Scale::new(C, ScaleKind::Major);
243 assert_eq!(scale.notes(), vec![C, D, E, F, G, A, B]);
244
245 let scale = Scale::new(D, ScaleKind::Major);
247 assert_eq!(scale.notes(), vec![D, E, FSharp, G, A, B, CSharpFive]);
248
249 let scale = Scale::new(A, ScaleKind::NaturalMinor);
251 assert_eq!(scale.notes(), vec![A, B, CFive, DFive, EFive, FFive, GFive]);
252
253 let scale = Scale::new(A, ScaleKind::HarmonicMinor);
255 assert_eq!(scale.notes(), vec![A, B, CFive, DFive, EFive, FFive, GSharpFive]);
256
257 let scale = Scale::new(A, ScaleKind::MelodicMinor);
259 assert_eq!(scale.notes(), vec![A, B, CFive, DFive, EFive, FSharpFive, GSharpFive]);
260
261 let scale = Scale::new(C, ScaleKind::WholeTone);
263 assert_eq!(scale.notes(), vec![C, D, E, FSharp, GSharp, ASharp]);
264
265 let scale = Scale::new(C, ScaleKind::Chromatic);
267 assert_eq!(scale.notes().len(), 12);
268 }
269
270 #[test]
271 fn test_scale_names() {
272 let scale = Scale::new(C, ScaleKind::Major);
273 assert_eq!(scale.name(), "C major");
274 assert_eq!(scale.static_name(), "major");
275
276 let scale = Scale::new(DFlat, ScaleKind::HarmonicMinor);
277 assert_eq!(scale.name(), "D♭ harmonic minor");
278
279 let scale = Scale::new(FSharp, ScaleKind::WholeTone);
280 assert_eq!(scale.name(), "F♯ whole tone");
281 }
282
283 #[test]
284 fn test_scale_display() {
285 let scale = Scale::new(C, ScaleKind::Major);
286 let display = format!("{}", scale);
287 assert!(display.contains("C major"));
288 assert!(display.contains("C, D, E, F, G, A, B"));
289 }
290
291 #[test]
292 fn test_different_roots() {
293 let scale = Scale::new(G, ScaleKind::Major);
295 assert_eq!(scale.notes(), vec![G, A, B, CFive, DFive, EFive, FSharpFive]);
296
297 let scale = Scale::new(F, ScaleKind::Major);
299 assert_eq!(scale.notes(), vec![F, G, A, BFlat, CFive, DFive, EFive]);
300
301 let scale = Scale::new(E, ScaleKind::NaturalMinor);
303 assert_eq!(scale.notes(), vec![E, FSharp, G, A, B, CFive, DFive]);
304 }
305
306 #[test]
307 fn test_scale_parse() {
308 let scale = Scale::parse("C major").unwrap();
310 assert_eq!(scale.root(), C);
311 assert_eq!(scale.kind(), ScaleKind::Major);
312
313 let scale = Scale::parse("A natural minor").unwrap();
314 assert_eq!(scale.root(), A);
315 assert_eq!(scale.kind(), ScaleKind::NaturalMinor);
316
317 let scale = Scale::parse("A naturalminor").unwrap();
318 assert_eq!(scale.root(), A);
319 assert_eq!(scale.kind(), ScaleKind::NaturalMinor);
320
321 let scale = Scale::parse("A harmonic minor").unwrap();
322 assert_eq!(scale.root(), A);
323 assert_eq!(scale.kind(), ScaleKind::HarmonicMinor);
324
325 let scale = Scale::parse("A melodic minor").unwrap();
326 assert_eq!(scale.root(), A);
327 assert_eq!(scale.kind(), ScaleKind::MelodicMinor);
328
329 let scale = Scale::parse("C whole tone").unwrap();
330 assert_eq!(scale.root(), C);
331 assert_eq!(scale.kind(), ScaleKind::WholeTone);
332
333 let scale = Scale::parse("C chromatic").unwrap();
334 assert_eq!(scale.root(), C);
335 assert_eq!(scale.kind(), ScaleKind::Chromatic);
336
337 let scale = Scale::parse("F# major").unwrap();
339 assert_eq!(scale.root(), FSharp);
340 assert_eq!(scale.kind(), ScaleKind::Major);
341
342 let scale = Scale::parse("Bb harmonic minor").unwrap();
343 assert_eq!(scale.root(), BFlat);
344 assert_eq!(scale.kind(), ScaleKind::HarmonicMinor);
345
346 let scale = Scale::parse("C major pentatonic").unwrap();
348 assert_eq!(scale.root(), C);
349 assert_eq!(scale.kind(), ScaleKind::MajorPentatonic);
350
351 let scale = Scale::parse("C majorpentatonic").unwrap();
352 assert_eq!(scale.root(), C);
353 assert_eq!(scale.kind(), ScaleKind::MajorPentatonic);
354
355 let scale = Scale::parse("A minor pentatonic").unwrap();
356 assert_eq!(scale.root(), A);
357 assert_eq!(scale.kind(), ScaleKind::MinorPentatonic);
358
359 let scale = Scale::parse("A minorpentatonic").unwrap();
360 assert_eq!(scale.root(), A);
361 assert_eq!(scale.kind(), ScaleKind::MinorPentatonic);
362
363 let scale = Scale::parse("C blues").unwrap();
365 assert_eq!(scale.root(), C);
366 assert_eq!(scale.kind(), ScaleKind::Blues);
367
368 let scale = Scale::parse("E blues").unwrap();
369 assert_eq!(scale.root(), E);
370 assert_eq!(scale.kind(), ScaleKind::Blues);
371 }
372
373 #[test]
374 fn test_enharmonic_spelling_major_scales() {
375 let scale = Scale::new(C, ScaleKind::Major);
379 scale.validate_spelling().unwrap();
380
381 let scale = Scale::new(G, ScaleKind::Major);
383 scale.validate_spelling().unwrap();
384 let notes = scale.notes();
385 assert_eq!(notes.len(), 7);
386
387 let scale = Scale::new(DFlat, ScaleKind::Major);
389 scale.validate_spelling().unwrap();
390 let notes = scale.notes();
391 assert_eq!(notes[0].named_pitch().letter(), "D");
392 assert_eq!(notes[1].named_pitch().letter(), "E");
393 assert_eq!(notes[2].named_pitch().letter(), "F");
394 assert_eq!(notes[3].named_pitch().letter(), "G");
395 assert_eq!(notes[4].named_pitch().letter(), "A");
396 assert_eq!(notes[5].named_pitch().letter(), "B");
397 assert_eq!(notes[6].named_pitch().letter(), "C");
398
399 let scale = Scale::new(FSharp, ScaleKind::Major);
401 scale.validate_spelling().unwrap();
402 let notes = scale.notes();
403 assert_eq!(notes[0].named_pitch().letter(), "F");
404 assert_eq!(notes[1].named_pitch().letter(), "G");
405 assert_eq!(notes[2].named_pitch().letter(), "A");
406 assert_eq!(notes[3].named_pitch().letter(), "B");
407 assert_eq!(notes[4].named_pitch().letter(), "C");
408 assert_eq!(notes[5].named_pitch().letter(), "D");
409 assert_eq!(notes[6].named_pitch().letter(), "E");
410 }
411
412 #[test]
413 fn test_enharmonic_spelling_minor_scales() {
414 let scale = Scale::new(A, ScaleKind::NaturalMinor);
418 scale.validate_spelling().unwrap();
419
420 let scale = Scale::new(A, ScaleKind::HarmonicMinor);
422 scale.validate_spelling().unwrap();
423 let notes = scale.notes();
424 assert_eq!(notes[6].named_pitch().letter(), "G"); let scale = Scale::new(CSharp, ScaleKind::HarmonicMinor);
428 scale.validate_spelling().unwrap();
429
430 let scale = Scale::new(F, ScaleKind::MelodicMinor);
432 scale.validate_spelling().unwrap();
433 }
434
435 #[test]
436 fn test_enharmonic_spelling_pentatonic_scales() {
437 let scale = Scale::new(C, ScaleKind::MajorPentatonic);
441 scale.validate_spelling().unwrap();
442 let notes = scale.notes();
443 assert_eq!(notes.len(), 5);
444
445 let scale = Scale::new(A, ScaleKind::MinorPentatonic);
447 scale.validate_spelling().unwrap();
448 let notes = scale.notes();
449 assert_eq!(notes.len(), 5);
450
451 let scale = Scale::new(FSharp, ScaleKind::MajorPentatonic);
453 scale.validate_spelling().unwrap();
454
455 let scale = Scale::new(BFlat, ScaleKind::MinorPentatonic);
457 scale.validate_spelling().unwrap();
458 }
459
460 #[test]
461 fn test_enharmonic_spelling_blues_scale() {
462 let scale = Scale::new(C, ScaleKind::Blues);
466 scale.validate_spelling().unwrap();
467 let notes = scale.notes();
468 assert_eq!(notes.len(), 6);
469
470 let scale = Scale::new(E, ScaleKind::Blues);
472 scale.validate_spelling().unwrap();
473
474 let scale = Scale::new(G, ScaleKind::Blues);
476 scale.validate_spelling().unwrap();
477 }
478
479 #[test]
480 fn test_enharmonic_spelling_whole_tone() {
481 let scale = Scale::new(C, ScaleKind::WholeTone);
485 scale.validate_spelling().unwrap();
486 let notes = scale.notes();
487 assert_eq!(notes.len(), 6);
488
489 let scale = Scale::new(DFlat, ScaleKind::WholeTone);
491 scale.validate_spelling().unwrap();
492 }
493
494 #[test]
495 fn test_enharmonic_spelling_diminished() {
496 let scale = Scale::new(C, ScaleKind::DiminishedWholeHalf);
500 scale.validate_spelling().unwrap(); let notes = scale.notes();
502 assert_eq!(notes.len(), 8);
503
504 let scale = Scale::new(C, ScaleKind::DiminishedHalfWhole);
506 scale.validate_spelling().unwrap(); let notes = scale.notes();
508 assert_eq!(notes.len(), 8);
509 }
510
511 #[test]
512 fn test_enharmonic_spelling_chromatic() {
513 let scale = Scale::new(C, ScaleKind::Chromatic);
517 scale.validate_spelling().unwrap(); let notes = scale.notes();
519 assert_eq!(notes.len(), 12);
520 }
521
522 #[test]
523 fn test_enharmonic_spelling_all_roots() {
524 for root in [C, CSharp, D, DFlat, E, F, FSharp, G, GFlat, A, AFlat, B, BFlat] {
526 let scale = Scale::new(root, ScaleKind::Major);
528 scale.validate_spelling().unwrap_or_else(|e| panic!("Major spelling failed for {}: {}", root.static_name(), e));
529
530 let scale = Scale::new(root, ScaleKind::NaturalMinor);
532 scale.validate_spelling().unwrap_or_else(|e| panic!("Natural Minor spelling failed for {}: {}", root.static_name(), e));
533
534 let scale = Scale::new(root, ScaleKind::HarmonicMinor);
536 scale.validate_spelling().unwrap_or_else(|e| panic!("Harmonic Minor spelling failed for {}: {}", root.static_name(), e));
537
538 let scale = Scale::new(root, ScaleKind::MajorPentatonic);
540 scale
541 .validate_spelling()
542 .unwrap_or_else(|e| panic!("Major Pentatonic spelling failed for {}: {}", root.static_name(), e));
543
544 let scale = Scale::new(root, ScaleKind::Blues);
546 scale.validate_spelling().unwrap_or_else(|e| panic!("Blues spelling failed for {}: {}", root.static_name(), e));
547 }
548 }
549
550 #[test]
551 fn test_heptatonic_spelling() {
552 let scale = Scale::new(DFlat, ScaleKind::Major);
553 assert_eq!(scale.notes(), vec![DFlat, EFlat, F, GFlat, AFlat, BFlat, CFive], "Db major scale spelling incorrect");
554 scale.validate_spelling().unwrap();
555
556 let scale = Scale::new(CSharp, ScaleKind::Major);
557 assert_eq!(
558 scale.notes(),
559 vec![CSharp, DSharp, ESharp, FSharp, GSharp, ASharp, BSharp],
560 "C# major scale spelling incorrect - should use sharps consistently"
561 );
562 scale.validate_spelling().unwrap();
563 }
564
565 #[test]
566 fn test_whole_tone_spelling() {
567 let scale = Scale::new(A, ScaleKind::WholeTone);
568 assert_eq!(
569 scale.intervals(),
570 &[
571 Interval::PerfectUnison,
572 Interval::MajorSecond,
573 Interval::MajorThird,
574 Interval::AugmentedFourth,
575 Interval::AugmentedFifth,
576 Interval::AugmentedSixth,
577 ],
578 "Whole tone scale intervals should use augmented intervals, not respelled for prettiness"
579 );
580
581 let notes = scale.notes();
582 assert_eq!(notes.len(), 6, "Whole tone scale should have 6 notes");
583 scale.validate_spelling().unwrap();
584
585 let scale = Scale::new(FSharp, ScaleKind::WholeTone);
586 let notes = scale.notes();
587 assert_eq!(notes.len(), 6, "F# whole tone scale should have 6 notes");
588 scale.validate_spelling().unwrap();
589 }
590
591 #[test]
592 fn test_octatonic_spelling() {
593 let scale = Scale::new(A, ScaleKind::DiminishedHalfWhole);
594 let notes = scale.notes();
595 assert_eq!(
596 scale.intervals(),
597 &[
598 Interval::PerfectUnison,
599 Interval::MinorSecond,
600 Interval::MinorThird,
601 Interval::MajorThird,
602 Interval::AugmentedFourth,
603 Interval::PerfectFifth,
604 Interval::MajorSixth,
605 Interval::MinorSeventh,
606 ]
607 );
608 assert_eq!(notes.len(), 8, "Diminished half-whole should have 8 notes");
609
610 let scale = Scale::new(A, ScaleKind::DiminishedWholeHalf);
611 let notes = scale.notes();
612 assert_eq!(
613 scale.intervals(),
614 &[
615 Interval::PerfectUnison,
616 Interval::MajorSecond,
617 Interval::MinorThird,
618 Interval::PerfectFourth,
619 Interval::DiminishedFifth,
620 Interval::MinorSixth,
621 Interval::DiminishedSeventh,
622 Interval::MajorSeventh,
623 ]
624 );
625 assert_eq!(notes.len(), 8, "Diminished whole-half should have 8 notes");
626 }
627
628 #[test]
629 fn test_pentatonic_blues_spelling() {
630 let scale = Scale::new(DFlat, ScaleKind::MajorPentatonic);
631 assert_eq!(scale.notes(), vec![DFlat, EFlat, F, AFlat, BFlat], "Db major pentatonic spelling incorrect");
632 scale.validate_spelling().unwrap();
633
634 let scale = Scale::new(A, ScaleKind::MinorPentatonic);
635 assert_eq!(scale.notes(), vec![A, CFive, DFive, EFive, GFive], "A minor pentatonic spelling incorrect");
636 scale.validate_spelling().unwrap();
637
638 let scale = Scale::new(FSharp, ScaleKind::Blues);
639 let notes = scale.notes();
640 assert_eq!(
641 notes,
642 vec![FSharp, A, B, BSharp, CSharpFive, EFive],
643 "F# blues scale spelling incorrect - should use B# (augmented 4th), not C (diminished 5th)"
644 );
645 assert_eq!(
646 scale.intervals(),
647 &[
648 Interval::PerfectUnison,
649 Interval::MinorThird,
650 Interval::PerfectFourth,
651 Interval::AugmentedFourth,
652 Interval::PerfectFifth,
653 Interval::MinorSeventh,
654 ]
655 );
656 scale.validate_spelling().unwrap();
657 }
658}