Skip to main content

oscen/
utils.rs

1use super::signal::{Rack, Real, Signal};
2use approx::relative_eq;
3
4/// Given f(0) = low, f(1/2) = mid, and f(1) = high, let f(x) = a + b*exp(cs).
5/// Fit a, b, and c so to match the above. If mid < 1/2(high + low) then f is
6/// convex, if equal f is linear, if greater then f is concave.
7#[derive(Copy, Clone, Debug)]
8pub struct ExpInterp {
9    low: Real,
10    mid: Real,
11    high: Real,
12    a: Real,
13    b: Real,
14    c: Real,
15    linear: bool,
16}
17
18impl ExpInterp {
19    pub fn new(low: Real, mid: Real, high: Real) -> Self {
20        let mut exp_interp = ExpInterp {
21            low,
22            mid,
23            high,
24            a: 0.0,
25            b: 0.0,
26            c: 0.0,
27            linear: true,
28        };
29        if relative_eq!(high - mid, mid - low) {
30            exp_interp
31        } else {
32            exp_interp.update(low, mid, high);
33            exp_interp
34        }
35    }
36
37    pub fn update(&mut self, low: Real, mid: Real, high: Real) {
38        if relative_eq!(high - mid, mid - low) {
39            self.low = low;
40            self.mid = mid;
41            self.high = high;
42            self.linear = true;
43        } else {
44            self.b = (mid - low) * (mid - low) / (high - 2.0 * mid + low);
45            self.a = low - self.b;
46            self.c = 2.0 * ((high - mid) / (mid - low)).ln();
47            self.linear = false;
48        }
49    }
50
51    /// Interpolate according to f(x).
52    pub fn interp(&self, x: Real) -> Real {
53        if self.linear {
54            self.low + (self.high - self.low) * x
55        } else {
56            self.a + self.b * (self.c * x).exp()
57        }
58    }
59
60    /// Inverse of interpolation function f.
61    pub fn interp_inv(&self, y: Real) -> Real {
62        if self.linear {
63            (y - self.low) / (self.high - self.low)
64        } else {
65            ((y - self.a) / self.b).ln() / self.c
66        }
67    }
68}
69
70pub fn signals<T>(module: &mut T, start: u32, end: u32, sample_rate: Real) -> Vec<(f32, f32)>
71where
72    T: Signal,
73{
74    let rack = Rack::new(vec![]);
75    let mut result = vec![];
76    for i in start..=end {
77        result.push((
78            i as f32 / sample_rate as f32,
79            module.signal(&rack, sample_rate) as f32,
80        ));
81    }
82    result
83}
84
85/// Variable length circular buffer.
86#[derive(Clone)]
87pub struct RingBuffer<T> {
88    buffer: Vec<T>,
89    pub read_pos: Real,
90    pub write_pos: usize,
91}
92
93impl<T> RingBuffer<T>
94where
95    T: Clone + Default,
96{
97    pub fn new(read_pos: Real, write_pos: usize) -> Self {
98        assert!(
99            read_pos.trunc() as usize <= write_pos,
100            "Read position must be <= write postion"
101        );
102        RingBuffer {
103            // +3 is to give room for cubic interpolation
104            buffer: vec![Default::default(); write_pos + 3],
105            read_pos,
106            write_pos,
107        }
108    }
109
110    pub fn push(&mut self, v: T) {
111        let n = self.buffer.len();
112        self.write_pos = (self.write_pos + 1) % n;
113        self.read_pos = (self.read_pos + 1.0) % n as Real;
114        self.buffer[self.write_pos] = v;
115    }
116
117    pub fn len(&self) -> usize {
118        self.buffer.len()
119    }
120
121    pub fn resize(&mut self, size: usize) {
122        self.buffer.resize_with(size, Default::default);
123    }
124
125    pub fn set_read_pos(&mut self, rp: Real) {
126        self.read_pos = rp % self.buffer.len() as Real;
127    }
128
129    pub fn set_write_pos(&mut self, wp: usize) {
130        self.write_pos = wp % self.buffer.len();
131    }
132}
133
134impl<T> RingBuffer<T>
135where
136    T: Copy + Default,
137{
138    pub fn get(&self) -> T {
139        self.buffer[self.read_pos.trunc() as usize]
140    }
141
142    pub fn get_offset(&self, offset: i32) -> T {
143        let n = self.buffer.len() as i32;
144        let mut offset = offset;
145        while offset < 0 {
146            offset += n;
147        }
148        let i = (self.read_pos.trunc() as usize + offset as usize) % n as usize;
149        self.buffer[i]
150    }
151}
152
153impl RingBuffer<Real> {
154    pub fn get_linear(&self) -> Real {
155        let f = self.read_pos - self.read_pos.trunc();
156        (1.0 - f) * self.get() + f * self.get_offset(1)
157    }
158
159    /// Hermite cubic polynomial interpolation.
160    pub fn get_cubic(&self) -> Real {
161        let v0 = self.get_offset(-1);
162        let v1 = self.get();
163        let v2 = self.get_offset(1);
164        let v3 = self.get_offset(2);
165        let f = self.read_pos - self.read_pos.trunc();
166        let a1 = 0.5 * (v2 - v0);
167        let a2 = v0 - 2.5 * v1 + 2.0 * v2 - 0.5 * v3;
168        let a3 = 0.5 * (v3 - v0) + 1.5 * (v1 - v2);
169        a3 * f * f * f + a2 * f * f + a1 * f + v1
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use approx::relative_eq;
177
178    fn trunc4(x: Real) -> i32 {
179        (10_000.0 * x + 0.5) as i32
180    }
181    #[test]
182    fn linear_interp() {
183        let ie = ExpInterp::new(0.0, 0.5, 1.0);
184        assert!(relative_eq!(ie.interp(0.0), 0.0));
185        assert!(relative_eq!(ie.interp(0.5), 0.5));
186        assert!(relative_eq!(ie.interp(0.75), 0.75));
187        assert!(relative_eq!(ie.interp(1.0), 1.0));
188    }
189    #[test]
190    fn exp_interp() {
191        let ie = ExpInterp::new(0.0, 0.4, 1.0);
192        let result = trunc4(ie.interp(0.0));
193        assert_eq!(result, 0, "interp returned {}, epxected 0", result);
194        let result = trunc4(ie.interp(0.5));
195        assert_eq!(result, 4_000, "interp returned {}, epxected 4,000", result);
196        let result = trunc4(ie.interp(0.75));
197        assert_eq!(result, 6_697, "interp returned {}, epxected 6,697", result);
198        let result = trunc4(ie.interp(1.0));
199        assert_eq!(
200            result, 10_000,
201            "interp returned {}, epxected 10,1000",
202            result
203        );
204    }
205    #[test]
206    fn linear_interp_inv() {
207        let ie = ExpInterp::new(0.0, 0.5, 1.0);
208        assert!(relative_eq!(ie.interp_inv(0.0), 0.0));
209        assert!(relative_eq!(ie.interp_inv(0.5), 0.5));
210        assert!(relative_eq!(ie.interp_inv(0.75), 0.75));
211        assert!(relative_eq!(ie.interp_inv(1.0), 1.0));
212    }
213    #[test]
214    fn exp_interp_inv() {
215        let ie = ExpInterp::new(0.0, 0.4, 1.0);
216        let result = trunc4(ie.interp_inv(0.0));
217        assert_eq!(result, 0, "interp returned {}, epxected 0", result);
218        let result = trunc4(ie.interp_inv(0.4));
219        assert_eq!(result, 5_000, "interp returned {}, epxected 4,000", result);
220        let result = trunc4(ie.interp_inv(0.6697));
221        assert_eq!(result, 7_500, "interp returned {}, epxected 7,500", result);
222        let result = trunc4(ie.interp_inv(1.0));
223        assert_eq!(
224            result, 10_000,
225            "interp returned {}, epxected 10,1000",
226            result
227        );
228    }
229
230    #[test]
231    fn ring_buffer() {
232        let mut rb = RingBuffer::<Real>::new(0.5, 5);
233        let result = rb.get();
234        assert_eq!(result, 0.0, "get returned {}, expected 0.0", result);
235        for i in 0..=6 {
236            rb.push(i as Real);
237        }
238        let result = rb.get();
239        assert_eq!(result, 1.0, "get returned {}, expected 0.0", result);
240        let result = rb.get_linear();
241        assert_eq!(result, 1.5, "get_linear returned {}, expected 0.0", result);
242        let result = rb.get_cubic();
243        assert_eq!(result, 1.5, "get_cubic returned {}, expected 0.0", result);
244    }
245
246    #[test]
247    fn ring_buffer_resize() {
248        let mut rb = RingBuffer::<Real>::new(0.5, 5);
249        rb.resize(10);
250        for i in 0..=6 {
251            rb.push(i as Real);
252        }
253        let result = rb.get_linear();
254        assert_eq!(result, 1.5, "get_linear returned {}, expected 0.0", result);
255        let result = rb.get_cubic();
256        assert_eq!(result, 1.5, "get_cubic returned {}, expected 0.0", result);
257    }
258}