1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::{
cell::Cell,
sync::atomic::{AtomicU32, Ordering},
};
use crate::{frame, Controlled, Filter, Frame, Signal};
pub struct Gain<T: ?Sized> {
shared: AtomicU32,
prev_gain: Cell<f32>,
next_gain: Cell<f32>,
time_since_changed: Cell<f32>,
inner: T,
}
impl<T> Gain<T> {
pub fn new(signal: T) -> Self {
Self {
shared: AtomicU32::new(1.0f32.to_bits()),
prev_gain: Cell::new(1.0),
next_gain: Cell::new(1.0),
time_since_changed: Cell::new(1.0),
inner: signal,
}
}
fn gain(&self) -> f32 {
let diff = self.next_gain.get() - self.prev_gain.get();
let progress = ((self.time_since_changed.get()) / SMOOTHING_PERIOD).min(1.0);
self.prev_gain.get() + progress * diff
}
}
impl<T: Signal> Signal for Gain<T>
where
T::Frame: Frame,
{
type Frame = T::Frame;
#[allow(clippy::float_cmp)]
fn sample(&self, interval: f32, out: &mut [T::Frame]) {
self.inner.sample(interval, out);
let shared = f32::from_bits(self.shared.load(Ordering::Relaxed));
if self.next_gain.get() != shared {
self.prev_gain.set(self.gain());
self.next_gain.set(shared);
self.time_since_changed.set(0.0);
}
for x in out {
*x = frame::scale(x, self.gain());
self.time_since_changed
.set(self.time_since_changed.get() + interval);
}
}
fn remaining(&self) -> f32 {
self.inner.remaining()
}
}
impl<T> Filter for Gain<T> {
type Inner = T;
fn inner(&self) -> &T {
&self.inner
}
}
pub struct GainControl<'a, T>(&'a Gain<T>);
unsafe impl<'a, T: 'a> Controlled<'a> for Gain<T> {
type Control = GainControl<'a, T>;
unsafe fn make_control(signal: &'a Gain<T>) -> Self::Control {
GainControl(signal)
}
}
impl<'a, T> GainControl<'a, T> {
pub fn gain(&self) -> f32 {
f32::from_bits(self.0.shared.load(Ordering::Relaxed))
}
pub fn set_gain(&mut self, factor: f32) {
self.0.shared.store(factor.to_bits(), Ordering::Relaxed);
}
}
const SMOOTHING_PERIOD: f32 = 0.1;
#[cfg(test)]
mod tests {
use super::*;
use crate::Sample;
struct Const;
impl Signal for Const {
type Frame = Sample;
fn sample(&self, _: f32, out: &mut [Sample]) {
for x in out {
*x = 1.0;
}
}
}
#[test]
fn smoothing() {
let s = Gain::new(Const);
let mut buf = [0.0; 6];
GainControl(&s).set_gain(5.0);
s.sample(0.025, &mut buf);
assert_eq!(buf, [1.0, 2.0, 3.0, 4.0, 5.0, 5.0]);
s.sample(0.025, &mut buf);
assert_eq!(buf, [5.0; 6]);
}
}