1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4pub const DEFAULT_MIN_AMP: f32 = 0.0001;
10pub const DEFAULT_MIN_DB: f32 = -80.0;
15
16#[derive(Debug, Clone, Copy, PartialEq)]
18#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub enum Volume {
21 Linear(f32),
26 Decibels(f32),
28}
29
30impl Volume {
31 pub const UNITY_GAIN: Self = Self::Linear(1.0);
33 pub const SILENT: Self = Self::Linear(0.0);
35
36 pub const fn from_percent(percent: f32) -> Self {
39 Self::Linear(percent / 100.0)
40 }
41
42 pub fn amp(&self) -> f32 {
44 match *self {
45 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, 0.0),
46 Self::Decibels(db) => db_to_amp(db),
47 }
48 }
49
50 pub fn amp_clamped(&self, min_amp: f32) -> f32 {
54 match *self {
55 Self::Linear(volume) => linear_volume_to_amp_clamped(volume, min_amp),
56 Self::Decibels(db) => {
57 if db == f32::NEG_INFINITY {
58 0.0
59 } else {
60 let amp = db_to_amp(db);
61 if amp <= min_amp { 0.0 } else { amp }
62 }
63 }
64 }
65 }
66
67 pub fn decibels(&self) -> f32 {
69 match *self {
70 Self::Linear(volume) => {
71 if volume == 0.0 {
72 f32::NEG_INFINITY
73 } else {
74 let amp = linear_volume_to_amp_clamped(volume, 0.0);
75 amp_to_db(amp)
76 }
77 }
78 Self::Decibels(db) => db,
79 }
80 }
81
82 pub fn decibels_clamped(&self, min_db: f32) -> f32 {
87 match *self {
88 Self::Linear(volume) => {
89 if volume == 0.0 {
90 f32::NEG_INFINITY
91 } else {
92 let amp = linear_volume_to_amp_clamped(volume, 0.0);
93 let db = amp_to_db(amp);
94 if db <= min_db { f32::NEG_INFINITY } else { db }
95 }
96 }
97 Self::Decibels(db) => {
98 if db <= min_db {
99 f32::NEG_INFINITY
100 } else {
101 db
102 }
103 }
104 }
105 }
106
107 pub fn linear(&self) -> f32 {
109 match *self {
110 Self::Linear(volume) => volume,
111 Self::Decibels(db) => amp_to_linear_volume_clamped(db_to_amp(db), 0.0),
112 }
113 }
114
115 pub fn percent(&self) -> f32 {
118 self.linear() * 100.0
119 }
120
121 pub fn as_linear_variant(&self) -> Self {
123 Self::Linear(self.linear())
124 }
125
126 pub fn as_decibel_variant(&self) -> Self {
128 Self::Decibels(self.decibels())
129 }
130}
131
132impl Default for Volume {
133 fn default() -> Self {
134 Self::UNITY_GAIN
135 }
136}
137
138impl core::ops::Add<Self> for Volume {
139 type Output = Self;
140
141 fn add(self, rhs: Self) -> Self {
142 use Volume::{Decibels, Linear};
143
144 match (self, rhs) {
145 (Linear(a), Linear(b)) => Linear(a + b),
146 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) + db_to_amp(b))),
147 (Linear(..), Decibels(..)) => self + rhs.as_linear_variant(),
151 (Decibels(..), Linear(..)) => self + rhs.as_decibel_variant(),
152 }
153 }
154}
155
156impl core::ops::Sub<Self> for Volume {
157 type Output = Self;
158
159 fn sub(self, rhs: Self) -> Self {
160 use Volume::{Decibels, Linear};
161
162 match (self, rhs) {
163 (Linear(a), Linear(b)) => Linear(a - b),
164 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) - db_to_amp(b))),
165 (Linear(..), Decibels(..)) => self - rhs.as_linear_variant(),
169 (Decibels(..), Linear(..)) => self - rhs.as_decibel_variant(),
170 }
171 }
172}
173
174impl core::ops::Mul<Self> for Volume {
175 type Output = Self;
176
177 fn mul(self, rhs: Self) -> Self {
178 use Volume::{Decibels, Linear};
179
180 match (self, rhs) {
181 (Linear(a), Linear(b)) => Linear(a * b),
182 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) * db_to_amp(b))),
183 (Linear(..), Decibels(..)) => self * rhs.as_linear_variant(),
187 (Decibels(..), Linear(..)) => self * rhs.as_decibel_variant(),
188 }
189 }
190}
191
192impl core::ops::Div<Self> for Volume {
193 type Output = Self;
194
195 fn div(self, rhs: Self) -> Self {
196 use Volume::{Decibels, Linear};
197
198 match (self, rhs) {
199 (Linear(a), Linear(b)) => Linear(a / b),
200 (Decibels(a), Decibels(b)) => Decibels(amp_to_db(db_to_amp(a) / db_to_amp(b))),
201 (Linear(..), Decibels(..)) => self / rhs.as_linear_variant(),
205 (Decibels(..), Linear(..)) => self / rhs.as_decibel_variant(),
206 }
207 }
208}
209
210impl core::ops::AddAssign<Self> for Volume {
211 fn add_assign(&mut self, rhs: Self) {
212 *self = *self + rhs;
213 }
214}
215
216impl core::ops::SubAssign<Self> for Volume {
217 fn sub_assign(&mut self, rhs: Self) {
218 *self = *self - rhs;
219 }
220}
221
222impl core::ops::MulAssign<Self> for Volume {
223 fn mul_assign(&mut self, rhs: Self) {
224 *self = *self * rhs;
225 }
226}
227
228impl core::ops::DivAssign<Self> for Volume {
229 fn div_assign(&mut self, rhs: Self) {
230 *self = *self / rhs;
231 }
232}
233
234#[inline]
236pub fn db_to_amp(db: f32) -> f32 {
237 if db == f32::NEG_INFINITY {
238 0.0
239 } else {
240 10.0f32.powf(0.05 * db)
241 }
242}
243
244#[inline]
246pub fn amp_to_db(amp: f32) -> f32 {
247 if amp == 0.0 {
248 f32::NEG_INFINITY
249 } else {
250 20.0 * amp.log10()
251 }
252}
253
254#[inline]
259pub fn db_to_amp_clamped(db: f32, min_db: f32) -> f32 {
260 if db == f32::NEG_INFINITY || db <= min_db {
261 0.0
262 } else {
263 db_to_amp(db)
264 }
265}
266
267#[inline]
271pub fn amp_to_db_clamped(amp: f32, min_amp: f32) -> f32 {
272 if amp <= min_amp {
273 f32::NEG_INFINITY
274 } else {
275 amp_to_db(amp)
276 }
277}
278
279#[inline]
286pub fn linear_volume_to_amp_clamped(linear_volume: f32, min_amp: f32) -> f32 {
287 let v = linear_volume * linear_volume;
288 if v <= min_amp { 0.0 } else { v }
289}
290
291#[inline]
297pub fn amp_to_linear_volume_clamped(amp: f32, min_amp: f32) -> f32 {
298 if amp <= min_amp { 0.0 } else { amp.sqrt() }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq)]
304pub struct DbMeterNormalizer {
305 min_db: f32,
306 range_recip: f32,
307 factor: f32,
308}
309
310impl DbMeterNormalizer {
311 pub fn new(min_db: f32, max_db: f32, center_db: f32) -> Self {
317 assert!(max_db > min_db);
318 assert!(center_db > min_db && center_db < max_db);
319
320 let range_recip = (max_db - min_db).recip();
321 let center_normalized = ((center_db - min_db) * range_recip).clamp(0.0, 1.0);
322
323 Self {
324 min_db,
325 range_recip,
326 factor: 0.5_f32.log(center_normalized),
327 }
328 }
329
330 #[inline]
331 pub fn normalize(&self, db: f32) -> f32 {
332 ((db - self.min_db) * self.range_recip)
333 .clamp(0.0, 1.0)
334 .powf(self.factor)
335 }
336}
337
338impl Default for DbMeterNormalizer {
339 fn default() -> Self {
340 Self::new(-100.0, 0.0, -22.0)
341 }
342}
343
344pub fn is_buffer_silent(buffer: &[f32], min_amp: f32) -> bool {
347 let mut silent = true;
348 for &s in buffer.iter() {
349 if s.abs() > min_amp {
350 silent = false;
351 break;
352 }
353 }
354 silent
355}