1use crate::PitchMod;
6use crate::octave::Octave;
7
8#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
15#[cfg_attr(
16 feature = "serde",
17 derive(serde_derive::Deserialize, serde_derive::Serialize),
18 serde(deny_unknown_fields, default, rename_all = "snake_case")
19)]
20#[repr(C)]
21pub struct Aspn {
22 pub(crate) class: usize,
23 pub(crate) octave: Octave,
24}
25
26impl Aspn {
27 pub fn new(class: usize, Octave(octave): Octave) -> Self {
28 Self {
29 class,
30 octave: Octave(octave),
31 }
32 }
33 pub fn from_pitch(pitch: usize) -> Self {
35 Self::new(pitch.pmod(), Octave(4))
36 }
37 pub const fn class(&self) -> usize {
39 self.class
40 }
41 pub fn class_mut(&mut self) -> &mut usize {
43 &mut self.class
44 }
45 pub const fn octave(&self) -> Octave {
47 self.octave
48 }
49 pub const fn octave_mut(&mut self) -> &mut Octave {
51 &mut self.octave
52 }
53 pub fn set_class(&mut self, class: usize) -> &mut Self {
55 self.class = class.pmod();
56 self
57 }
58 pub fn set_octave(&mut self, octave: Octave) -> &mut Self {
60 self.octave = octave;
61 self
62 }
63 pub fn with_class(self, class: usize) -> Self {
65 Self { class, ..self }
66 }
67 pub fn with_octave(self, octave: Octave) -> Self {
69 Self { octave, ..self }
70 }
71}
72
73impl core::fmt::Display for Aspn {
74 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
75 write!(f, "{}.{}", self.class, self.octave)
76 }
77}
78
79impl PartialEq<usize> for Aspn {
80 fn eq(&self, other: &usize) -> bool {
81 self.class() == *other
82 }
83}
84
85impl PartialEq<Aspn> for usize {
86 fn eq(&self, other: &Aspn) -> bool {
87 *self == other.class()
88 }
89}
90
91impl PartialOrd<usize> for Aspn {
92 fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
93 self.class().partial_cmp(other)
94 }
95}
96
97impl PartialOrd<Aspn> for usize {
98 fn partial_cmp(&self, other: &Aspn) -> Option<core::cmp::Ordering> {
99 self.partial_cmp(&other.class())
100 }
101}
102
103impl core::ops::Add<Aspn> for Aspn {
104 type Output = Self;
105
106 fn add(self, rhs: Aspn) -> Self::Output {
107 let class = (self.class + rhs.class).pmod();
108 let octave = self.octave + rhs.octave;
109
110 Self::new(class, octave)
111 }
112}
113
114impl core::ops::AddAssign<Aspn> for Aspn {
115 fn add_assign(&mut self, rhs: Aspn) {
116 self.class += rhs.class;
117 self.octave += rhs.octave;
118 }
119}
120
121impl core::ops::Add<usize> for Aspn {
122 type Output = Self;
123
124 fn add(self, rhs: usize) -> Self::Output {
125 Self::new(self.class + rhs, self.octave)
126 }
127}
128
129impl core::ops::AddAssign<usize> for Aspn {
130 fn add_assign(&mut self, rhs: usize) {
131 self.class = (self.class + rhs).pmod();
132 }
133}
134
135impl core::ops::Sub<usize> for Aspn {
136 type Output = Self;
137
138 fn sub(self, rhs: usize) -> Self::Output {
139 let class = self.class as isize - rhs as isize;
140 Self {
141 class: class.pmod() as usize,
142 ..self
143 }
144 }
145}
146
147impl core::ops::SubAssign<usize> for Aspn {
148 fn sub_assign(&mut self, rhs: usize) {
149 self.class = (self.class as isize - rhs as isize).pmod() as usize;
150 }
151}
152
153macro_rules! impl_note_from {
154 ($($t:ty),*) => {
155 $(
156 impl From<$t> for Aspn {
157 fn from(class: $t) -> Self {
158 Self::from_pitch(class.pmod() as usize)
159 }
160 }
161
162 impl From<($t, Octave)> for Aspn {
163 fn from((class, octave): ($t, Octave)) -> Self {
164 Self::new(class.pmod() as usize, octave)
165 }
166 }
167 )*
168 };
169}
170
171impl_note_from!(
172 usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128
173);