1use crate::traits::PitchMod;
6use crate::types::Octave;
7
8#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
9#[cfg_attr(
10 feature = "serde",
11 derive(serde_derive::Deserialize, serde_derive::Serialize)
12)]
13pub struct Note {
14 pub(crate) class: usize,
15 pub(crate) octave: Octave,
16}
17
18impl Note {
19 pub fn new(class: usize, Octave(octave): Octave) -> Self {
20 Self {
21 class,
22 octave: Octave(octave),
23 }
24 }
25 pub fn from_pitch(pitch: usize) -> Self {
27 Self::new(pitch.pmod(), Octave(4))
28 }
29 pub const fn class(&self) -> usize {
31 self.class
32 }
33 pub fn class_mut(&mut self) -> &mut usize {
35 &mut self.class
36 }
37 pub const fn octave(&self) -> Octave {
39 self.octave
40 }
41 pub fn octave_mut(&mut self) -> &mut Octave {
43 &mut self.octave
44 }
45 pub fn set_class(&mut self, class: usize) {
47 self.class = class.pmod();
48 }
49 pub fn set_octave(&mut self, octave: Octave) {
51 self.octave = octave;
52 }
53 pub fn with_class(self, class: usize) -> Self {
55 Self { class, ..self }
56 }
57 pub fn with_octave(self, octave: Octave) -> Self {
59 Self { octave, ..self }
60 }
61}
62
63impl core::fmt::Display for Note {
64 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
65 write!(f, "{}.{}", self.class, self.octave)
66 }
67}
68
69impl core::ops::Add<Note> for Note {
70 type Output = Self;
71
72 fn add(self, rhs: Note) -> Self::Output {
73 Self::new(self.class + rhs.class, self.octave + rhs.octave)
74 }
75}
76
77impl core::ops::AddAssign<Note> for Note {
78 fn add_assign(&mut self, rhs: Note) {
79 self.class += rhs.class;
80 self.octave += rhs.octave;
81 }
82}
83
84impl core::ops::Add<usize> for Note {
85 type Output = Self;
86
87 fn add(self, rhs: usize) -> Self::Output {
88 Self::new(self.class + rhs, self.octave)
89 }
90}
91
92impl core::ops::AddAssign<usize> for Note {
93 fn add_assign(&mut self, rhs: usize) {
94 self.class = (self.class + rhs).pmod();
95 }
96}
97
98impl core::ops::Sub<usize> for Note {
99 type Output = Self;
100
101 fn sub(self, rhs: usize) -> Self::Output {
102 let class = self.class as isize - rhs as isize;
103 Self {
104 class: class.pmod() as usize,
105 ..self
106 }
107 }
108}
109
110impl core::ops::SubAssign<usize> for Note {
111 fn sub_assign(&mut self, rhs: usize) {
112 self.class = (self.class as isize - rhs as isize).pmod() as usize;
113 }
114}
115
116macro_rules! impl_note_from {
117 ($($t:ty),*) => {
118 $(
119 impl From<$t> for Note {
120 fn from(class: $t) -> Self {
121 Note::from_pitch(class.pmod() as usize)
122 }
123 }
124
125 impl From<($t, Octave)> for Note {
126 fn from((class, octave): ($t, Octave)) -> Self {
127 Note::new(class.pmod() as usize, octave)
128 }
129 }
130 )*
131 };
132}
133
134impl_note_from!(
135 usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128
136);