use crate::{Interval, Pitch};
use core::fmt;
use core::ops::{Add, Sub};
mod octave;
pub use octave::Octave;
pub mod message;
mod midi_set;
pub use midi_set::MidiSet;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MidiNote(u8);
impl MidiNote {
pub const fn new(pitch: Pitch, octave: Octave) -> Self {
Self::from_byte(
(octave.into_i8() + 1) as u8 * (Pitch::B.into_byte() + 1) + pitch.into_byte(),
)
}
pub const fn from_byte(byte: u8) -> Self {
Self(byte)
}
pub const fn pitch(self) -> Pitch {
Pitch::from_byte(self.into_byte())
}
pub const fn octave(self) -> Octave {
Octave::from_midi(self)
}
#[cfg(feature = "std")]
pub fn frequency(self) -> f64 {
let a_midi = 69;
let a_freq = 440.;
a_freq * 2f64.powf((self.into_byte() as i8 - a_midi) as f64 / 12.)
}
pub const fn into_byte(self) -> u8 {
self.0
}
pub fn abs_diff(self, rhs: Self) -> Interval {
let interval = Interval::new((self.into_byte() as u8).abs_diff(rhs.into_byte()));
if self < rhs {
Interval::OCTAVE - interval
} else {
interval
}
}
}
impl Add<Interval> for MidiNote {
type Output = Self;
fn add(self, rhs: Interval) -> Self::Output {
Self::from_byte(self.into_byte() + rhs.semitones())
}
}
impl Sub for MidiNote {
type Output = Interval;
fn sub(self, rhs: Self) -> Self::Output {
Interval::new((self.into_byte() as i8 - rhs.into_byte() as i8).abs() as _)
}
}
impl From<u8> for MidiNote {
fn from(byte: u8) -> Self {
Self::from_byte(byte)
}
}
impl From<MidiNote> for u8 {
fn from(midi: MidiNote) -> Self {
midi.into_byte()
}
}
impl fmt::Display for MidiNote {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.pitch(), self.octave())
}
}