firewheel_core/dsp/
volume.rs

1pub const DEFAULT_AMP_EPSILON: f32 = 0.00001;
2pub const DEFAULT_DB_EPSILON: f32 = -100.0;
3
4use crate::{
5    diff::{Diff, Patch},
6    event::ParamData,
7};
8
9/// A value representing a volume (gain) applied to an audio signal.
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum Volume {
12    /// Volume in a linear scale, where `0.0` is silence and `1.0` is unity gain.
13    Linear(f32),
14    /// Volume in decibels, where `0.0` is unity gain and `f32::NEG_INFINITY` is silence.
15    Decibels(f32),
16}
17
18impl Volume {
19    /// Unity gain (the resulting volume is the same as the source signal)
20    pub const UNITY_GAIN: Self = Self::Linear(1.0);
21    /// Silence
22    pub const SILENT: Self = Self::Linear(0.0);
23
24    /// Get the volume in raw amplitude for use in DSP.
25    pub fn amp(&self) -> f32 {
26        match *self {
27            Self::Linear(volume) => linear_volume_to_amp_clamped(volume, 0.0),
28            Self::Decibels(db) => db_to_amp(db),
29        }
30    }
31
32    /// Get the volume in raw amplitude for use in DSP.
33    ///
34    /// If the resulting amplitude is `<= amp_epsilon`, then `0.0` (silence) will be returned.
35    pub fn amp_clamped(&self, amp_epsilon: f32) -> f32 {
36        match *self {
37            Self::Linear(volume) => linear_volume_to_amp_clamped(volume, amp_epsilon),
38            Self::Decibels(db) => {
39                if db == f32::NEG_INFINITY {
40                    0.0
41                } else {
42                    let amp = db_to_amp(db);
43                    if amp <= amp_epsilon {
44                        0.0
45                    } else {
46                        amp
47                    }
48                }
49            }
50        }
51    }
52
53    /// Get the volume in decibles.
54    pub fn decibels(&self) -> f32 {
55        match *self {
56            Self::Linear(volume) => {
57                if volume == 0.0 {
58                    f32::NEG_INFINITY
59                } else {
60                    let amp = linear_volume_to_amp_clamped(volume, 0.0);
61                    amp_to_db(amp)
62                }
63            }
64            Self::Decibels(db) => db,
65        }
66    }
67
68    /// Get the volume in decibles.
69    ///
70    /// If the resulting decibel value is `<= db_epsilon`, then `f32::NEG_INFINITY` (silence)
71    /// will be returned.
72    pub fn decibels_clamped(&self, db_epsilon: f32) -> f32 {
73        match *self {
74            Self::Linear(volume) => {
75                if volume == 0.0 {
76                    f32::NEG_INFINITY
77                } else {
78                    let amp = linear_volume_to_amp_clamped(volume, 0.0);
79                    let db = amp_to_db(amp);
80                    if db <= db_epsilon {
81                        f32::NEG_INFINITY
82                    } else {
83                        db
84                    }
85                }
86            }
87            Self::Decibels(db) => {
88                if db <= db_epsilon {
89                    f32::NEG_INFINITY
90                } else {
91                    db
92                }
93            }
94        }
95    }
96
97    /// Get the volume in linear units, where `0.0` is silence and `1.0` is unity gain.
98    pub fn linear(&self) -> f32 {
99        match *self {
100            Self::Linear(volume) => volume,
101            Self::Decibels(db) => amp_to_linear_volume_clamped(db_to_amp(db), 0.0),
102        }
103    }
104
105    /// Get the value as a [`Volume::Linear`] value.
106    pub fn as_linear_variant(&self) -> Self {
107        Self::Linear(self.linear())
108    }
109
110    /// Get the value as a [`Volume::Decibels`] value.
111    pub fn as_decibel_variant(&self) -> Self {
112        Self::Decibels(self.decibels())
113    }
114}
115
116impl Default for Volume {
117    fn default() -> Self {
118        Self::UNITY_GAIN
119    }
120}
121
122impl Diff for Volume {
123    fn diff<E: crate::diff::EventQueue>(
124        &self,
125        baseline: &Self,
126        path: crate::diff::PathBuilder,
127        event_queue: &mut E,
128    ) {
129        if self != baseline {
130            event_queue.push_param(*self, path);
131        }
132    }
133}
134
135impl Patch for Volume {
136    type Patch = Self;
137
138    fn patch(data: &ParamData, _path: &[u32]) -> Result<Self::Patch, crate::diff::PatchError> {
139        data.try_into()
140    }
141
142    fn apply(&mut self, patch: Self::Patch) {
143        *self = patch;
144    }
145}
146
147impl core::ops::Add<Self> for Volume {
148    type Output = Self;
149
150    fn add(self, rhs: Self) -> Self {
151        use Volume::{Decibels, Linear};
152
153        match (self, rhs) {
154            (Linear(a), Linear(b)) => Linear(a + b),
155            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) + db_to_amp(b))),
156            // {Linear, Decibels} favors the left hand side of the operation by
157            // first converting the right hand side to the same type as the left
158            // hand side and then performing the operation.
159            (Linear(..), Decibels(..)) => self + rhs.as_linear_variant(),
160            (Decibels(..), Linear(..)) => self + rhs.as_decibel_variant(),
161        }
162    }
163}
164
165impl core::ops::Sub<Self> for Volume {
166    type Output = Self;
167
168    fn sub(self, rhs: Self) -> Self {
169        use Volume::{Decibels, Linear};
170
171        match (self, rhs) {
172            (Linear(a), Linear(b)) => Linear(a - b),
173            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) - db_to_amp(b))),
174            // {Linear, Decibels} favors the left hand side of the operation by
175            // first converting the right hand side to the same type as the left
176            // hand side and then performing the operation.
177            (Linear(..), Decibels(..)) => self - rhs.as_linear_variant(),
178            (Decibels(..), Linear(..)) => self - rhs.as_decibel_variant(),
179        }
180    }
181}
182
183impl core::ops::Mul<Self> for Volume {
184    type Output = Self;
185
186    fn mul(self, rhs: Self) -> Self {
187        use Volume::{Decibels, Linear};
188
189        match (self, rhs) {
190            (Linear(a), Linear(b)) => Linear(a * b),
191            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) * db_to_amp(b))),
192            // {Linear, Decibels} favors the left hand side of the operation by
193            // first converting the right hand side to the same type as the left
194            // hand side and then performing the operation.
195            (Linear(..), Decibels(..)) => self * rhs.as_linear_variant(),
196            (Decibels(..), Linear(..)) => self * rhs.as_decibel_variant(),
197        }
198    }
199}
200
201impl core::ops::Div<Self> for Volume {
202    type Output = Self;
203
204    fn div(self, rhs: Self) -> Self {
205        use Volume::{Decibels, Linear};
206
207        match (self, rhs) {
208            (Linear(a), Linear(b)) => Linear(a / b),
209            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) / db_to_amp(b))),
210            // {Linear, Decibels} favors the left hand side of the operation by
211            // first converting the right hand side to the same type as the left
212            // hand side and then performing the operation.
213            (Linear(..), Decibels(..)) => self / rhs.as_linear_variant(),
214            (Decibels(..), Linear(..)) => self / rhs.as_decibel_variant(),
215        }
216    }
217}
218
219impl core::ops::AddAssign<Self> for Volume {
220    fn add_assign(&mut self, rhs: Self) {
221        *self = *self + rhs;
222    }
223}
224
225impl core::ops::SubAssign<Self> for Volume {
226    fn sub_assign(&mut self, rhs: Self) {
227        *self = *self - rhs;
228    }
229}
230
231impl core::ops::MulAssign<Self> for Volume {
232    fn mul_assign(&mut self, rhs: Self) {
233        *self = *self * rhs;
234    }
235}
236
237impl core::ops::DivAssign<Self> for Volume {
238    fn div_assign(&mut self, rhs: Self) {
239        *self = *self / rhs;
240    }
241}
242
243/// Returns the raw amplitude from the given decibel value.
244#[inline]
245pub fn db_to_amp(db: f32) -> f32 {
246    if db == f32::NEG_INFINITY {
247        0.0
248    } else {
249        10.0f32.powf(0.05 * db)
250    }
251}
252
253/// Returns the decibel value from the given raw amplitude.
254#[inline]
255pub fn amp_to_db(amp: f32) -> f32 {
256    if amp == 0.0 {
257        f32::NEG_INFINITY
258    } else {
259        20.0 * amp.log10()
260    }
261}
262
263/// Returns the raw amplitude from the given decibel value.
264///
265/// If `db == f32::NEG_INFINITY || db <= db_epsilon`, then `0.0` (silence) will be
266/// returned.
267#[inline]
268pub fn db_to_amp_clamped(db: f32, db_epsilon: f32) -> f32 {
269    if db == f32::NEG_INFINITY || db <= db_epsilon {
270        0.0
271    } else {
272        db_to_amp(db)
273    }
274}
275
276/// Returns the decibel value from the given raw amplitude.
277///
278/// If `amp <= amp_epsilon`, then `f32::NEG_INFINITY` (silence) will be returned.
279#[inline]
280pub fn amp_to_db_clamped(amp: f32, amp_epsilon: f32) -> f32 {
281    if amp <= amp_epsilon {
282        f32::NEG_INFINITY
283    } else {
284        amp_to_db(amp)
285    }
286}
287
288/// Map the linear volume (where `0.0` means mute and `1.0` means unity
289/// gain) to the corresponding raw amplitude value (not decibels) for use in
290/// DSP. Values above `1.0` are allowed.
291///
292/// If the resulting amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
293/// returned.
294#[inline]
295pub fn linear_volume_to_amp_clamped(linear_volume: f32, amp_epsilon: f32) -> f32 {
296    let v = linear_volume * linear_volume;
297    if v <= amp_epsilon {
298        0.0
299    } else {
300        v
301    }
302}
303
304/// Map the raw amplitude (where `0.0` means mute and `1.0` means unity
305/// gain) to the corresponding linear volume.
306///
307/// If the amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
308/// returned.
309#[inline]
310pub fn amp_to_linear_volume_clamped(amp: f32, amp_epsilon: f32) -> f32 {
311    if amp <= amp_epsilon {
312        0.0
313    } else {
314        amp.sqrt()
315    }
316}
317
318/// A struct that converts a value in decibels to a normalized range used in
319/// meters.
320#[derive(Debug, Clone, Copy, PartialEq)]
321pub struct DbMeterNormalizer {
322    min_db: f32,
323    range_recip: f32,
324    factor: f32,
325}
326
327impl DbMeterNormalizer {
328    /// * `min_db` - The minimum decibel value shown in the meter.
329    /// * `max_db` - The maximum decibel value shown in the meter.
330    /// * `center_db` - The decibel value that will appear halfway (0.5) in the
331    /// normalized range. For example, if you had `min_db` as `-100.0` and
332    /// `max_db` as `0.0`, then a good `center_db` value would be `-22`.
333    pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
334        assert!(max_db > min_db);
335        assert!(center_db > min_db && center_db < max_db);
336
337        let range_recip = (max_db - min_db).recip();
338        let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
339
340        Self {
341            min_db,
342            range_recip,
343            factor: 0.5_f32.log(center_normalized),
344        }
345    }
346
347    #[inline]
348    pub fn normalize(&self, db: f32) -> f32 {
349        ((db - self.min_db) * self.range_recip)
350            .clamp(0.0, 1.0)
351            .powf(self.factor)
352    }
353}
354
355impl Default for DbMeterNormalizer {
356    fn default() -> Self {
357        Self::new(-100.0, 0.0, -22.0)
358    }
359}