rstmt_core/pitch/
pitch.rs

1/*
2    Appellation: pitch <module>
3    Contrib: FL03 <jo3mccain@icloud.com>
4*/
5use super::{PitchTy, Pitches};
6use crate::PyMod;
7
8/// A [pitch](Pitch) is a discrete tone with an individual frequency that may be
9/// classified as a [pitch class](Pitches).
10#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
11#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
12#[repr(transparent)]
13pub struct Pitch(pub PitchTy);
14
15impl Pitch {
16    pub const MOD: PitchTy = crate::MODULUS;
17
18    pub fn new(pitch: PitchTy) -> Self {
19        Self(pitch)
20    }
21    /// Consumes the object, returning the assigned pitch value.
22    pub fn into_inner(self) -> PitchTy {
23        self.0
24    }
25    /// A functional accessor for the pitch value.
26    pub const fn get(&self) -> &PitchTy {
27        &self.0
28    }
29    /// returns a mutable reference to the inner value of the pitch;
30    pub fn get_mut(&mut self) -> &mut PitchTy {
31        &mut self.0
32    }
33    /// A functional mutator for the pitch value.
34    pub fn set(&mut self, val: PitchTy) {
35        self.0 = val;
36    }
37}
38
39impl Pitch {
40    /// Returns a new instance of the class representing the given pitch.
41    pub fn class(&self) -> Pitches {
42        Pitches::try_from_value(self.0.pymod(Self::MOD).abs()).unwrap()
43    }
44    /// Consumes the pitch; returns a new instance of the class representing the given pitch.
45    pub fn into_class(self) -> Pitches {
46        self.class()
47    }
48    ///
49    pub fn map<F>(self, f: F) -> Self
50    where
51        F: Fn(PitchTy) -> PitchTy,
52    {
53        Self(f(self.0))
54    }
55
56    pub fn map_mut<F>(&mut self, mut f: F)
57    where
58        F: FnMut(&mut PitchTy),
59    {
60        f(&mut self.0)
61    }
62    /// Consumes the current instance and applies the given function to the pitch value, returning
63    /// a new instance of [Pitch].
64    pub fn map_once<F>(self, f: F) -> Self
65    where
66        F: FnOnce(PitchTy) -> PitchTy,
67    {
68        Self(f(self.0))
69    }
70    /// Consumes the current instance and computes the absolute value of the pitch, returning
71    /// a new instance of [Pitch].
72    pub fn abs(self) -> Self {
73        self.map(|p| p.abs())
74    }
75    /// Returns the absolute value of the remainder of the pitch divided by the modulus.
76    pub fn absmod(self) -> Self {
77        self.map(|p| p.pymod(Self::MOD).abs())
78    }
79    /// The [`octmod`](Pitch::octmod) method computes the modulus of the current pitch using
80    /// Python's modulo operator, `%`. This method is useful for pitch arithmetic due to its
81    /// unique sign-handling behavior.
82    pub fn pymod(self) -> Self {
83        self.map(|p| p.pymod(Self::MOD))
84    }
85}
86
87impl AsRef<PitchTy> for Pitch {
88    fn as_ref(&self) -> &PitchTy {
89        &self.0
90    }
91}
92
93impl AsMut<PitchTy> for Pitch {
94    fn as_mut(&mut self) -> &mut PitchTy {
95        &mut self.0
96    }
97}
98
99impl core::borrow::Borrow<PitchTy> for Pitch {
100    fn borrow(&self) -> &PitchTy {
101        &self.0
102    }
103}
104
105impl core::borrow::BorrowMut<PitchTy> for Pitch {
106    fn borrow_mut(&mut self) -> &mut PitchTy {
107        &mut self.0
108    }
109}
110
111impl Default for Pitch {
112    fn default() -> Self {
113        Self(super::Natural::default().pitch())
114    }
115}
116
117impl core::ops::Deref for Pitch {
118    type Target = PitchTy;
119
120    fn deref(&self) -> &Self::Target {
121        self.as_ref()
122    }
123}
124
125impl core::ops::DerefMut for Pitch {
126    fn deref_mut(&mut self) -> &mut Self::Target {
127        self.as_mut()
128    }
129}
130
131impl core::fmt::Debug for Pitch {
132    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
133        f.debug_tuple(&self.class().to_string())
134            .field(&self.0)
135            .finish()
136    }
137}
138
139impl core::fmt::Display for Pitch {
140    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
141        write!(f, "{}({})", self.class(), self.0)
142    }
143}
144macro_rules! impl_fmt {
145    (@impl $trait:ident) => {
146        impl ::core::fmt::$trait for Pitch {
147            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
148                ::core::fmt::$trait::fmt(&self.0, f)
149            }
150        }
151    };
152    ($($trait:ident),* $(,)?) => {
153        $(
154            impl_fmt!(@impl $trait);
155        )*
156    };
157}
158
159impl_fmt!(Binary, LowerHex, Octal, UpperHex);
160
161impl From<PitchTy> for Pitch {
162    fn from(pitch: PitchTy) -> Self {
163        Self(pitch)
164    }
165}
166
167impl From<Pitch> for PitchTy {
168    fn from(pitch: Pitch) -> Self {
169        pitch.0
170    }
171}
172
173impl From<Pitches> for Pitch {
174    fn from(pitch: Pitches) -> Self {
175        Self(pitch.value())
176    }
177}
178
179impl From<Pitch> for Pitches {
180    fn from(pitch: Pitch) -> Self {
181        pitch.class()
182    }
183}