1pub const MINUS_INFINITY_DECIBELS: f64 = -128.0;
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd)]
5pub struct Level {
6 linear: f64,
7}
8
9impl Level {
10 pub fn unity() -> Self {
12 Level::from_linear(1.0)
13 }
14
15 pub fn zero() -> Self {
17 Level::from_linear(0.0)
18 }
19
20 pub fn from_db(level_in_db: f64) -> Self {
22 if level_in_db <= MINUS_INFINITY_DECIBELS {
23 Self { linear: 0.0 }
24 } else {
25 Self {
26 linear: 10.0_f64.powf(level_in_db / 20.0),
27 }
28 }
29 }
30
31 pub fn from_db_f32(level_in_db: f32) -> Self {
33 Self::from_db(level_in_db as f64)
34 }
35
36 pub fn as_db(&self) -> f64 {
38 if self.linear <= 1e-9 {
39 MINUS_INFINITY_DECIBELS
40 } else {
41 20.0 * self.linear.log10()
42 }
43 }
44
45 pub fn from_linear(linear_gain: f64) -> Self {
47 Self {
48 linear: linear_gain,
49 }
50 }
51
52 pub fn as_linear(&self) -> f64 {
54 self.linear
55 }
56
57 pub fn as_linear_f32(&self) -> f32 {
59 self.linear as f32
60 }
61
62 pub fn clamp(&self, min_value: &Self, max_value: &Self) -> Self {
64 Self {
65 linear: self.linear.clamp(min_value.linear, max_value.linear),
66 }
67 }
68
69 pub fn is_zero(&self) -> bool {
71 relative_eq!(self.linear, 0.0)
72 }
73
74 pub fn is_unity(&self) -> bool {
76 relative_eq!(self.linear, 1.0)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use approx::assert_relative_eq;
83
84 use super::*;
85
86 #[test]
87 fn db_to_linear() {
88 let epsilon = 1e-2;
89 assert_relative_eq!(Level::from_db(0.0).as_linear(), 1.0, epsilon = epsilon);
90 assert_relative_eq!(Level::from_db(-6.0).as_linear(), 0.5, epsilon = epsilon);
91 assert_relative_eq!(Level::from_db(-12.0).as_linear(), 0.25, epsilon = epsilon);
92 assert_relative_eq!(Level::from_db(-200.0).as_linear(), 0.0, epsilon = epsilon);
93 }
94
95 #[test]
96 fn linear_to_db() {
97 let epsilon = 0.1;
98 assert_relative_eq!(Level::from_linear(1.0).as_db(), 0.0, epsilon = epsilon);
99 assert_relative_eq!(Level::from_linear(0.5).as_db(), -6.0, epsilon = epsilon);
100 assert_relative_eq!(Level::from_linear(0.25).as_db(), -12.0, epsilon = epsilon);
101 assert_relative_eq!(
102 Level::from_linear(0.0).as_db(),
103 MINUS_INFINITY_DECIBELS,
104 epsilon = epsilon
105 );
106 }
107}