rusty_daw_core/
declick.rs1use 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}