math_audio_dsp/
smoothing.rs1#[derive(Debug, Clone, Copy)]
7pub struct Smoother {
8 target: f32,
9 current: f32,
10 coeff: f32,
11}
12
13#[allow(dead_code)]
14impl Smoother {
15 pub fn new(value: f32, time_ms: f32, sample_rate: u32) -> Self {
18 let coeff = Self::calculate_coeff(time_ms, sample_rate);
19 Self {
20 target: value,
21 current: value,
22 coeff,
23 }
24 }
25
26 fn calculate_coeff(time_ms: f32, sample_rate: u32) -> f32 {
27 if time_ms <= 0.0 || sample_rate == 0 {
28 0.0
29 } else {
30 (-1.0 / (time_ms * 0.001 * sample_rate as f32)).exp()
33 }
34 }
35
36 pub fn set_time(&mut self, time_ms: f32, sample_rate: u32) {
37 self.coeff = Self::calculate_coeff(time_ms, sample_rate);
38 }
39
40 pub fn set_target(&mut self, value: f32) {
42 self.target = value;
43 if self.coeff == 0.0 {
45 self.current = value;
46 }
47 }
48
49 #[inline]
51 pub fn next_n(&mut self, n: usize) -> f32 {
52 if self.coeff == 0.0 || (self.current - self.target).abs() < 1e-5 || n == 0 {
53 self.current = self.target;
54 } else {
55 let block_coeff = self.coeff.powi(n as i32);
57 self.current = self.target + block_coeff * (self.current - self.target);
58 }
59 self.current
60 }
61
62 #[inline]
64 pub fn advance(&mut self) -> f32 {
65 self.next_n(1)
66 }
67
68 #[inline]
70 pub fn current(&self) -> f32 {
71 self.current
72 }
73
74 #[inline]
76 pub fn target(&self) -> f32 {
77 self.target
78 }
79
80 #[inline]
83 pub fn process_sample(&mut self, sample: f32) -> f32 {
84 if (self.current - self.target).abs() < 1e-5 {
87 self.current = self.target;
88 } else {
89 self.current = self.target + self.coeff * (self.current - self.target);
90 }
91 sample * self.current
92 }
93
94 pub fn reset(&mut self, value: f32) {
96 self.target = value;
97 self.current = value;
98 }
99}
100
101#[derive(Debug, Clone, Copy)]
104pub struct LinearSmoother {
105 target: f32,
106 current: f32,
107 step: f32,
108 sample_rate: u32,
109 time_ms: f32,
110}
111
112impl LinearSmoother {
113 pub fn new(value: f32, time_ms: f32, sample_rate: u32) -> Self {
114 Self {
115 target: value,
116 current: value,
117 step: 0.0,
118 sample_rate,
119 time_ms,
120 }
121 }
122
123 pub fn set_target(&mut self, value: f32) {
124 self.target = value;
125 if self.time_ms <= 0.0 {
126 self.current = value;
127 self.step = 0.0;
128 } else {
129 let samples = (self.time_ms * 0.001 * self.sample_rate as f32).max(1.0);
130 self.step = (self.target - self.current) / samples;
131 }
132 }
133
134 #[inline]
135 pub fn advance(&mut self) -> f32 {
136 self.next_n(1)
137 }
138
139 #[inline]
140 pub fn next_n(&mut self, n: usize) -> f32 {
141 if n == 0 {
142 return self.current;
143 }
144 let total_step = self.step * n as f32;
145 if (self.current - self.target).abs() <= total_step.abs() {
146 self.current = self.target;
147 self.step = 0.0;
148 } else {
149 self.current += total_step;
150 }
151 self.current
152 }
153
154 #[allow(dead_code)]
155 pub fn reset(&mut self, value: f32) {
156 self.target = value;
157 self.current = value;
158 self.step = 0.0;
159 }
160
161 #[allow(dead_code)]
162 pub fn current(&self) -> f32 {
163 self.current
164 }
165
166 pub fn target(&self) -> f32 {
167 self.target
168 }
169}
170
171#[derive(Debug, Clone, Copy)]
174pub struct LogSmoother {
175 target: f32,
176 current: f32,
177 ratio: f32,
178 sample_rate: u32,
179 time_ms: f32,
180}
181
182impl LogSmoother {
183 pub fn new(value: f32, time_ms: f32, sample_rate: u32) -> Self {
184 Self {
185 target: value.max(1e-7),
186 current: value.max(1e-7),
187 ratio: 1.0,
188 sample_rate,
189 time_ms,
190 }
191 }
192
193 pub fn set_target(&mut self, value: f32) {
194 self.target = value.max(1e-7);
195 if self.time_ms <= 0.0 {
196 self.current = self.target;
197 self.ratio = 1.0;
198 } else {
199 let samples = (self.time_ms * 0.001 * self.sample_rate as f32).max(1.0);
200 self.ratio = (self.target / self.current).powf(1.0 / samples);
202 }
203 }
204
205 #[inline]
206 pub fn advance(&mut self) -> f32 {
207 self.next_n(1)
208 }
209
210 #[inline]
211 pub fn next_n(&mut self, n: usize) -> f32 {
212 if self.ratio == 1.0 || n == 0 {
213 return self.current;
214 }
215
216 self.current *= self.ratio.powi(n as i32);
217
218 if (self.ratio > 1.0 && self.current >= self.target)
220 || (self.ratio < 1.0 && self.current <= self.target)
221 {
222 self.current = self.target;
223 self.ratio = 1.0;
224 }
225
226 self.current
227 }
228
229 #[allow(dead_code)]
230 pub fn reset(&mut self, value: f32) {
231 self.target = value.max(1e-7);
232 self.current = self.target;
233 self.ratio = 1.0;
234 }
235
236 #[allow(dead_code)]
237 pub fn current(&self) -> f32 {
238 self.current
239 }
240
241 pub fn target(&self) -> f32 {
242 self.target
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_exponential_smoother() {
252 let mut s = Smoother::new(0.0, 10.0, 1000); s.set_target(1.0);
254
255 let first = s.advance();
256 assert!(first > 0.0 && first < 1.0);
257
258 for _ in 0..100 {
259 s.advance();
260 }
261 assert!((s.current() - 1.0).abs() < 1e-4);
262 }
263
264 #[test]
265 fn test_linear_smoother() {
266 let mut s = LinearSmoother::new(0.0, 10.0, 1000); s.set_target(1.0);
268
269 assert!((s.advance() - 0.1).abs() < 1e-6);
270 assert!((s.advance() - 0.2).abs() < 1e-6);
271
272 for _ in 0..7 {
273 s.advance();
274 }
275 assert!((s.advance() - 1.0).abs() < 1e-6);
276 assert!((s.advance() - 1.0).abs() < 1e-6);
277 }
278
279 #[test]
280 fn test_log_smoother() {
281 let mut s = LogSmoother::new(100.0, 10.0, 1000); s.set_target(1000.0);
283
284 let first = s.advance();
285 assert!((first - 125.89).abs() < 0.1);
287
288 for _ in 0..8 {
289 s.advance();
290 }
291 assert!((s.advance() - 1000.0).abs() < 1e-4);
292 assert!((s.advance() - 1000.0).abs() < 1e-6);
293 }
294
295 #[test]
296 fn test_smoother_sample_rate_zero_no_panic() {
297 let mut s = Smoother::new(1.0, 50.0, 0);
299 assert_eq!(s.current(), 1.0);
300 s.set_target(2.0);
301 assert_eq!(s.current(), 2.0);
303 let val = s.advance();
305 assert_eq!(val, 2.0);
306 assert!(!val.is_nan());
307 assert!(!val.is_infinite());
308 }
309
310 #[test]
311 fn test_smoother_set_time_sample_rate_zero() {
312 let mut s = Smoother::new(1.0, 10.0, 48000);
313 s.set_time(10.0, 0);
314 s.set_target(5.0);
315 assert_eq!(s.current(), 5.0);
317 }
318}