rusty_daw_core/
declick.rs

1// Some modified code from baseplug:
2//
3// https://github.com/wrl/baseplug/blob/trunk/src/declick.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;
10
11use super::{SampleRate, Seconds, SmoothF32, SmoothStatus};
12
13const DECLICK_SETTLE: f32 = 0.001;
14
15pub struct DeclickOutput<'a, T, const MAX_BLOCKSIZE: usize> {
16    pub from: &'a T,
17    pub to: &'a T,
18
19    pub fade: &'a [f32; MAX_BLOCKSIZE],
20    pub status: SmoothStatus,
21}
22
23pub struct Declick<T: Sized + Clone, const MAX_BLOCKSIZE: usize> {
24    current: T,
25    next: Option<T>,
26    staged: Option<T>,
27
28    fade: SmoothF32<MAX_BLOCKSIZE>,
29}
30
31impl<T, const MAX_BLOCKSIZE: usize> Declick<T, MAX_BLOCKSIZE>
32where
33    T: Sized + Clone + Eq,
34{
35    pub fn new(initial: T) -> Self {
36        Self {
37            current: initial,
38            next: None,
39            staged: None,
40            fade: SmoothF32::new(0.0),
41        }
42    }
43
44    pub fn reset(&mut self, to: T) {
45        self.current = to;
46        self.next = None;
47        self.staged = None;
48
49        self.fade.reset(0.0);
50    }
51
52    pub fn set(&mut self, to: T) {
53        if self.dest() == &to {
54            return;
55        }
56
57        if self.next.is_none() {
58            self.next = Some(to);
59
60            self.fade.reset(0.0);
61            self.fade.set(1.0);
62        } else {
63            self.staged = Some(to);
64        }
65    }
66
67    pub fn set_speed(&mut self, sample_rate: SampleRate, seconds: Seconds) {
68        self.fade.set_speed(sample_rate, seconds);
69    }
70
71    pub fn output(&self) -> DeclickOutput<T, MAX_BLOCKSIZE> {
72        let fade = self.fade.output();
73
74        DeclickOutput {
75            from: &self.current,
76            to: self.next.as_ref().unwrap_or(&self.current),
77
78            fade: fade.values,
79            status: fade.status,
80        }
81    }
82
83    pub fn current_value(&self) -> (&T, SmoothStatus) {
84        let (_, status) = self.fade.current_value();
85
86        (&self.current, status)
87    }
88
89    pub fn dest(&self) -> &T {
90        self.staged
91            .as_ref()
92            .or_else(|| self.next.as_ref())
93            .unwrap_or(&self.current)
94    }
95
96    pub fn is_active(&self) -> bool {
97        self.next.is_some()
98    }
99
100    pub fn process(&mut self, frames: usize) {
101        self.fade.process(frames);
102    }
103
104    pub fn update_status(&mut self) {
105        if !self.is_active() {
106            return;
107        }
108
109        self.fade.update_status_with_epsilon(DECLICK_SETTLE);
110
111        if self.fade.is_active() {
112            return;
113        }
114
115        self.current = self.next.take().unwrap();
116        self.next = self.staged.take();
117    }
118}
119
120impl<T, const MAX_BLOCKSIZE: usize> fmt::Debug for Declick<T, MAX_BLOCKSIZE>
121where
122    T: fmt::Debug + Sized + Clone,
123{
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        f.debug_struct(concat!("Declick<", stringify!(T), ">"))
126            .field("current", &self.current)
127            .field("next", &self.next)
128            .field("staged", &self.staged)
129            .field("fade", &self.fade)
130            .finish()
131    }
132}