music_note/midi/
mod.rs

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/// MIDI note represented as a byte.
14#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15pub struct MidiNote(u8);
16
17impl MidiNote {
18    /// Create a new `MidiNote` from a `Pitch` and `Octave`.
19    /// ```
20    /// use music_note::midi::{Octave, MidiNote};
21    /// use music_note::Pitch;
22    ///
23    /// let note = MidiNote::new(Pitch::A, Octave::FOUR);
24    /// assert_eq!(note.into_byte(), 69);
25    /// assert_eq!(note.to_string(), "A4");
26    /// ```
27    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    /// Create a new `MidiNote` from a byte.
34    pub const fn from_byte(byte: u8) -> Self {
35        Self(byte)
36    }
37
38    /// ```
39    /// use music_note::midi::MidiNote;
40    /// use music_note::Pitch;
41    ///
42    /// let note = MidiNote::from_byte(108);
43    /// assert_eq!(note.pitch(), Pitch::C);
44    /// ```
45    pub const fn pitch(self) -> Pitch {
46        Pitch::from_byte(self.into_byte())
47    }
48
49    /// ```
50    /// use music_note::midi::{Octave, MidiNote};
51    /// use music_note::Pitch;
52    ///
53    /// let note = MidiNote::new(Pitch::C, Octave::EIGHT);
54    /// assert_eq!(note.octave(), Octave::EIGHT);
55    /// ```
56    ///
57    /// Midi notes start at octave -1.
58    /// ```
59    /// use music_note::midi::{Octave, MidiNote};
60    ///
61    /// let note = MidiNote::from_byte(11);
62    /// assert_eq!(note.octave(), Octave::NEGATIVE_ONE);
63    /// ```
64    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    /// Return the byte representation of `self`.
76    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}