use crate::core::Frequency;
use super::helpers::mel;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub trait HasPitch {
fn pitch(&self) -> Pitch;
}
pub trait HasBaseFrequency {
fn base_frequency(&self) -> f32;
}
pub trait HasFrequency {
fn frequency(&self) -> f32;
fn frequency_range(&self) -> (f32, f32) {
let frequency = self.frequency();
(
frequency * (1.0 - 1.0 / 17.462 / 2.0),
frequency * (1.0 + 1.0 / 16.8196 / 2.0),
)
}
fn tight_frequency_range(&self) -> (f32, f32) {
let frequency = self.frequency();
(
frequency * (1.0 - 1.0 / 17.462 / 8.0),
frequency * (1.0 + 1.0 / 16.8196 / 8.0),
)
}
}
pub trait HasMel: HasFrequency {
fn mel(&self) -> f32 {
mel(self.frequency())
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug, Ord, PartialOrd)]
#[repr(u8)]
pub enum Pitch {
C,
DFlat,
D,
EFlat,
E,
F,
GFlat,
G,
AFlat,
A,
BFlat,
B,
}
impl Pitch {
pub const fn base_frequency(&self) -> Frequency {
Frequency(match self {
Pitch::C => 16.35,
Pitch::DFlat => 17.32,
Pitch::D => 18.35,
Pitch::EFlat => 19.45,
Pitch::E => 20.60,
Pitch::F => 21.83,
Pitch::GFlat => 23.12,
Pitch::G => 24.50,
Pitch::AFlat => 25.96,
Pitch::A => 27.50,
Pitch::BFlat => 29.14,
Pitch::B => 30.87,
})
}
}
impl HasPitch for Pitch {
fn pitch(&self) -> Pitch {
*self
}
}
impl TryFrom<u8> for Pitch {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Pitch::C),
1 => Ok(Pitch::DFlat),
2 => Ok(Pitch::D),
3 => Ok(Pitch::EFlat),
4 => Ok(Pitch::E),
5 => Ok(Pitch::F),
6 => Ok(Pitch::GFlat),
7 => Ok(Pitch::G),
8 => Ok(Pitch::AFlat),
9 => Ok(Pitch::A),
10 => Ok(Pitch::BFlat),
11 => Ok(Pitch::B),
_ => Err("Invalid pitch"),
}
}
}
pub static ALL_PITCHES: [Pitch; 12] = [
Pitch::C,
Pitch::DFlat,
Pitch::D,
Pitch::EFlat,
Pitch::E,
Pitch::F,
Pitch::GFlat,
Pitch::G,
Pitch::AFlat,
Pitch::A,
Pitch::BFlat,
Pitch::B,
];
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_properties() {
self::assert_eq!(Pitch::G.pitch(), Pitch::G);
self::assert_eq!(Pitch::G.base_frequency().hertz(), 24.50);
}
}