1use crate::{midi::MidiNote, Interval, Natural};
2use core::ops::{Add, Sub};
3use core::{fmt, mem};
4
5#[repr(u8)]
7#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
8pub enum Pitch {
9 C,
10 CSharp,
11 D,
12 DSharp,
13 E,
14 F,
15 FSharp,
16 G,
17 GSharp,
18 A,
19 ASharp,
20 B,
21}
22
23impl Pitch {
24 pub const fn natural(letter: Natural) -> Self {
32 match letter {
33 Natural::C => Self::C,
34 Natural::D => Self::D,
35 Natural::E => Self::E,
36 Natural::F => Self::F,
37 Natural::G => Self::G,
38 Natural::A => Self::A,
39 Natural::B => Self::B,
40 }
41 }
42
43 pub const fn from_byte(byte: u8) -> Self {
44 unsafe { mem::transmute(byte % (Self::B.into_byte() + 1)) }
45 }
46
47 pub const fn add_interval(self, interval: Interval) -> Self {
48 unsafe { mem::transmute((self as u8 + interval.semitones()) % (Self::B as u8 + 1)) }
49 }
50
51 pub const fn sub_interval(self, interval: Interval) -> Self {
52 Self::from_byte((self as u8 as i8 - interval.semitones() as i8).abs() as u8)
53 }
54
55 pub const fn into_byte(self) -> u8 {
56 self as _
57 }
58
59 pub const fn sub(self, rhs: Self) -> Interval {
60 Interval::new(self as u8 - rhs as u8)
61 }
62
63 pub fn transpose(self, key: Pitch, to: Pitch) -> Pitch {
64 let f = self - key;
65 to + f
66 }
67}
68
69impl From<u8> for Pitch {
70 fn from(byte: u8) -> Self {
71 Self::from_byte(byte)
72 }
73}
74
75impl From<Natural> for Pitch {
76 fn from(letter: Natural) -> Self {
77 match letter {
78 Natural::C => Self::C,
79 Natural::D => Self::D,
80 Natural::E => Self::E,
81 Natural::F => Self::F,
82 Natural::G => Self::G,
83 Natural::A => Self::A,
84 Natural::B => Self::B,
85 }
86 }
87}
88
89impl From<MidiNote> for Pitch {
90 fn from(midi: MidiNote) -> Self {
91 midi.pitch()
92 }
93}
94
95impl From<Pitch> for u8 {
96 fn from(pitch: Pitch) -> Self {
97 pitch.into_byte()
98 }
99}
100
101impl Add<Interval> for Pitch {
102 type Output = Self;
103
104 fn add(self, interval: Interval) -> Self {
105 self.add_interval(interval)
106 }
107}
108
109impl Sub for Pitch {
110 type Output = Interval;
111
112 fn sub(self, rhs: Self) -> Interval {
113 self.sub(rhs)
114 }
115}
116
117impl Sub<Interval> for Pitch {
118 type Output = Self;
119
120 fn sub(self, interval: Interval) -> Self {
121 self.sub_interval(interval)
122 }
123}
124
125impl fmt::Display for Pitch {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 let s = match self {
128 Pitch::C => "C",
129 Pitch::CSharp => "C#",
130 Pitch::D => "D",
131 Pitch::DSharp => "D#",
132 Pitch::E => "E",
133 Pitch::F => "F",
134 Pitch::FSharp => "F#",
135 Pitch::G => "G",
136 Pitch::GSharp => "G#",
137 Pitch::A => "A",
138 Pitch::ASharp => "A#",
139 Pitch::B => "B",
140 };
141 f.write_str(s)
142 }
143}