rusty_daw_core/
smooth.rs

1// Some modified code from baseplug:
2//
3// https://github.com/wrl/baseplug/blob/trunk/src/smooth.rs
4// https://github.com/wrl/baseplug/blob/trunk/LICENSE-APACHE
5// https://github.com/wrl/baseplug/blob/trunk/LICENSE-MIT
6//
7//  Thanks wrl! :)
8
9use std::fmt;
10use std::ops;
11use std::slice;
12
13use super::{SampleRate, Seconds};
14
15const SETTLE: f32 = 0.00001f32;
16
17#[derive(Debug, PartialEq, Clone, Copy)]
18pub enum SmoothStatus {
19    Inactive,
20    Active,
21    Deactivating,
22}
23
24impl SmoothStatus {
25    fn is_active(&self) -> bool {
26        self != &SmoothStatus::Inactive
27    }
28}
29
30pub struct SmoothOutputF32<'a, const MAX_BLOCKSIZE: usize> {
31    pub values: &'a [f32; MAX_BLOCKSIZE],
32    pub status: SmoothStatus,
33}
34
35impl<'a, const MAX_BLOCKSIZE: usize> SmoothOutputF32<'a, MAX_BLOCKSIZE> {
36    pub fn is_smoothing(&self) -> bool {
37        self.status.is_active()
38    }
39}
40
41impl<'a, I, const MAX_BLOCKSIZE: usize> ops::Index<I> for SmoothOutputF32<'a, MAX_BLOCKSIZE>
42where
43    I: slice::SliceIndex<[f32]>,
44{
45    type Output = I::Output;
46
47    #[inline]
48    fn index(&self, idx: I) -> &I::Output {
49        &self.values[idx]
50    }
51}
52
53pub struct SmoothF32<const MAX_BLOCKSIZE: usize> {
54    output: [f32; MAX_BLOCKSIZE],
55    input: f32,
56
57    status: SmoothStatus,
58
59    a: f32,
60    b: f32,
61    last_output: f32,
62}
63
64impl<const MAX_BLOCKSIZE: usize> SmoothF32<MAX_BLOCKSIZE> {
65    pub fn new(input: f32) -> Self {
66        Self {
67            status: SmoothStatus::Inactive,
68            input,
69            output: [input; MAX_BLOCKSIZE],
70
71            a: 1.0,
72            b: 0.0,
73            last_output: input,
74        }
75    }
76
77    pub fn reset(&mut self, val: f32) {
78        *self = Self {
79            a: self.a,
80            b: self.b,
81            ..Self::new(val)
82        };
83    }
84
85    pub fn set(&mut self, val: f32) {
86        self.input = val;
87        self.status = SmoothStatus::Active;
88    }
89
90    pub fn dest(&self) -> f32 {
91        self.input
92    }
93
94    pub fn output(&self) -> SmoothOutputF32<MAX_BLOCKSIZE> {
95        SmoothOutputF32 {
96            values: &self.output,
97            status: self.status,
98        }
99    }
100
101    pub fn current_value(&self) -> (f32, SmoothStatus) {
102        (self.last_output, self.status)
103    }
104
105    pub fn update_status_with_epsilon(&mut self, epsilon: f32) -> SmoothStatus {
106        let status = self.status;
107
108        match status {
109            SmoothStatus::Active => {
110                if (self.input - self.output[0]).abs() < epsilon {
111                    self.reset(self.input);
112                    self.status = SmoothStatus::Deactivating;
113                }
114            }
115
116            SmoothStatus::Deactivating => self.status = SmoothStatus::Inactive,
117
118            _ => (),
119        };
120
121        self.status
122    }
123
124    pub fn process(&mut self, frames: usize) {
125        if self.status != SmoothStatus::Active {
126            return;
127        }
128
129        let frames = frames.min(MAX_BLOCKSIZE);
130        let input = self.input * self.a;
131
132        self.output[0] = input + (self.last_output * self.b);
133
134        for i in 1..frames {
135            // This is safe because `proc_frames.unchecked_frames()` is always less than or
136            // equal to MAX_BLOCKSIZE.
137            unsafe {
138                *self.output.get_unchecked_mut(i) =
139                    input + (*self.output.get_unchecked(i - 1) * self.b);
140            }
141
142            self.output[i] = input + (self.output[i - 1] * self.b);
143        }
144
145        // This is safe because `proc_frames.unchecked_frames()` is always less than or
146        // equal to MAX_BLOCKSIZE.
147        unsafe {
148            self.last_output = *self.output.get_unchecked(frames - 1);
149        }
150    }
151
152    pub fn is_active(&self) -> bool {
153        self.status.is_active()
154    }
155}
156
157impl<const MAX_BLOCKSIZE: usize> SmoothF32<MAX_BLOCKSIZE> {
158    pub fn set_speed(&mut self, sample_rate: SampleRate, seconds: Seconds) {
159        self.b = (-1.0f32 / (seconds.0 as f32 * sample_rate.0 as f32)).exp();
160        self.a = 1.0f32 - self.b;
161    }
162
163    pub fn update_status(&mut self) -> SmoothStatus {
164        self.update_status_with_epsilon(SETTLE)
165    }
166}
167
168impl<const MAX_BLOCKSIZE: usize> From<f32> for SmoothF32<MAX_BLOCKSIZE> {
169    fn from(val: f32) -> Self {
170        Self::new(val)
171    }
172}
173
174impl<const MAX_BLOCKSIZE: usize> fmt::Debug for SmoothF32<MAX_BLOCKSIZE> {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct(concat!("SmoothF32"))
177            .field("output[0]", &self.output[0])
178            .field("input", &self.input)
179            .field("status", &self.status)
180            .field("last_output", &self.last_output)
181            .finish()
182    }
183}
184
185// ------  F64  -------------------------------------------------------------------------
186
187pub struct SmoothOutputF64<'a, const MAX_BLOCKSIZE: usize> {
188    pub values: &'a [f64; MAX_BLOCKSIZE],
189    pub status: SmoothStatus,
190}
191
192impl<'a, const MAX_BLOCKSIZE: usize> SmoothOutputF64<'a, MAX_BLOCKSIZE> {
193    pub fn is_smoothing(&self) -> bool {
194        self.status.is_active()
195    }
196}
197
198impl<'a, I, const MAX_BLOCKSIZE: usize> ops::Index<I> for SmoothOutputF64<'a, MAX_BLOCKSIZE>
199where
200    I: slice::SliceIndex<[f64]>,
201{
202    type Output = I::Output;
203
204    #[inline]
205    fn index(&self, idx: I) -> &I::Output {
206        &self.values[idx]
207    }
208}
209
210pub struct SmoothF64<const MAX_BLOCKSIZE: usize> {
211    output: [f64; MAX_BLOCKSIZE],
212    input: f64,
213
214    status: SmoothStatus,
215
216    a: f64,
217    b: f64,
218    last_output: f64,
219}
220
221impl<const MAX_BLOCKSIZE: usize> SmoothF64<MAX_BLOCKSIZE> {
222    pub fn new(input: f64) -> Self {
223        Self {
224            status: SmoothStatus::Inactive,
225            input,
226            output: [input; MAX_BLOCKSIZE],
227
228            a: 1.0,
229            b: 0.0,
230            last_output: input,
231        }
232    }
233
234    pub fn reset(&mut self, val: f64) {
235        *self = Self {
236            a: self.a,
237            b: self.b,
238            ..Self::new(val)
239        };
240    }
241
242    pub fn set(&mut self, val: f64) {
243        self.input = val;
244        self.status = SmoothStatus::Active;
245    }
246
247    pub fn dest(&self) -> f64 {
248        self.input
249    }
250
251    pub fn output(&self) -> SmoothOutputF64<MAX_BLOCKSIZE> {
252        SmoothOutputF64 {
253            values: &self.output,
254            status: self.status,
255        }
256    }
257
258    pub fn current_value(&self) -> (f64, SmoothStatus) {
259        (self.last_output, self.status)
260    }
261
262    pub fn update_status_with_epsilon(&mut self, epsilon: f64) -> SmoothStatus {
263        let status = self.status;
264
265        match status {
266            SmoothStatus::Active => {
267                if (self.input - self.output[0]).abs() < epsilon {
268                    self.reset(self.input);
269                    self.status = SmoothStatus::Deactivating;
270                }
271            }
272
273            SmoothStatus::Deactivating => self.status = SmoothStatus::Inactive,
274
275            _ => (),
276        };
277
278        self.status
279    }
280
281    pub fn process(&mut self, frames: usize) {
282        if self.status != SmoothStatus::Active {
283            return;
284        }
285
286        let frames = frames.min(MAX_BLOCKSIZE);
287        let input = self.input * self.a;
288
289        self.output[0] = input + (self.last_output * self.b);
290
291        for i in 1..frames {
292            // This is safe because `proc_frames.unchecked_frames()` is always less than or
293            // equal to MAX_BLOCKSIZE.
294            unsafe {
295                *self.output.get_unchecked_mut(i) =
296                    input + (*self.output.get_unchecked(i - 1) * self.b);
297            }
298
299            self.output[i] = input + (self.output[i - 1] * self.b);
300        }
301
302        // This is safe because `proc_frames.unchecked_frames()` is always less than or
303        // equal to MAX_BLOCKSIZE.
304        unsafe {
305            self.last_output = *self.output.get_unchecked(frames - 1);
306        }
307    }
308
309    pub fn is_active(&self) -> bool {
310        self.status.is_active()
311    }
312}
313
314impl<const MAX_BLOCKSIZE: usize> SmoothF64<MAX_BLOCKSIZE> {
315    pub fn set_speed(&mut self, sample_rate: SampleRate, seconds: Seconds) {
316        self.b = (-1.0f64 / (seconds.0 as f64 * sample_rate.0 as f64)).exp();
317        self.a = 1.0f64 - self.b;
318    }
319
320    pub fn update_status(&mut self) -> SmoothStatus {
321        self.update_status_with_epsilon(SETTLE as f64)
322    }
323}
324
325impl<const MAX_BLOCKSIZE: usize> From<f64> for SmoothF64<MAX_BLOCKSIZE> {
326    fn from(val: f64) -> Self {
327        Self::new(val)
328    }
329}
330
331impl<const MAX_BLOCKSIZE: usize> fmt::Debug for SmoothF64<MAX_BLOCKSIZE> {
332    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333        f.debug_struct(concat!("SmoothF64"))
334            .field("output[0]", &self.output[0])
335            .field("input", &self.input)
336            .field("status", &self.status)
337            .field("last_output", &self.last_output)
338            .finish()
339    }
340}