rstmt_core/freq/
impl_freq.rs

1/*
2    appellation: impl_freq <module>
3    authors: @FL03
4*/
5use super::{Frequency, RawFrequency};
6use crate::consts::A4_FREQUENCY;
7use crate::pitch::{PitchClass, RawAccidental, RawPitchClass};
8use crate::utils::{classify_freq_with_scale, compute_freq_of_pitch};
9use num_traits::{Float, FromPrimitive, ToPrimitive};
10use rstmt_traits::ClassifyBy;
11
12impl<T> Frequency<T>
13where
14    T: RawFrequency,
15{
16    /// returns a new instance of the [`Frequency`] wrapping the given value
17    pub const fn new(index: T) -> Self {
18        Frequency(index)
19    }
20    /// calculate the frequency (in hertz) of a given pitch class, using the formula:
21    ///
22    /// ```math
23    /// F=\gamma\cdot{2^\frac{n}{12}}
24    /// ```
25    pub fn from_class_with_scale<N>(note: N, root: T) -> Self
26    where
27        N: ToPrimitive,
28        T: Float + FromPrimitive,
29    {
30        let class = note.to_isize().unwrap();
31        Self(compute_freq_of_pitch(class, root))
32    }
33    /// a shorthand method for creating a new frequency from the given pitch class using A4 as
34    /// the base frequency
35    pub fn from_class_on_a4<N>(note: N) -> Self
36    where
37        N: ToPrimitive,
38        T: Float + FromPrimitive,
39    {
40        let root = <T>::from_f64(A4_FREQUENCY).unwrap();
41        Self::from_class_with_scale(note, root)
42    }
43    /// a method for directly converting an instance of the [`PitchClass`] into a frequency
44    pub fn from_pitch_class<P, K>(class: PitchClass<P, K>, root: T) -> Self
45    where
46        P: RawPitchClass<Tag = K>,
47        K: RawAccidental,
48        T: RawFrequency + Float + FromPrimitive,
49    {
50        let semitones = class.get().index();
51        Self::from_class_with_scale(semitones, root)
52    }
53    /// initializes a new frequency by capturing the result of the given function
54    pub fn init<F>(f: F) -> Self
55    where
56        F: FnOnce() -> T,
57    {
58        Frequency(f())
59    }
60    /// returns a new Frequency with the value of one
61    pub fn one() -> Self
62    where
63        T: num_traits::One,
64    {
65        Frequency::init(T::one)
66    }
67    /// returns a new Frequency with the value of zero
68    pub fn zero() -> Self
69    where
70        T: num_traits::Zero,
71    {
72        Frequency::init(T::zero)
73    }
74    /// returns a pointer to the inner value
75    pub const fn as_ptr(&self) -> *const T {
76        core::ptr::from_ref(self.get())
77    }
78    /// returns a mutable pointer to the inner value
79    pub const fn as_mut_ptr(&mut self) -> *mut T {
80        core::ptr::from_mut(self.get_mut())
81    }
82    #[inline]
83    /// consumes the index returning the inner value
84    pub fn value(self) -> T {
85        self.0
86    }
87    /// returns an immutable reference to the inner value
88    pub const fn get(&self) -> &T {
89        &self.0
90    }
91    /// returns a mutable reference to the inner value
92    pub const fn get_mut(&mut self) -> &mut T {
93        &mut self.0
94    }
95    #[inline]
96    /// apply a function to the inner value and returns a new Frequency wrapping the result
97    pub fn map<U, F>(self, f: F) -> Frequency<U>
98    where
99        F: FnOnce(T) -> U,
100    {
101        Frequency(f(self.value()))
102    }
103    #[inline]
104    /// apply a function to a reference of the current frequency, capturing the result in a
105    /// new instance
106    pub fn apply<U, F>(&self, mut f: F) -> Frequency<U>
107    where
108        F: FnMut(&T) -> U,
109    {
110        Frequency(f(self.get()))
111    }
112    /// applies the given function to m
113    pub fn apply_inplace<F>(&mut self, f: F) -> &mut Self
114    where
115        F: FnOnce(&mut T),
116    {
117        f(self.get_mut());
118        self
119    }
120    /// [`replace`](core::mem::replace) the inner value with the given index, returning the
121    /// previous frequency value
122    pub const fn replace(&mut self, index: T) -> T {
123        core::mem::replace(self.get_mut(), index)
124    }
125    #[inline]
126    /// update the current frequency
127    pub fn set(&mut self, index: T) {
128        *self.get_mut() = index
129    }
130    /// [`swap`](core::mem::swap) the values of two instances of the [`Frequency`] wrapper
131    pub const fn swap(&mut self, other: &mut Self) {
132        core::mem::swap(self.get_mut(), other.get_mut());
133    }
134    #[inline]
135    /// [`take`](core::mem::take) and return the current frequency, leaving the logical default
136    /// in its place.
137    pub fn take(&mut self) -> T
138    where
139        T: Default,
140    {
141        core::mem::take(self.get_mut())
142    }
143    #[inline]
144    /// consumes the current instance to create another with the given value
145    pub fn with<U>(self, other: U) -> Frequency<U> {
146        Frequency(other)
147    }
148    /// returns a new instance containing a reference to the inner value
149    pub const fn view(&self) -> Frequency<&T> {
150        Frequency(self.get())
151    }
152    /// returns a new instance containing a mutable reference to the inner value
153    pub const fn view_mut(&mut self) -> Frequency<&mut T> {
154        Frequency(self.get_mut())
155    }
156    /// Compute the pitch class of a frequency (in hertz), using the formula:
157    ///
158    /// ```math
159    /// n = 12\cdot\log_2(\frac{F}{\gamma})
160    /// ```
161    pub fn classify_by<Z>(&self, base: T) -> Z
162    where
163        Self: ClassifyBy<T, Output = Z>,
164    {
165        ClassifyBy::classify_by(self, base)
166    }
167    /// calculates the octave number of the frequency with respect to the given anchor
168    pub fn get_octave_with(&self, base: T) -> isize
169    where
170        T: Float + FromPrimitive,
171    {
172        self.classify_by(base) / 12
173    }
174    /// calculates the octave of the current frequency assuming A4 (440 Hz) as the reference.
175    pub fn get_octave(&self) -> isize
176    where
177        T: Float + FromPrimitive,
178    {
179        let root = <T>::from_f64(A4_FREQUENCY).unwrap();
180        self.get_octave_with(root)
181    }
182}
183
184impl<T> ClassifyBy<T> for Frequency<T>
185where
186    T: RawFrequency + Float + FromPrimitive,
187{
188    type Output = isize;
189
190    fn classify_by(&self, base: T) -> Self::Output {
191        classify_freq_with_scale(self.value(), base).expect("failed to classify frequency")
192    }
193}