pitch_calc/
step.rs

1use super::{
2    calc, hz_from_step, letter_octave_from_step, mel_from_step, perc_from_step,
3    scaled_perc_from_step, Hz, Letter, LetterOctave, Mel, Octave, Perc, ScaleWeight, ScaledPerc,
4    DEFAULT_SCALE_WEIGHT,
5};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::cmp::Ordering;
9use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
10
11/// Pitch representation in the form of a MIDI-esque Step.
12#[derive(Debug, Copy, Clone)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Step(pub calc::Step);
15
16impl Step {
17    /// Return the value in steps.
18    #[inline]
19    pub fn step(self) -> calc::Step {
20        let Step(step) = self;
21        step
22    }
23
24    /// Return the unit value of the equivalent frequency Hz.
25    #[inline]
26    pub fn hz(self) -> calc::Hz {
27        let Step(step) = self;
28        hz_from_step(step)
29    }
30
31    /// Convert to the equivalent frequency in Hz.
32    #[inline]
33    pub fn to_hz(self) -> Hz {
34        Hz(self.hz())
35    }
36
37    /// Convert to the closest equivalent (Letter, Octave).
38    #[inline]
39    pub fn letter_octave(self) -> (Letter, Octave) {
40        letter_octave_from_step(self.step())
41    }
42
43    /// Convert to the closest equivalent Letter.
44    #[inline]
45    pub fn letter(self) -> Letter {
46        let (letter, _) = self.letter_octave();
47        letter
48    }
49
50    /// Convert to the closest equivalent Octave.
51    #[inline]
52    pub fn octave(self) -> Octave {
53        let (_, octave) = self.letter_octave();
54        octave
55    }
56
57    /// Convert to the closest equivalent LetterOctave.
58    #[inline]
59    pub fn to_letter_octave(self) -> LetterOctave {
60        let (letter, octave) = self.letter_octave();
61        LetterOctave(letter, octave)
62    }
63
64    /// Convert to a Mel unit value.
65    #[inline]
66    pub fn mel(self) -> calc::Mel {
67        mel_from_step(self.step())
68    }
69
70    /// Convert to a Mel struct.
71    #[inline]
72    pub fn to_mel(self) -> Mel {
73        Mel(self.mel())
74    }
75
76    /// Convert to the unit value of the equivalent Perc.
77    #[inline]
78    pub fn perc(self) -> calc::Perc {
79        perc_from_step(self.step())
80    }
81
82    /// Convert to a percentage of the human hearing range.
83    #[inline]
84    pub fn to_perc(self) -> Perc {
85        Perc(self.perc())
86    }
87
88    /// Convert to a scaled percentage of the human hearing range with a given weight.
89    #[inline]
90    pub fn scaled_perc_with_weight(self, weight: ScaleWeight) -> calc::Perc {
91        scaled_perc_from_step(self.step(), weight)
92    }
93
94    /// Convert to a scaled percentage of the human hearing range.
95    #[inline]
96    pub fn scaled_perc(self) -> calc::Perc {
97        self.scaled_perc_with_weight(DEFAULT_SCALE_WEIGHT)
98    }
99
100    /// Convert to a scaled percentage of the human hearing range with a given weight.
101    #[inline]
102    pub fn to_scaled_perc_with_weight(self, weight: ScaleWeight) -> ScaledPerc {
103        ScaledPerc(self.scaled_perc_with_weight(weight), weight)
104    }
105
106    /// Convert to a scaled percentage of the human hearing range.
107    #[inline]
108    pub fn to_scaled_perc(self) -> ScaledPerc {
109        self.to_scaled_perc_with_weight(DEFAULT_SCALE_WEIGHT)
110    }
111}
112
113impl Add for Step {
114    type Output = Step;
115    #[inline]
116    fn add(self, rhs: Step) -> Step {
117        Step(self.step() + rhs.step())
118    }
119}
120
121impl Sub for Step {
122    type Output = Step;
123    #[inline]
124    fn sub(self, rhs: Step) -> Step {
125        Step(self.step() - rhs.step())
126    }
127}
128
129impl Mul for Step {
130    type Output = Step;
131    #[inline]
132    fn mul(self, rhs: Step) -> Step {
133        Step(self.step() * rhs.step())
134    }
135}
136
137impl Div for Step {
138    type Output = Step;
139    #[inline]
140    fn div(self, rhs: Step) -> Step {
141        Step(self.step() / rhs.step())
142    }
143}
144
145impl Rem for Step {
146    type Output = Step;
147    #[inline]
148    fn rem(self, rhs: Step) -> Step {
149        Step(self.step() % rhs.step())
150    }
151}
152
153impl Neg for Step {
154    type Output = Step;
155    #[inline]
156    fn neg(self) -> Step {
157        Step(-self.step())
158    }
159}
160
161impl PartialEq for Step {
162    #[inline]
163    fn eq(&self, other: &Step) -> bool {
164        self.step() == other.step()
165    }
166}
167
168impl Eq for Step {}
169
170impl PartialOrd for Step {
171    #[inline]
172    fn partial_cmp(&self, other: &Step) -> Option<Ordering> {
173        self.step().partial_cmp(&other.step())
174    }
175}
176
177impl Ord for Step {
178    #[inline]
179    fn cmp(&self, other: &Step) -> Ordering {
180        self.partial_cmp(other).unwrap()
181    }
182}
183
184impl<T> From<T> for Step
185where
186    T: Into<f32>,
187{
188    fn from(t: T) -> Step {
189        Step(t.into())
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::Step;
196
197    macro_rules! t {
198        (
199            $($x:ty),+
200        ) => {
201            fn from(val: f32) {
202                $(
203                    assert_eq!(Step::from(val as $x), Step(val));
204                )*
205            }
206
207            #[test]
208            fn test_from_integer() {
209                from(0.0);
210                from(60.0);
211                from(127.0);
212            }
213
214            fn into(val: f32) {
215                $(
216                    let actual: Step = (val as $x).into();
217                    assert_eq!(actual, Step(val));
218                )*
219            }
220
221            #[test]
222            fn test_into_integer() {
223                into(0.0);
224                into(60.0);
225                into(127.0);
226            }
227        };
228    }
229
230    t!(u8, u16, i8, i16);
231}