1use alloc::sync::Arc;
2use core::sync::atomic::{AtomicU32, Ordering};
3
4use crate::{frame, math::Float, Frame, Seek, Signal, Smoothed};
5
6pub struct FixedGain<T: ?Sized> {
10 gain: f32,
11 inner: T,
12}
13
14impl<T> FixedGain<T> {
15 pub fn new(signal: T, db: f32) -> Self {
19 Self {
20 gain: 10.0f32.powf(db / 20.0),
21 inner: signal,
22 }
23 }
24}
25
26impl<T: Signal + ?Sized> Signal for FixedGain<T>
27where
28 T::Frame: Frame,
29{
30 type Frame = T::Frame;
31
32 fn sample(&mut self, interval: f32, out: &mut [T::Frame]) {
33 self.inner.sample(interval, out);
34 for x in out {
35 *x = frame::scale(x, self.gain);
36 }
37 }
38
39 fn is_finished(&self) -> bool {
40 self.inner.is_finished()
41 }
42}
43
44impl<T: Seek + ?Sized> Seek for FixedGain<T>
45where
46 T::Frame: Frame,
47{
48 fn seek(&mut self, seconds: f32) {
49 self.inner.seek(seconds)
50 }
51}
52
53pub struct Gain<T: ?Sized> {
59 shared: Arc<AtomicU32>,
60 gain: Smoothed<f32>,
61 inner: T,
62}
63
64impl<T> Gain<T> {
65 pub fn new(signal: T) -> (GainControl, Self) {
67 let signal = Gain {
68 shared: Arc::new(AtomicU32::new(1.0f32.to_bits())),
69 gain: Smoothed::new(1.0),
70 inner: signal,
71 };
72 let handle = GainControl(signal.shared.clone());
73 (handle, signal)
74 }
75
76 pub fn set_gain(&mut self, db: f32) {
82 self.set_amplitude_ratio(10.0f32.powf(db / 20.0));
83 }
84
85 pub fn set_amplitude_ratio(&mut self, factor: f32) {
91 self.shared.store(factor.to_bits(), Ordering::Relaxed);
92 self.gain = Smoothed::new(factor);
93 }
94}
95
96impl<T: Signal> Signal for Gain<T>
97where
98 T::Frame: Frame,
99{
100 type Frame = T::Frame;
101
102 #[allow(clippy::float_cmp)]
103 fn sample(&mut self, interval: f32, out: &mut [T::Frame]) {
104 self.inner.sample(interval, out);
105 let shared = f32::from_bits(self.shared.load(Ordering::Relaxed));
106 if self.gain.target() != &shared {
107 self.gain.set(shared);
108 }
109 if self.gain.progress() == 1.0 {
110 let g = self.gain.get();
111 if g != 1.0 {
112 for x in out {
113 *x = frame::scale(x, g);
114 }
115 }
116 return;
117 }
118 for x in out {
119 *x = frame::scale(x, self.gain.get());
120 self.gain.advance(interval / SMOOTHING_PERIOD);
121 }
122 }
123
124 fn is_finished(&self) -> bool {
125 self.inner.is_finished()
126 }
127}
128
129pub struct GainControl(Arc<AtomicU32>);
131
132impl GainControl {
133 pub fn gain(&self) -> f32 {
135 20.0 * self.amplitude_ratio().log10()
136 }
137
138 pub fn set_gain(&mut self, db: f32) {
144 self.set_amplitude_ratio(10.0f32.powf(db / 20.0));
145 }
146
147 pub fn amplitude_ratio(&self) -> f32 {
149 f32::from_bits(self.0.load(Ordering::Relaxed))
150 }
151
152 pub fn set_amplitude_ratio(&mut self, factor: f32) {
158 self.0.store(factor.to_bits(), Ordering::Relaxed);
159 }
160}
161
162const SMOOTHING_PERIOD: f32 = 0.1;
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::Constant;
169
170 #[test]
171 fn smoothing() {
172 let (mut c, mut s) = Gain::new(Constant(1.0));
173 let mut buf = [0.0; 6];
174 c.set_amplitude_ratio(5.0);
175 s.sample(0.025, &mut buf);
176 assert_eq!(buf, [1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
177 s.sample(0.025, &mut buf);
178 assert_eq!(buf, [5.0; 6]);
179 }
180}