eurorack_oxide_utils/
voct.rs

1//! V/Oct conversion methods.
2
3use core::ops;
4#[allow(unused_imports)]
5use micromath::F32Ext;
6
7/// Trait to handle voltage per octave conversions.
8pub trait Voltage {
9    /// Voltage to Hertz.
10    fn hz(&self) -> f32;
11
12    /// Voltage to millisecond.
13    fn ms(&self) -> u32;
14
15    /// Voltage to microseconds.
16    fn us(&self) -> u32;
17}
18
19/// V/Oct type.
20#[derive(Debug)]
21pub struct VOct(pub f32);
22
23/// mV/Oct Type.
24#[derive(Debug)]
25pub struct MvOct(pub f32);
26
27const C1_FREQ: f32 = 32.703;
28
29impl Voltage for VOct {
30    /// Convert to Hz.
31    fn hz(&self) -> f32 {
32        C1_FREQ * 2_f32.powf(self.0 - 1.0)
33    }
34
35    /// Convert to ms.
36    fn ms(&self) -> u32 {
37        (1000.0 / self.hz()) as u32
38    }
39
40    /// Convert to ms.
41    fn us(&self) -> u32 {
42        (1000000.0 / self.hz()) as u32
43    }
44}
45
46impl Voltage for MvOct {
47    fn hz(&self) -> f32{
48        C1_FREQ * 2_f32.powf(self.0 / 1000.0 - 1.0)
49    }
50
51    fn ms(&self) -> u32{
52        (1000.0 / self.hz()) as u32
53    }
54
55    fn us(&self) -> u32{
56        (1000000.0 / self.hz()) as u32
57    }
58}
59
60impl From<MvOct> for VOct {
61    fn from(mv: MvOct) -> Self {
62        Self(mv.0 / 1000.0)
63    }
64}
65
66impl From<VOct> for MvOct {
67    fn from(mv: VOct) -> Self {
68        Self(mv.0 * 1000.0)
69    }
70}
71
72/// Macro to implement arithmetic operations (e.g. multiplication, division)
73/// for wrapper types.
74macro_rules! impl_arithmetic {
75    ($wrapper:ty, $wrapped:ty) => {
76        impl ops::Mul<$wrapped> for $wrapper {
77            type Output = Self;
78            fn mul(self, rhs: $wrapped) -> Self {
79                Self(self.0 * rhs)
80            }
81        }
82
83        impl ops::MulAssign<$wrapped> for $wrapper {
84            fn mul_assign(&mut self, rhs: $wrapped) {
85                self.0 *= rhs;
86            }
87        }
88
89        impl ops::Div<$wrapped> for $wrapper {
90            type Output = Self;
91            fn div(self, rhs: $wrapped) -> Self {
92                Self(self.0 / rhs)
93            }
94        }
95
96        impl ops::Div<$wrapper> for $wrapper {
97            type Output = $wrapped;
98            fn div(self, rhs: $wrapper) -> $wrapped {
99                self.0 / rhs.0
100            }
101        }
102
103        impl ops::DivAssign<$wrapped> for $wrapper {
104            fn div_assign(&mut self, rhs: $wrapped) {
105                self.0 /= rhs;
106            }
107        }
108
109        impl ops::Add<$wrapped> for $wrapper {
110            type Output = Self;
111            fn add(self, rhs: $wrapped) -> Self {
112                Self(self.0 + rhs)
113            }
114        }
115
116        impl ops::Sub<$wrapped> for $wrapper {
117            type Output = Self;
118            fn sub(self, rhs: $wrapped) -> Self {
119                Self(self.0 - rhs)
120            }
121        }
122
123        impl ops::AddAssign<$wrapped> for $wrapper {
124            fn add_assign(&mut self, rhs: $wrapped) {
125                self.0 += rhs;
126            }
127        }
128
129        impl ops::SubAssign<$wrapped> for $wrapper {
130            fn sub_assign(&mut self, rhs: $wrapped) {
131                self.0 -= rhs;
132            }
133        }
134    };
135}
136
137impl_arithmetic!(VOct, f32);
138impl_arithmetic!(MvOct, f32);