use crate::core::{
base::{HasDescription, HasName, HasStaticName},
interval::{HasIntervals, Interval},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum ScaleKind {
Major,
NaturalMinor,
HarmonicMinor,
MelodicMinor,
WholeTone,
Chromatic,
DiminishedWholeHalf,
DiminishedHalfWhole,
MajorPentatonic,
MinorPentatonic,
Blues,
}
impl HasIntervals for ScaleKind {
fn intervals(&self) -> &'static [Interval] {
match self {
ScaleKind::Major => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MajorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MajorSixth,
Interval::MajorSeventh,
],
ScaleKind::NaturalMinor => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MinorSixth,
Interval::MinorSeventh,
],
ScaleKind::HarmonicMinor => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MinorSixth,
Interval::MajorSeventh,
],
ScaleKind::MelodicMinor => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MajorSixth,
Interval::MajorSeventh,
],
ScaleKind::WholeTone => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MajorThird,
Interval::AugmentedFourth,
Interval::AugmentedFifth,
Interval::AugmentedSixth,
],
ScaleKind::Chromatic => &[
Interval::PerfectUnison,
Interval::MinorSecond,
Interval::MajorSecond,
Interval::MinorThird,
Interval::MajorThird,
Interval::PerfectFourth,
Interval::AugmentedFourth,
Interval::PerfectFifth,
Interval::MinorSixth,
Interval::MajorSixth,
Interval::MinorSeventh,
Interval::MajorSeventh,
],
ScaleKind::DiminishedWholeHalf => &[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::DiminishedFifth,
Interval::MinorSixth,
Interval::DiminishedSeventh,
Interval::MajorSeventh,
],
ScaleKind::DiminishedHalfWhole => &[
Interval::PerfectUnison,
Interval::MinorSecond,
Interval::MinorThird,
Interval::MajorThird,
Interval::AugmentedFourth,
Interval::PerfectFifth,
Interval::MajorSixth,
Interval::MinorSeventh,
],
ScaleKind::MajorPentatonic => &[Interval::PerfectUnison, Interval::MajorSecond, Interval::MajorThird, Interval::PerfectFifth, Interval::MajorSixth],
ScaleKind::MinorPentatonic => &[Interval::PerfectUnison, Interval::MinorThird, Interval::PerfectFourth, Interval::PerfectFifth, Interval::MinorSeventh],
ScaleKind::Blues => &[
Interval::PerfectUnison,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::AugmentedFourth, Interval::PerfectFifth,
Interval::MinorSeventh,
],
}
}
}
impl HasDescription for ScaleKind {
fn description(&self) -> &'static str {
match self {
ScaleKind::Major => "major scale, ionian mode parent",
ScaleKind::NaturalMinor => "natural minor scale, aeolian mode parent",
ScaleKind::HarmonicMinor => "harmonic minor scale, raised seventh degree",
ScaleKind::MelodicMinor => "melodic minor scale, raised sixth and seventh degrees",
ScaleKind::WholeTone => "whole tone scale, all whole steps",
ScaleKind::Chromatic => "chromatic scale, all twelve semitones",
ScaleKind::DiminishedWholeHalf => "diminished scale, whole-half (W-H) pattern, fully diminished 7th chord parent",
ScaleKind::DiminishedHalfWhole => "diminished scale, half-whole (H-W) pattern, dominant 7♭9 (flat 9) chord parent",
ScaleKind::MajorPentatonic => "major pentatonic scale, five-note major scale without 4th and 7th",
ScaleKind::MinorPentatonic => "minor pentatonic scale, five-note minor scale without 2nd and 6th",
ScaleKind::Blues => "blues scale, minor pentatonic with added ♯4 (blue note)",
}
}
}
impl HasStaticName for ScaleKind {
fn static_name(&self) -> &'static str {
match self {
ScaleKind::Major => "major",
ScaleKind::NaturalMinor => "natural minor",
ScaleKind::HarmonicMinor => "harmonic minor",
ScaleKind::MelodicMinor => "melodic minor",
ScaleKind::WholeTone => "whole tone",
ScaleKind::Chromatic => "chromatic",
ScaleKind::DiminishedWholeHalf => "diminished (whole-half)",
ScaleKind::DiminishedHalfWhole => "diminished (half-whole)",
ScaleKind::MajorPentatonic => "major pentatonic",
ScaleKind::MinorPentatonic => "minor pentatonic",
ScaleKind::Blues => "blues",
}
}
}
impl HasName for ScaleKind {
fn name(&self) -> String {
self.static_name().to_owned()
}
}
impl ScaleKind {
pub fn ascii_name(&self) -> &'static str {
match self {
ScaleKind::Major => "major",
ScaleKind::NaturalMinor => "natural minor",
ScaleKind::HarmonicMinor => "harmonic minor",
ScaleKind::MelodicMinor => "melodic minor",
ScaleKind::WholeTone => "whole tone",
ScaleKind::Chromatic => "chromatic",
ScaleKind::DiminishedWholeHalf => "diminished whole-half",
ScaleKind::DiminishedHalfWhole => "diminished half-whole",
ScaleKind::MajorPentatonic => "major pentatonic",
ScaleKind::MinorPentatonic => "minor pentatonic",
ScaleKind::Blues => "blues",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_scale_intervals() {
assert_eq!(ScaleKind::Major.intervals().len(), 7);
assert_eq!(
ScaleKind::Major.intervals(),
&[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MajorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MajorSixth,
Interval::MajorSeventh,
]
);
assert_eq!(ScaleKind::NaturalMinor.intervals().len(), 7);
assert_eq!(
ScaleKind::NaturalMinor.intervals(),
&[
Interval::PerfectUnison,
Interval::MajorSecond,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::PerfectFifth,
Interval::MinorSixth,
Interval::MinorSeventh,
]
);
assert_eq!(ScaleKind::HarmonicMinor.intervals().len(), 7);
assert_eq!(ScaleKind::WholeTone.intervals().len(), 6);
assert_eq!(ScaleKind::Chromatic.intervals().len(), 12);
assert_eq!(ScaleKind::DiminishedWholeHalf.intervals().len(), 8);
assert_eq!(ScaleKind::DiminishedHalfWhole.intervals().len(), 8);
assert_eq!(ScaleKind::MajorPentatonic.intervals().len(), 5);
assert_eq!(
ScaleKind::MajorPentatonic.intervals(),
&[Interval::PerfectUnison, Interval::MajorSecond, Interval::MajorThird, Interval::PerfectFifth, Interval::MajorSixth,]
);
assert_eq!(ScaleKind::MinorPentatonic.intervals().len(), 5);
assert_eq!(
ScaleKind::MinorPentatonic.intervals(),
&[Interval::PerfectUnison, Interval::MinorThird, Interval::PerfectFourth, Interval::PerfectFifth, Interval::MinorSeventh,]
);
assert_eq!(ScaleKind::Blues.intervals().len(), 6);
assert_eq!(
ScaleKind::Blues.intervals(),
&[
Interval::PerfectUnison,
Interval::MinorThird,
Interval::PerfectFourth,
Interval::AugmentedFourth,
Interval::PerfectFifth,
Interval::MinorSeventh,
]
);
}
#[test]
fn test_scale_names() {
assert_eq!(ScaleKind::Major.static_name(), "major");
assert_eq!(ScaleKind::NaturalMinor.static_name(), "natural minor");
assert_eq!(ScaleKind::HarmonicMinor.static_name(), "harmonic minor");
assert_eq!(ScaleKind::MelodicMinor.static_name(), "melodic minor");
assert_eq!(ScaleKind::WholeTone.static_name(), "whole tone");
assert_eq!(ScaleKind::MajorPentatonic.static_name(), "major pentatonic");
assert_eq!(ScaleKind::MinorPentatonic.static_name(), "minor pentatonic");
assert_eq!(ScaleKind::Blues.static_name(), "blues");
}
#[test]
fn test_scale_descriptions() {
assert_eq!(ScaleKind::Major.description(), "major scale, ionian mode parent");
assert_eq!(ScaleKind::NaturalMinor.description(), "natural minor scale, aeolian mode parent");
assert_eq!(ScaleKind::MajorPentatonic.description(), "major pentatonic scale, five-note major scale without 4th and 7th");
assert_eq!(ScaleKind::MinorPentatonic.description(), "minor pentatonic scale, five-note minor scale without 2nd and 6th");
assert_eq!(ScaleKind::Blues.description(), "blues scale, minor pentatonic with added ♯4 (blue note)");
}
}