1use std::sync::atomic::{AtomicU64, Ordering};
2
3#[derive(Clone, Copy, Debug)]
5pub enum SmoothingStyle {
6 None,
7 Linear(f64),
8 Exponential(f64),
9}
10
11struct SmoothAtomic {
13 bits: AtomicU64,
14}
15
16impl SmoothAtomic {
17 fn new(value: f64) -> Self {
18 Self {
19 bits: AtomicU64::new(value.to_bits()),
20 }
21 }
22
23 #[inline]
24 fn get(&self) -> f64 {
25 f64::from_bits(self.bits.load(Ordering::Relaxed))
26 }
27
28 #[inline]
29 fn set(&self, value: f64) {
30 self.bits.store(value.to_bits(), Ordering::Relaxed);
31 }
32}
33
34pub struct Smoother {
38 style: SmoothingStyle,
39 current: SmoothAtomic,
40 target: SmoothAtomic,
41 coeff: SmoothAtomic,
42 sample_rate: SmoothAtomic,
43}
44
45impl Smoother {
46 pub fn new(style: SmoothingStyle) -> Self {
47 Self {
48 style,
49 current: SmoothAtomic::new(0.0),
50 target: SmoothAtomic::new(0.0),
51 coeff: SmoothAtomic::new(0.0),
52 sample_rate: SmoothAtomic::new(44100.0),
53 }
54 }
55
56 pub fn set_sample_rate(&self, sr: f64) {
57 self.sample_rate.set(sr);
58 self.recalculate_coeff();
59 }
60
61 pub fn snap(&self, value: f64) {
63 self.current.set(value);
64 self.target.set(value);
65 }
66
67 #[inline]
69 pub fn next(&self, target: f64) -> f32 {
70 self.target.set(target);
71 let current = self.current.get();
72 let coeff = self.coeff.get();
73
74 let new_current = match self.style {
75 SmoothingStyle::None => target,
76 SmoothingStyle::Linear(_) => {
77 let diff = target - current;
78 if diff.abs() < 1e-8 {
79 target
80 } else {
81 let step = diff * coeff;
82 if step.abs() >= diff.abs() {
83 target
84 } else {
85 current + step
86 }
87 }
88 }
89 SmoothingStyle::Exponential(_) => current + coeff * (target - current),
90 };
91
92 self.current.set(new_current);
93 new_current as f32
94 }
95
96 #[inline]
98 pub fn current(&self) -> f32 {
99 self.current.get() as f32
100 }
101
102 fn recalculate_coeff(&self) {
103 let sr = self.sample_rate.get();
104 let new_coeff = match self.style {
105 SmoothingStyle::None => 1.0,
106 SmoothingStyle::Linear(ms) => {
107 let samples = (ms / 1000.0) * sr;
108 if samples > 1.0 {
109 1.0 / samples
110 } else {
111 1.0
112 }
113 }
114 SmoothingStyle::Exponential(ms) => {
115 let samples = (ms / 1000.0) * sr;
116 if samples > 0.0 {
117 1.0 - (-1.0 / samples).exp()
118 } else {
119 1.0
120 }
121 }
122 };
123 self.coeff.set(new_coeff);
124 }
125}