use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::prelude::{Semitones, Syllable};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
pub enum IntervalQuality {
Diminished,
Minor,
Perfect,
Major,
Augmented,
Tritone,
}
impl Display for IntervalQuality {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
pub enum Interval {
Unison,
Minor2nd,
Major2nd,
Minor3nd,
Major3nd,
Perfect4th,
Augmented4th,
Tritone,
Diminished5th,
Perfect5th,
Augmented5th,
Minor6th,
Major6th,
Diminished7th,
Minor7th,
Major7th,
Perfect8ve,
}
impl Display for Interval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_text())
}
}
impl From<Interval> for IntervalQuality {
fn from(v: Interval) -> Self {
match v {
Interval::Unison => Self::Perfect,
Interval::Minor2nd => Self::Minor,
Interval::Major2nd => Self::Major,
Interval::Minor3nd => Self::Minor,
Interval::Major3nd => Self::Major,
Interval::Perfect4th => Self::Perfect,
Interval::Augmented4th => Self::Augmented,
Interval::Tritone => Self::Tritone,
Interval::Diminished5th => Self::Diminished,
Interval::Perfect5th => Self::Perfect,
Interval::Augmented5th => Self::Augmented,
Interval::Minor6th => Self::Minor,
Interval::Major6th => Self::Major,
Interval::Diminished7th => Self::Diminished,
Interval::Minor7th => Self::Minor,
Interval::Major7th => Self::Major,
Interval::Perfect8ve => Self::Perfect,
}
}
}
impl From<Interval> for Syllable {
fn from(v: Interval) -> Self {
match v {
Interval::Unison => Self::Do,
Interval::Minor2nd => Self::Ra,
Interval::Major2nd => Self::Re,
Interval::Minor3nd => Self::Me,
Interval::Major3nd => Self::Mi,
Interval::Perfect4th => Self::Fa,
Interval::Augmented4th => Self::Fi,
Interval::Tritone => Self::Se,
Interval::Diminished5th => Self::Se,
Interval::Perfect5th => Self::So,
Interval::Augmented5th => Self::Si,
Interval::Minor6th => Self::Le,
Interval::Major6th => Self::La,
Interval::Diminished7th => Self::La,
Interval::Minor7th => Self::Te,
Interval::Major7th => Self::Ti,
Interval::Perfect8ve => Self::Do,
}
}
}
impl From<Interval> for Semitones {
fn from(v: Interval) -> Self {
match v {
Interval::Unison => 0,
Interval::Minor2nd => 1,
Interval::Major2nd => 2,
Interval::Minor3nd => 3,
Interval::Major3nd => 4,
Interval::Perfect4th => 5,
Interval::Augmented4th => 6,
Interval::Tritone => 6,
Interval::Diminished5th => 6,
Interval::Perfect5th => 7,
Interval::Augmented5th => 8,
Interval::Minor6th => 8,
Interval::Major6th => 9,
Interval::Diminished7th => 9,
Interval::Minor7th => 10,
Interval::Major7th => 11,
Interval::Perfect8ve => 12,
}
.into()
}
}
impl From<(Syllable, Interval)> for Syllable {
fn from(v: (Syllable, Interval)) -> Self {
(Semitones::from(v.0) + Semitones::from(v.1)).into()
}
}
impl From<Semitones> for Interval {
fn from(v: Semitones) -> Self {
if v.0 == 0 {
return Self::Unison;
}
let mut s = v.0;
while s < 0 {
s += 12;
}
match s % 12 {
0 => Self::Perfect8ve,
1 => Self::Minor2nd,
2 => Self::Major2nd,
3 => Self::Minor3nd,
4 => Self::Major3nd,
5 => Self::Perfect4th,
6 => Self::Tritone,
7 => Self::Perfect5th,
8 => Self::Minor6th,
9 => Self::Major6th,
10 => Self::Minor7th,
11 => Self::Major7th,
_ => Self::Unison,
}
}
}
impl From<(Syllable, Syllable)> for Interval {
fn from(v: (Syllable, Syllable)) -> Self {
(Semitones::from(v.1) - Semitones::from(v.0)).into()
}
}
impl Interval {
pub fn is_matched(&self, root: Syllable, syllable: Syllable) -> bool {
Semitones::from(Interval::from((root, syllable))) == Semitones::from(*self)
}
pub fn dot_count(&self) -> usize {
match self {
Interval::Unison => 1,
Interval::Minor2nd => 2,
Interval::Major2nd => 2,
Interval::Minor3nd => 3,
Interval::Major3nd => 3,
Interval::Perfect4th => 4,
Interval::Augmented4th => 4,
Interval::Tritone => 4,
Interval::Diminished5th => 5,
Interval::Perfect5th => 5,
Interval::Augmented5th => 5,
Interval::Minor6th => 6,
Interval::Major6th => 6,
Interval::Diminished7th => 7,
Interval::Minor7th => 7,
Interval::Major7th => 7,
Interval::Perfect8ve => 1,
}
}
pub fn to_text(&self) -> String {
match self {
Interval::Unison => "1",
Interval::Minor2nd => "2-",
Interval::Major2nd => "2",
Interval::Minor3nd => "3-",
Interval::Major3nd => "3",
Interval::Perfect4th => "4",
Interval::Augmented4th => "4+",
Interval::Tritone => "t",
Interval::Diminished5th => "5o",
Interval::Perfect5th => "5",
Interval::Augmented5th => "5+",
Interval::Minor6th => "6-",
Interval::Major6th => "6",
Interval::Diminished7th => "7o",
Interval::Minor7th => "7-",
Interval::Major7th => "7",
Interval::Perfect8ve => "8",
}
.into()
}
pub fn from_text(text: &str) -> Self {
match text {
"1" => Self::Unison,
"2-" => Self::Minor2nd,
"2" => Self::Major2nd,
"3-" => Self::Minor3nd,
"3" => Self::Major3nd,
"4" => Self::Perfect4th,
"4+" => Self::Augmented4th,
"t" => Self::Tritone,
"5o" => Self::Diminished5th,
"5" => Self::Perfect5th,
"5+" => Self::Augmented5th,
"6-" => Self::Minor6th,
"6" => Self::Major6th,
"7o" => Self::Diminished7th,
"7-" => Self::Minor7th,
"7" => Self::Major7th,
"8" => Self::Perfect8ve,
_ => Self::Tritone,
}
}
pub fn to_ident(&self) -> String {
format!("{:?}", self)
}
pub fn syllable_on_root(&self, root: &Syllable) -> Syllable {
(Semitones::from(*root) + Semitones::from(*self)).into()
}
}