1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4pub const DEFAULT_AMP_EPSILON: f32 = 0.00001;
5pub const DEFAULT_DB_EPSILON: f32 = -100.0;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
9#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
10pub enum Volume {
11 Linear(f32),
13 Decibels(f32),
15}
16
17impl Volume {
18 pub const UNITY_GAIN: Self = Self::Linear(1.0);
20 pub const SILENT: Self = Self::Linear(0.0);
22
23 pub fn amp(&self) -> f32 {
25 match *self {
26 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, 0.0),
27 Self::Decibels(db) => db_to_amp(db),
28 }
29 }
30
31 pub fn amp_clamped(&self, amp_epsilon: f32) -> f32 {
35 match *self {
36 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, amp_epsilon),
37 Self::Decibels(db) => {
38 if db == f32::NEG_INFINITY {
39 0.0
40 } else {
41 let amp = db_to_amp(db);
42 if amp <= amp_epsilon {
43 0.0
44 } else {
45 amp
46 }
47 }
48 }
49 }
50 }
51
52 pub fn decibels(&self) -> f32 {
54 match *self {
55 Self::Linear(volume) => {
56 if volume == 0.0 {
57 f32::NEG_INFINITY
58 } else {
59 let amp = linear_volume_to_amp_clamped(volume, 0.0);
60 amp_to_db(amp)
61 }
62 }
63 Self::Decibels(db) => db,
64 }
65 }
66
67 pub fn decibels_clamped(&self, db_epsilon: f32) -> f32 {
72 match *self {
73 Self::Linear(volume) => {
74 if volume == 0.0 {
75 f32::NEG_INFINITY
76 } else {
77 let amp = linear_volume_to_amp_clamped(volume, 0.0);
78 let db = amp_to_db(amp);
79 if db <= db_epsilon {
80 f32::NEG_INFINITY
81 } else {
82 db
83 }
84 }
85 }
86 Self::Decibels(db) => {
87 if db <= db_epsilon {
88 f32::NEG_INFINITY
89 } else {
90 db
91 }
92 }
93 }
94 }
95
96 pub fn linear(&self) -> f32 {
98 match *self {
99 Self::Linear(volume) => volume,
100 Self::Decibels(db) => amp_to_linear_volume_clamped(db_to_amp(db), 0.0),
101 }
102 }
103
104 pub fn as_linear_variant(&self) -> Self {
106 Self::Linear(self.linear())
107 }
108
109 pub fn as_decibel_variant(&self) -> Self {
111 Self::Decibels(self.decibels())
112 }
113}
114
115impl Default for Volume {
116 fn default() -> Self {
117 Self::UNITY_GAIN
118 }
119}
120
121impl core::ops::Add<Self> for Volume {
122 type Output = Self;
123
124 fn add(self, rhs: Self) -> Self {
125 use Volume::{Decibels, Linear};
126
127 match (self, rhs) {
128 (Linear(a), Linear(b)) => Linear(a + b),
129 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) + db_to_amp(b))),
130 (Linear(..), Decibels(..)) => self + rhs.as_linear_variant(),
134 (Decibels(..), Linear(..)) => self + rhs.as_decibel_variant(),
135 }
136 }
137}
138
139impl core::ops::Sub<Self> for Volume {
140 type Output = Self;
141
142 fn sub(self, rhs: Self) -> Self {
143 use Volume::{Decibels, Linear};
144
145 match (self, rhs) {
146 (Linear(a), Linear(b)) => Linear(a - b),
147 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) - db_to_amp(b))),
148 (Linear(..), Decibels(..)) => self - rhs.as_linear_variant(),
152 (Decibels(..), Linear(..)) => self - rhs.as_decibel_variant(),
153 }
154 }
155}
156
157impl core::ops::Mul<Self> for Volume {
158 type Output = Self;
159
160 fn mul(self, rhs: Self) -> Self {
161 use Volume::{Decibels, Linear};
162
163 match (self, rhs) {
164 (Linear(a), Linear(b)) => Linear(a * b),
165 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) * db_to_amp(b))),
166 (Linear(..), Decibels(..)) => self * rhs.as_linear_variant(),
170 (Decibels(..), Linear(..)) => self * rhs.as_decibel_variant(),
171 }
172 }
173}
174
175impl core::ops::Div<Self> for Volume {
176 type Output = Self;
177
178 fn div(self, rhs: Self) -> Self {
179 use Volume::{Decibels, Linear};
180
181 match (self, rhs) {
182 (Linear(a), Linear(b)) => Linear(a / b),
183 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) / db_to_amp(b))),
184 (Linear(..), Decibels(..)) => self / rhs.as_linear_variant(),
188 (Decibels(..), Linear(..)) => self / rhs.as_decibel_variant(),
189 }
190 }
191}
192
193impl core::ops::AddAssign<Self> for Volume {
194 fn add_assign(&mut self, rhs: Self) {
195 *self = *self + rhs;
196 }
197}
198
199impl core::ops::SubAssign<Self> for Volume {
200 fn sub_assign(&mut self, rhs: Self) {
201 *self = *self - rhs;
202 }
203}
204
205impl core::ops::MulAssign<Self> for Volume {
206 fn mul_assign(&mut self, rhs: Self) {
207 *self = *self * rhs;
208 }
209}
210
211impl core::ops::DivAssign<Self> for Volume {
212 fn div_assign(&mut self, rhs: Self) {
213 *self = *self / rhs;
214 }
215}
216
217#[inline]
219pub fn db_to_amp(db: f32) -> f32 {
220 if db == f32::NEG_INFINITY {
221 0.0
222 } else {
223 10.0f32.powf(0.05 * db)
224 }
225}
226
227#[inline]
229pub fn amp_to_db(amp: f32) -> f32 {
230 if amp == 0.0 {
231 f32::NEG_INFINITY
232 } else {
233 20.0 * amp.log10()
234 }
235}
236
237#[inline]
242pub fn db_to_amp_clamped(db: f32, db_epsilon: f32) -> f32 {
243 if db == f32::NEG_INFINITY || db <= db_epsilon {
244 0.0
245 } else {
246 db_to_amp(db)
247 }
248}
249
250#[inline]
254pub fn amp_to_db_clamped(amp: f32, amp_epsilon: f32) -> f32 {
255 if amp <= amp_epsilon {
256 f32::NEG_INFINITY
257 } else {
258 amp_to_db(amp)
259 }
260}
261
262#[inline]
269pub fn linear_volume_to_amp_clamped(linear_volume: f32, amp_epsilon: f32) -> f32 {
270 let v = linear_volume * linear_volume;
271 if v <= amp_epsilon {
272 0.0
273 } else {
274 v
275 }
276}
277
278#[inline]
284pub fn amp_to_linear_volume_clamped(amp: f32, amp_epsilon: f32) -> f32 {
285 if amp <= amp_epsilon {
286 0.0
287 } else {
288 amp.sqrt()
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq)]
295pub struct DbMeterNormalizer {
296 min_db: f32,
297 range_recip: f32,
298 factor: f32,
299}
300
301impl DbMeterNormalizer {
302 pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
308 assert!(max_db > min_db);
309 assert!(center_db > min_db && center_db < max_db);
310
311 let range_recip = (max_db - min_db).recip();
312 let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
313
314 Self {
315 min_db,
316 range_recip,
317 factor: 0.5_f32.log(center_normalized),
318 }
319 }
320
321 #[inline]
322 pub fn normalize(&self, db: f32) -> f32 {
323 ((db - self.min_db) * self.range_recip)
324 .clamp(0.0, 1.0)
325 .powf(self.factor)
326 }
327}
328
329impl Default for DbMeterNormalizer {
330 fn default() -> Self {
331 Self::new(-100.0, 0.0, -22.0)
332 }
333}