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(ParamData::Volume(*self), path);
131        }
132    }
133}
134
135impl Patch for Volume {
136    fn patch(&mut self, data: &ParamData, _path: &[u32]) -> Result<(), crate::diff::PatchError> {
137        *self = data.try_into()?;
138        Ok(())
139    }
140}
141
142impl core::ops::Add<Self> for Volume {
143    type Output = Self;
144
145    fn add(self, rhs: Self) -> Self {
146        use Volume::{Decibels, Linear};
147
148        match (self, rhs) {
149            (Linear(a), Linear(b)) => Linear(a + b),
150            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) + db_to_amp(b))),
151            // {Linear, Decibels} favors the left hand side of the operation by
152            // first converting the right hand side to the same type as the left
153            // hand side and then performing the operation.
154            (Linear(..), Decibels(..)) => self + rhs.as_linear_variant(),
155            (Decibels(..), Linear(..)) => self + rhs.as_decibel_variant(),
156        }
157    }
158}
159
160impl core::ops::Sub<Self> for Volume {
161    type Output = Self;
162
163    fn sub(self, rhs: Self) -> Self {
164        use Volume::{Decibels, Linear};
165
166        match (self, rhs) {
167            (Linear(a), Linear(b)) => Linear(a - b),
168            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) - db_to_amp(b))),
169            // {Linear, Decibels} favors the left hand side of the operation by
170            // first converting the right hand side to the same type as the left
171            // hand side and then performing the operation.
172            (Linear(..), Decibels(..)) => self - rhs.as_linear_variant(),
173            (Decibels(..), Linear(..)) => self - rhs.as_decibel_variant(),
174        }
175    }
176}
177
178impl core::ops::Mul<Self> for Volume {
179    type Output = Self;
180
181    fn mul(self, rhs: Self) -> Self {
182        use Volume::{Decibels, Linear};
183
184        match (self, rhs) {
185            (Linear(a), Linear(b)) => Linear(a * b),
186            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) * db_to_amp(b))),
187            // {Linear, Decibels} favors the left hand side of the operation by
188            // first converting the right hand side to the same type as the left
189            // hand side and then performing the operation.
190            (Linear(..), Decibels(..)) => self * rhs.as_linear_variant(),
191            (Decibels(..), Linear(..)) => self * rhs.as_decibel_variant(),
192        }
193    }
194}
195
196impl core::ops::Div<Self> for Volume {
197    type Output = Self;
198
199    fn div(self, rhs: Self) -> Self {
200        use Volume::{Decibels, Linear};
201
202        match (self, rhs) {
203            (Linear(a), Linear(b)) => Linear(a / b),
204            (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) / db_to_amp(b))),
205            // {Linear, Decibels} favors the left hand side of the operation by
206            // first converting the right hand side to the same type as the left
207            // hand side and then performing the operation.
208            (Linear(..), Decibels(..)) => self / rhs.as_linear_variant(),
209            (Decibels(..), Linear(..)) => self / rhs.as_decibel_variant(),
210        }
211    }
212}
213
214impl core::ops::AddAssign<Self> for Volume {
215    fn add_assign(&mut self, rhs: Self) {
216        *self = *self + rhs;
217    }
218}
219
220impl core::ops::SubAssign<Self> for Volume {
221    fn sub_assign(&mut self, rhs: Self) {
222        *self = *self - rhs;
223    }
224}
225
226impl core::ops::MulAssign<Self> for Volume {
227    fn mul_assign(&mut self, rhs: Self) {
228        *self = *self * rhs;
229    }
230}
231
232impl core::ops::DivAssign<Self> for Volume {
233    fn div_assign(&mut self, rhs: Self) {
234        *self = *self / rhs;
235    }
236}
237
238/// Returns the raw amplitude from the given decibel value.
239#[inline]
240pub fn db_to_amp(db: f32) -> f32 {
241    if db == f32::NEG_INFINITY {
242        0.0
243    } else {
244        10.0f32.powf(0.05 * db)
245    }
246}
247
248/// Returns the decibel value from the given raw amplitude.
249#[inline]
250pub fn amp_to_db(amp: f32) -> f32 {
251    if amp == 0.0 {
252        f32::NEG_INFINITY
253    } else {
254        20.0 * amp.log10()
255    }
256}
257
258/// Returns the raw amplitude from the given decibel value.
259///
260/// If `db == f32::NEG_INFINITY || db <= db_epsilon`, then `0.0` (silence) will be
261/// returned.
262#[inline]
263pub fn db_to_amp_clamped(db: f32, db_epsilon: f32) -> f32 {
264    if db == f32::NEG_INFINITY || db <= db_epsilon {
265        0.0
266    } else {
267        db_to_amp(db)
268    }
269}
270
271/// Returns the decibel value from the given raw amplitude.
272///
273/// If `amp <= amp_epsilon`, then `f32::NEG_INFINITY` (silence) will be returned.
274#[inline]
275pub fn amp_to_db_clamped(amp: f32, amp_epsilon: f32) -> f32 {
276    if amp <= amp_epsilon {
277        f32::NEG_INFINITY
278    } else {
279        amp_to_db(amp)
280    }
281}
282
283/// Map the linear volume (where `0.0` means mute and `1.0` means unity
284/// gain) to the corresponding raw amplitude value (not decibels) for use in
285/// DSP. Values above `1.0` are allowed.
286///
287/// If the resulting amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
288/// returned.
289#[inline]
290pub fn linear_volume_to_amp_clamped(linear_volume: f32, amp_epsilon: f32) -> f32 {
291    let v = linear_volume * linear_volume;
292    if v <= amp_epsilon {
293        0.0
294    } else {
295        v
296    }
297}
298
299/// Map the raw amplitude (where `0.0` means mute and `1.0` means unity
300/// gain) to the corresponding linear volume.
301///
302/// If the amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
303/// returned.
304#[inline]
305pub fn amp_to_linear_volume_clamped(amp: f32, amp_epsilon: f32) -> f32 {
306    if amp <= amp_epsilon {
307        0.0
308    } else {
309        amp.sqrt()
310    }
311}
312
313/// A struct that converts a value in decibels to a normalized range used in
314/// meters.
315#[derive(Debug, Clone, Copy, PartialEq)]
316pub struct DbMeterNormalizer {
317    min_db: f32,
318    range_recip: f32,
319    factor: f32,
320}
321
322impl DbMeterNormalizer {
323    /// * `min_db` - The minimum decibel value shown in the meter.
324    /// * `max_db` - The maximum decibel value shown in the meter.
325    /// * `center_db` - The decibel value that will appear halfway (0.5) in the
326    /// normalized range. For example, if you had `min_db` as `-100.0` and
327    /// `max_db` as `0.0`, then a good `center_db` value would be `-22`.
328    pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
329        assert!(max_db > min_db);
330        assert!(center_db > min_db && center_db < max_db);
331
332        let range_recip = (max_db - min_db).recip();
333        let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
334
335        Self {
336            min_db,
337            range_recip,
338            factor: 0.5_f32.log(center_normalized),
339        }
340    }
341
342    #[inline]
343    pub fn normalize(&self, db: f32) -> f32 {
344        ((db - self.min_db) * self.range_recip)
345            .clamp(0.0, 1.0)
346            .powf(self.factor)
347    }
348}
349
350impl Default for DbMeterNormalizer {
351    fn default() -> Self {
352        Self::new(-100.0, 0.0, -22.0)
353    }
354}