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))]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub enum Volume {
12 Linear(f32),
17 Decibels(f32),
19}
20
21impl Volume {
22 pub const UNITY_GAIN: Self = Self::Linear(1.0);
24 pub const SILENT: Self = Self::Linear(0.0);
26
27 pub const fn from_percent(percent: f32) -> Self {
30 Self::Linear(percent / 100.0)
31 }
32
33 pub fn amp(&self) -> f32 {
35 match *self {
36 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, 0.0),
37 Self::Decibels(db) => db_to_amp(db),
38 }
39 }
40
41 pub fn amp_clamped(&self, amp_epsilon: f32) -> f32 {
45 match *self {
46 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, amp_epsilon),
47 Self::Decibels(db) => {
48 if db == f32::NEG_INFINITY {
49 0.0
50 } else {
51 let amp = db_to_amp(db);
52 if amp <= amp_epsilon {
53 0.0
54 } else {
55 amp
56 }
57 }
58 }
59 }
60 }
61
62 pub fn decibels(&self) -> f32 {
64 match *self {
65 Self::Linear(volume) => {
66 if volume == 0.0 {
67 f32::NEG_INFINITY
68 } else {
69 let amp = linear_volume_to_amp_clamped(volume, 0.0);
70 amp_to_db(amp)
71 }
72 }
73 Self::Decibels(db) => db,
74 }
75 }
76
77 pub fn decibels_clamped(&self, db_epsilon: f32) -> f32 {
82 match *self {
83 Self::Linear(volume) => {
84 if volume == 0.0 {
85 f32::NEG_INFINITY
86 } else {
87 let amp = linear_volume_to_amp_clamped(volume, 0.0);
88 let db = amp_to_db(amp);
89 if db <= db_epsilon {
90 f32::NEG_INFINITY
91 } else {
92 db
93 }
94 }
95 }
96 Self::Decibels(db) => {
97 if db <= db_epsilon {
98 f32::NEG_INFINITY
99 } else {
100 db
101 }
102 }
103 }
104 }
105
106 pub fn linear(&self) -> f32 {
108 match *self {
109 Self::Linear(volume) => volume,
110 Self::Decibels(db) => amp_to_linear_volume_clamped(db_to_amp(db), 0.0),
111 }
112 }
113
114 pub fn percent(&self) -> f32 {
117 self.linear() * 100.0
118 }
119
120 pub fn as_linear_variant(&self) -> Self {
122 Self::Linear(self.linear())
123 }
124
125 pub fn as_decibel_variant(&self) -> Self {
127 Self::Decibels(self.decibels())
128 }
129}
130
131impl Default for Volume {
132 fn default() -> Self {
133 Self::UNITY_GAIN
134 }
135}
136
137impl core::ops::Add<Self> for Volume {
138 type Output = Self;
139
140 fn add(self, rhs: Self) -> Self {
141 use Volume::{Decibels, Linear};
142
143 match (self, rhs) {
144 (Linear(a), Linear(b)) => Linear(a + b),
145 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) + db_to_amp(b))),
146 (Linear(..), Decibels(..)) => self + rhs.as_linear_variant(),
150 (Decibels(..), Linear(..)) => self + rhs.as_decibel_variant(),
151 }
152 }
153}
154
155impl core::ops::Sub<Self> for Volume {
156 type Output = Self;
157
158 fn sub(self, rhs: Self) -> Self {
159 use Volume::{Decibels, Linear};
160
161 match (self, rhs) {
162 (Linear(a), Linear(b)) => Linear(a - b),
163 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) - db_to_amp(b))),
164 (Linear(..), Decibels(..)) => self - rhs.as_linear_variant(),
168 (Decibels(..), Linear(..)) => self - rhs.as_decibel_variant(),
169 }
170 }
171}
172
173impl core::ops::Mul<Self> for Volume {
174 type Output = Self;
175
176 fn mul(self, rhs: Self) -> Self {
177 use Volume::{Decibels, Linear};
178
179 match (self, rhs) {
180 (Linear(a), Linear(b)) => Linear(a * b),
181 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) * db_to_amp(b))),
182 (Linear(..), Decibels(..)) => self * rhs.as_linear_variant(),
186 (Decibels(..), Linear(..)) => self * rhs.as_decibel_variant(),
187 }
188 }
189}
190
191impl core::ops::Div<Self> for Volume {
192 type Output = Self;
193
194 fn div(self, rhs: Self) -> Self {
195 use Volume::{Decibels, Linear};
196
197 match (self, rhs) {
198 (Linear(a), Linear(b)) => Linear(a / b),
199 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) / db_to_amp(b))),
200 (Linear(..), Decibels(..)) => self / rhs.as_linear_variant(),
204 (Decibels(..), Linear(..)) => self / rhs.as_decibel_variant(),
205 }
206 }
207}
208
209impl core::ops::AddAssign<Self> for Volume {
210 fn add_assign(&mut self, rhs: Self) {
211 *self = *self + rhs;
212 }
213}
214
215impl core::ops::SubAssign<Self> for Volume {
216 fn sub_assign(&mut self, rhs: Self) {
217 *self = *self - rhs;
218 }
219}
220
221impl core::ops::MulAssign<Self> for Volume {
222 fn mul_assign(&mut self, rhs: Self) {
223 *self = *self * rhs;
224 }
225}
226
227impl core::ops::DivAssign<Self> for Volume {
228 fn div_assign(&mut self, rhs: Self) {
229 *self = *self / rhs;
230 }
231}
232
233#[inline]
235pub fn db_to_amp(db: f32) -> f32 {
236 if db == f32::NEG_INFINITY {
237 0.0
238 } else {
239 10.0f32.powf(0.05 * db)
240 }
241}
242
243#[inline]
245pub fn amp_to_db(amp: f32) -> f32 {
246 if amp == 0.0 {
247 f32::NEG_INFINITY
248 } else {
249 20.0 * amp.log10()
250 }
251}
252
253#[inline]
258pub fn db_to_amp_clamped(db: f32, db_epsilon: f32) -> f32 {
259 if db == f32::NEG_INFINITY || db <= db_epsilon {
260 0.0
261 } else {
262 db_to_amp(db)
263 }
264}
265
266#[inline]
270pub fn amp_to_db_clamped(amp: f32, amp_epsilon: f32) -> f32 {
271 if amp <= amp_epsilon {
272 f32::NEG_INFINITY
273 } else {
274 amp_to_db(amp)
275 }
276}
277
278#[inline]
285pub fn linear_volume_to_amp_clamped(linear_volume: f32, amp_epsilon: f32) -> f32 {
286 let v = linear_volume * linear_volume;
287 if v <= amp_epsilon {
288 0.0
289 } else {
290 v
291 }
292}
293
294#[inline]
300pub fn amp_to_linear_volume_clamped(amp: f32, amp_epsilon: f32) -> f32 {
301 if amp <= amp_epsilon {
302 0.0
303 } else {
304 amp.sqrt()
305 }
306}
307
308#[derive(Debug, Clone, Copy, PartialEq)]
311pub struct DbMeterNormalizer {
312 min_db: f32,
313 range_recip: f32,
314 factor: f32,
315}
316
317impl DbMeterNormalizer {
318 pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
324 assert!(max_db > min_db);
325 assert!(center_db > min_db && center_db < max_db);
326
327 let range_recip = (max_db - min_db).recip();
328 let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
329
330 Self {
331 min_db,
332 range_recip,
333 factor: 0.5_f32.log(center_normalized),
334 }
335 }
336
337 #[inline]
338 pub fn normalize(&self, db: f32) -> f32 {
339 ((db - self.min_db) * self.range_recip)
340 .clamp(0.0, 1.0)
341 .powf(self.factor)
342 }
343}
344
345impl Default for DbMeterNormalizer {
346 fn default() -> Self {
347 Self::new(-100.0, 0.0, -22.0)
348 }
349}
350
351pub fn is_buffer_silent(buffer: &[f32], amp_epsilon: f32) -> bool {
354 let mut silent = true;
355 for &s in buffer.iter() {
356 if s.abs() > amp_epsilon {
357 silent = false;
358 break;
359 }
360 }
361 silent
362}