1use crate::{Interval, Pitch};
2use core::fmt;
3use core::ops::{Add, Sub};
4
5mod octave;
6pub use octave::Octave;
7
8pub mod message;
9
10mod midi_set;
11pub use midi_set::MidiSet;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15pub struct MidiNote(u8);
16
17impl MidiNote {
18 pub const fn new(pitch: Pitch, octave: Octave) -> Self {
28 Self::from_byte(
29 (octave.into_i8() + 1) as u8 * (Pitch::B.into_byte() + 1) + pitch.into_byte(),
30 )
31 }
32
33 pub const fn from_byte(byte: u8) -> Self {
35 Self(byte)
36 }
37
38 pub const fn pitch(self) -> Pitch {
46 Pitch::from_byte(self.into_byte())
47 }
48
49 pub const fn octave(self) -> Octave {
65 Octave::from_midi(self)
66 }
67
68 #[cfg(feature = "std")]
69 pub fn frequency(self) -> f64 {
70 let a_midi = 69;
71 let a_freq = 440.;
72 a_freq * 2f64.powf((self.into_byte() as i8 - a_midi) as f64 / 12.)
73 }
74
75 pub const fn into_byte(self) -> u8 {
77 self.0
78 }
79}
80
81impl Add<Interval> for MidiNote {
82 type Output = Self;
83
84 fn add(self, rhs: Interval) -> Self::Output {
85 Self::from_byte(self.into_byte() + rhs.semitones())
86 }
87}
88
89impl Sub for MidiNote {
90 type Output = Interval;
91
92 fn sub(self, rhs: Self) -> Self::Output {
93 Interval::new((self.into_byte() as i8 - rhs.into_byte() as i8).abs() as _)
94 }
95}
96
97impl From<u8> for MidiNote {
98 fn from(byte: u8) -> Self {
99 Self::from_byte(byte)
100 }
101}
102
103impl From<MidiNote> for u8 {
104 fn from(midi: MidiNote) -> Self {
105 midi.into_byte()
106 }
107}
108
109impl fmt::Display for MidiNote {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}{}", self.pitch(), self.octave())
112 }
113}