use std::ops::{Add, Sub};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::core::{
base::HasStaticName,
pitch::{HasPitch, Pitch},
};
pub trait HasNamedPitch {
fn named_pitch(&self) -> NamedPitch;
}
pub trait HasLetter {
fn letter(&self) -> &'static str;
}
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum NamedPitch {
FTripleFlat,
CTripleFlat,
GTripleFlat,
DTripleFlat,
ATripleFlat,
ETripleFlat,
BTripleFlat,
FDoubleFlat,
CDoubleFlat,
GDoubleFlat,
DDoubleFlat,
ADoubleFlat,
EDoubleFlat,
BDoubleFlat,
FFlat,
CFlat,
GFlat,
DFlat,
AFlat,
EFlat,
BFlat,
F,
C,
G,
D,
A,
E,
B,
FSharp,
CSharp,
GSharp,
DSharp,
ASharp,
ESharp,
BSharp,
FDoubleSharp,
CDoubleSharp,
GDoubleSharp,
DDoubleSharp,
ADoubleSharp,
EDoubleSharp,
BDoubleSharp,
FTripleSharp,
CTripleSharp,
GTripleSharp,
DTripleSharp,
ATripleSharp,
ETripleSharp,
BTripleSharp,
}
impl HasNamedPitch for NamedPitch {
fn named_pitch(&self) -> NamedPitch {
*self
}
}
impl HasLetter for NamedPitch {
#[coverage(off)]
fn letter(&self) -> &'static str {
match self {
NamedPitch::FTripleFlat => "F",
NamedPitch::CTripleFlat => "C",
NamedPitch::GTripleFlat => "G",
NamedPitch::DTripleFlat => "D",
NamedPitch::ATripleFlat => "A",
NamedPitch::ETripleFlat => "E",
NamedPitch::BTripleFlat => "B",
NamedPitch::FDoubleFlat => "F",
NamedPitch::CDoubleFlat => "C",
NamedPitch::GDoubleFlat => "G",
NamedPitch::DDoubleFlat => "D",
NamedPitch::ADoubleFlat => "A",
NamedPitch::EDoubleFlat => "E",
NamedPitch::BDoubleFlat => "B",
NamedPitch::FFlat => "F",
NamedPitch::CFlat => "C",
NamedPitch::GFlat => "G",
NamedPitch::DFlat => "D",
NamedPitch::AFlat => "A",
NamedPitch::EFlat => "E",
NamedPitch::BFlat => "B",
NamedPitch::F => "F",
NamedPitch::C => "C",
NamedPitch::G => "G",
NamedPitch::D => "D",
NamedPitch::A => "A",
NamedPitch::E => "E",
NamedPitch::B => "B",
NamedPitch::FSharp => "F",
NamedPitch::CSharp => "C",
NamedPitch::GSharp => "G",
NamedPitch::DSharp => "D",
NamedPitch::ASharp => "A",
NamedPitch::ESharp => "E",
NamedPitch::BSharp => "B",
NamedPitch::FDoubleSharp => "F",
NamedPitch::CDoubleSharp => "C",
NamedPitch::GDoubleSharp => "G",
NamedPitch::DDoubleSharp => "D",
NamedPitch::ADoubleSharp => "A",
NamedPitch::EDoubleSharp => "E",
NamedPitch::BDoubleSharp => "B",
NamedPitch::FTripleSharp => "F",
NamedPitch::CTripleSharp => "C",
NamedPitch::GTripleSharp => "G",
NamedPitch::DTripleSharp => "D",
NamedPitch::ATripleSharp => "A",
NamedPitch::ETripleSharp => "E",
NamedPitch::BTripleSharp => "B",
}
}
}
impl HasStaticName for NamedPitch {
#[coverage(off)]
fn static_name(&self) -> &'static str {
match self {
NamedPitch::FTripleFlat => "F♭𝄫",
NamedPitch::CTripleFlat => "C♭𝄫",
NamedPitch::GTripleFlat => "G♭𝄫",
NamedPitch::DTripleFlat => "D♭𝄫",
NamedPitch::ATripleFlat => "A♭𝄫",
NamedPitch::ETripleFlat => "E♭𝄫",
NamedPitch::BTripleFlat => "B♭𝄫",
NamedPitch::FDoubleFlat => "F𝄫",
NamedPitch::CDoubleFlat => "C𝄫",
NamedPitch::GDoubleFlat => "G𝄫",
NamedPitch::DDoubleFlat => "D𝄫",
NamedPitch::ADoubleFlat => "A𝄫",
NamedPitch::EDoubleFlat => "E𝄫",
NamedPitch::BDoubleFlat => "B𝄫",
NamedPitch::FFlat => "F♭",
NamedPitch::CFlat => "C♭",
NamedPitch::GFlat => "G♭",
NamedPitch::DFlat => "D♭",
NamedPitch::AFlat => "A♭",
NamedPitch::EFlat => "E♭",
NamedPitch::BFlat => "B♭",
NamedPitch::F => "F",
NamedPitch::C => "C",
NamedPitch::G => "G",
NamedPitch::D => "D",
NamedPitch::A => "A",
NamedPitch::E => "E",
NamedPitch::B => "B",
NamedPitch::FSharp => "F♯",
NamedPitch::CSharp => "C♯",
NamedPitch::GSharp => "G♯",
NamedPitch::DSharp => "D♯",
NamedPitch::ASharp => "A♯",
NamedPitch::ESharp => "E♯",
NamedPitch::BSharp => "B♯",
NamedPitch::FDoubleSharp => "F𝄪",
NamedPitch::CDoubleSharp => "C𝄪",
NamedPitch::GDoubleSharp => "G𝄪",
NamedPitch::DDoubleSharp => "D𝄪",
NamedPitch::ADoubleSharp => "A𝄪",
NamedPitch::EDoubleSharp => "E𝄪",
NamedPitch::BDoubleSharp => "B𝄪",
NamedPitch::FTripleSharp => "F♯𝄪",
NamedPitch::CTripleSharp => "C♯𝄪",
NamedPitch::GTripleSharp => "G♯𝄪",
NamedPitch::DTripleSharp => "D♯𝄪",
NamedPitch::ATripleSharp => "A♯𝄪",
NamedPitch::ETripleSharp => "E♯𝄪",
NamedPitch::BTripleSharp => "B♯𝄪",
}
}
}
impl HasPitch for NamedPitch {
#[coverage(off)]
fn pitch(&self) -> Pitch {
match self {
NamedPitch::FTripleFlat => Pitch::D,
NamedPitch::CTripleFlat => Pitch::A,
NamedPitch::GTripleFlat => Pitch::E,
NamedPitch::DTripleFlat => Pitch::B,
NamedPitch::ATripleFlat => Pitch::GFlat,
NamedPitch::ETripleFlat => Pitch::DFlat,
NamedPitch::BTripleFlat => Pitch::AFlat,
NamedPitch::FDoubleFlat => Pitch::EFlat,
NamedPitch::CDoubleFlat => Pitch::BFlat,
NamedPitch::GDoubleFlat => Pitch::F,
NamedPitch::DDoubleFlat => Pitch::C,
NamedPitch::ADoubleFlat => Pitch::G,
NamedPitch::EDoubleFlat => Pitch::D,
NamedPitch::BDoubleFlat => Pitch::A,
NamedPitch::FFlat => Pitch::E,
NamedPitch::CFlat => Pitch::B,
NamedPitch::GFlat => Pitch::GFlat,
NamedPitch::DFlat => Pitch::DFlat,
NamedPitch::AFlat => Pitch::AFlat,
NamedPitch::EFlat => Pitch::EFlat,
NamedPitch::BFlat => Pitch::BFlat,
NamedPitch::F => Pitch::F,
NamedPitch::C => Pitch::C,
NamedPitch::G => Pitch::G,
NamedPitch::D => Pitch::D,
NamedPitch::A => Pitch::A,
NamedPitch::E => Pitch::E,
NamedPitch::B => Pitch::B,
NamedPitch::FSharp => Pitch::GFlat,
NamedPitch::CSharp => Pitch::DFlat,
NamedPitch::GSharp => Pitch::AFlat,
NamedPitch::DSharp => Pitch::EFlat,
NamedPitch::ASharp => Pitch::BFlat,
NamedPitch::ESharp => Pitch::F,
NamedPitch::BSharp => Pitch::C,
NamedPitch::FDoubleSharp => Pitch::G,
NamedPitch::CDoubleSharp => Pitch::D,
NamedPitch::GDoubleSharp => Pitch::A,
NamedPitch::DDoubleSharp => Pitch::E,
NamedPitch::ADoubleSharp => Pitch::B,
NamedPitch::EDoubleSharp => Pitch::GFlat,
NamedPitch::BDoubleSharp => Pitch::DFlat,
NamedPitch::FTripleSharp => Pitch::AFlat,
NamedPitch::CTripleSharp => Pitch::EFlat,
NamedPitch::GTripleSharp => Pitch::BFlat,
NamedPitch::DTripleSharp => Pitch::F,
NamedPitch::ATripleSharp => Pitch::C,
NamedPitch::ETripleSharp => Pitch::G,
NamedPitch::BTripleSharp => Pitch::D,
}
}
}
impl Add<i8> for NamedPitch {
type Output = Self;
fn add(self, rhs: i8) -> Self {
let index = ALL_PITCHES.iter().position(|&p| p == self).unwrap();
let new_index = index as i8 + rhs;
assert!((0..=49).contains(&new_index), "NamedPitch out of range.");
ALL_PITCHES[new_index as usize]
}
}
impl Sub<i8> for NamedPitch {
type Output = Self;
fn sub(self, rhs: i8) -> Self {
self + (-rhs)
}
}
impl From<Pitch> for NamedPitch {
fn from(pitch: Pitch) -> Self {
NamedPitch::from(&pitch)
}
}
impl From<&Pitch> for NamedPitch {
fn from(pitch: &Pitch) -> Self {
match pitch {
Pitch::C => NamedPitch::C,
Pitch::DFlat => NamedPitch::DFlat,
Pitch::D => NamedPitch::D,
Pitch::EFlat => NamedPitch::EFlat,
Pitch::E => NamedPitch::E,
Pitch::F => NamedPitch::F,
Pitch::GFlat => NamedPitch::GFlat,
Pitch::G => NamedPitch::G,
Pitch::AFlat => NamedPitch::AFlat,
Pitch::A => NamedPitch::A,
Pitch::BFlat => NamedPitch::BFlat,
Pitch::B => NamedPitch::B,
}
}
}
static ALL_PITCHES: [NamedPitch; 49] = [
NamedPitch::FTripleFlat,
NamedPitch::CTripleFlat,
NamedPitch::GTripleFlat,
NamedPitch::DTripleFlat,
NamedPitch::ATripleFlat,
NamedPitch::ETripleFlat,
NamedPitch::BTripleFlat,
NamedPitch::FDoubleFlat,
NamedPitch::CDoubleFlat,
NamedPitch::GDoubleFlat,
NamedPitch::DDoubleFlat,
NamedPitch::ADoubleFlat,
NamedPitch::EDoubleFlat,
NamedPitch::BDoubleFlat,
NamedPitch::FFlat,
NamedPitch::CFlat,
NamedPitch::GFlat,
NamedPitch::DFlat,
NamedPitch::AFlat,
NamedPitch::EFlat,
NamedPitch::BFlat,
NamedPitch::F,
NamedPitch::C,
NamedPitch::G,
NamedPitch::D,
NamedPitch::A,
NamedPitch::E,
NamedPitch::B,
NamedPitch::FSharp,
NamedPitch::CSharp,
NamedPitch::GSharp,
NamedPitch::DSharp,
NamedPitch::ASharp,
NamedPitch::ESharp,
NamedPitch::BSharp,
NamedPitch::FDoubleSharp,
NamedPitch::CDoubleSharp,
NamedPitch::GDoubleSharp,
NamedPitch::DDoubleSharp,
NamedPitch::ADoubleSharp,
NamedPitch::EDoubleSharp,
NamedPitch::BDoubleSharp,
NamedPitch::FTripleSharp,
NamedPitch::CTripleSharp,
NamedPitch::GTripleSharp,
NamedPitch::DTripleSharp,
NamedPitch::ATripleSharp,
NamedPitch::ETripleSharp,
NamedPitch::BTripleSharp,
];
#[cfg(test)]
mod tests {
use super::*;
use crate::core::note::*;
use pretty_assertions::assert_eq;
#[test]
#[should_panic]
fn test_improper_add() {
let _ = C.named_pitch() + 50;
}
#[test]
fn test_properties() {
assert_eq!(NamedPitch::A.named_pitch(), NamedPitch::A);
}
#[test]
fn test_pitch_conversion() {
assert_eq!(NamedPitch::from(Pitch::C), NamedPitch::C);
assert_eq!(NamedPitch::from(&Pitch::C), NamedPitch::C);
assert_eq!(NamedPitch::from(Pitch::DFlat), NamedPitch::DFlat);
assert_eq!(NamedPitch::from(&Pitch::DFlat), NamedPitch::DFlat);
assert_eq!(NamedPitch::from(Pitch::D), NamedPitch::D);
assert_eq!(NamedPitch::from(&Pitch::D), NamedPitch::D);
assert_eq!(NamedPitch::from(Pitch::EFlat), NamedPitch::EFlat);
assert_eq!(NamedPitch::from(&Pitch::EFlat), NamedPitch::EFlat);
assert_eq!(NamedPitch::from(Pitch::E), NamedPitch::E);
assert_eq!(NamedPitch::from(&Pitch::E), NamedPitch::E);
assert_eq!(NamedPitch::from(Pitch::F), NamedPitch::F);
assert_eq!(NamedPitch::from(&Pitch::F), NamedPitch::F);
assert_eq!(NamedPitch::from(Pitch::GFlat), NamedPitch::GFlat);
assert_eq!(NamedPitch::from(&Pitch::GFlat), NamedPitch::GFlat);
assert_eq!(NamedPitch::from(Pitch::G), NamedPitch::G);
assert_eq!(NamedPitch::from(&Pitch::G), NamedPitch::G);
assert_eq!(NamedPitch::from(Pitch::AFlat), NamedPitch::AFlat);
assert_eq!(NamedPitch::from(&Pitch::AFlat), NamedPitch::AFlat);
assert_eq!(NamedPitch::from(Pitch::A), NamedPitch::A);
assert_eq!(NamedPitch::from(&Pitch::A), NamedPitch::A);
assert_eq!(NamedPitch::from(Pitch::BFlat), NamedPitch::BFlat);
assert_eq!(NamedPitch::from(&Pitch::BFlat), NamedPitch::BFlat);
assert_eq!(NamedPitch::from(Pitch::B), NamedPitch::B);
assert_eq!(NamedPitch::from(&Pitch::B), NamedPitch::B);
}
}