synfx_dsp/
oversampling.rs

1// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5//! Oversampling related utilities, such as an up/downsampling filter.
6
7use crate::{Biquad, BiquadCoefs};
8use std::simd::f32x4;
9
10// Loosely adapted from https://github.com/VCVRack/Befaco/blob/v1/src/ChowDSP.hpp
11// Copyright (c) 2019-2020 Andrew Belt and Befaco contributors
12// Under GPLv-3.0-or-later
13//
14// Which was originally taken from https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp
15// Copyright (c) 2020 jatinchowdhury18
16/// Implements oversampling with a ratio of N and a 4 times cascade
17/// of Butterworth lowpass filters (~48dB?).
18#[derive(Debug, Copy, Clone)]
19pub struct Oversampling<const N: usize> {
20    filters: [Biquad; 4],
21    buffer: [f32; N],
22}
23
24impl<const N: usize> Oversampling<N> {
25    pub fn new() -> Self {
26        let mut this = Self { filters: [Biquad::new(); 4], buffer: [0.0; N] };
27
28        this.set_sample_rate(44100.0);
29
30        this
31    }
32
33    pub fn reset(&mut self) {
34        self.buffer = [0.0; N];
35        for filt in &mut self.filters {
36            filt.reset();
37        }
38    }
39
40    pub fn set_sample_rate(&mut self, srate: f32) {
41        let cutoff = 0.98 * (0.5 * srate);
42
43        let ovr_srate = (N as f32) * srate;
44        let filters_len = self.filters.len();
45
46        for (i, filt) in self.filters.iter_mut().enumerate() {
47            let q = BiquadCoefs::calc_cascaded_butter_q(2 * 4, filters_len - i);
48
49            filt.set_coefs(BiquadCoefs::lowpass(ovr_srate, q, cutoff));
50        }
51    }
52
53    #[inline]
54    pub fn upsample(&mut self, v: f32) {
55        self.buffer.fill(0.0);
56        self.buffer[0] = (N as f32) * v;
57
58        for s in &mut self.buffer {
59            for filt in &mut self.filters {
60                *s = filt.tick(*s);
61            }
62        }
63    }
64
65    #[inline]
66    pub fn resample_buffer(&mut self) -> &mut [f32; N] {
67        &mut self.buffer
68    }
69
70    #[inline]
71    pub fn downsample(&mut self) -> f32 {
72        let mut ret = 0.0;
73        for s in &mut self.buffer {
74            ret = *s;
75            for filt in &mut self.filters {
76                ret = filt.tick(ret);
77            }
78        }
79
80        ret
81    }
82}
83
84// Taken from va-filter by Fredemus aka Frederik Halkjær aka RocketPhysician
85// https://github.com/Fredemus/va-filter
86// Under License GPL-3.0-or-later
87//
88// below is a polyphase iir halfband filter (cutoff at fs/4) consisting of cascades of allpasses
89// translated from the freely available source code at https://www.musicdsp.org/en/latest/Filters/39-polyphase-filters.html
90// no changes to the algorithm, just some rust-ifying and simple simding for independent samples
91
92#[derive(Debug, Copy, Clone)]
93struct Allpass {
94    a: f32x4,
95    x0: f32x4,
96    x1: f32x4,
97    x2: f32x4,
98
99    y0: f32x4,
100    y1: f32x4,
101    y2: f32x4,
102}
103
104impl Default for Allpass {
105    fn default() -> Allpass {
106        Allpass {
107            a: f32x4::splat(0.),
108
109            x0: f32x4::splat(0.),
110            x1: f32x4::splat(0.),
111            x2: f32x4::splat(0.),
112
113            y0: f32x4::splat(0.),
114            y1: f32x4::splat(0.),
115            y2: f32x4::splat(0.),
116        }
117    }
118}
119impl Allpass {
120    fn process(&mut self, input: f32x4) -> f32x4 {
121        //shuffle inputs
122        self.x2 = self.x1;
123        self.x1 = self.x0;
124        self.x0 = input;
125
126        //shuffle outputs
127        self.y2 = self.y1;
128        self.y1 = self.y0;
129
130        //allpass filter 1
131        let output = self.x2 + ((input - self.y2) * self.a);
132
133        self.y0 = output;
134        output
135    }
136}
137#[derive(Debug, Copy, Clone)]
138struct AllpassCascade {
139    allpasses: [Allpass; 6],
140    num_filters: usize,
141}
142
143impl AllpassCascade {
144    fn process(&mut self, input: f32x4) -> f32x4 {
145        let mut output = input;
146        for i in 0..self.num_filters {
147            output = self.allpasses[i].process(output)
148        }
149        output
150    }
151}
152
153/// This is a polyphase iir halfband filter (cutoff at fs/4) consisting of cascades of allpasses.
154/// translated from the freely available source code at <https://www.musicdsp.org/en/latest/Filters/39-polyphase-filters.html>
155///
156/// Usage:
157///```
158/// #![feature(portable_simd)]
159/// use std::simd::f32x4;
160///
161/// use synfx_dsp::PolyIIRHalfbandFilter;
162///
163/// struct MyNiceDistort {
164///     upsampler: PolyIIRHalfbandFilter,
165///     downsampler: PolyIIRHalfbandFilter,
166/// }
167///
168/// impl MyNiceDistort {
169///     fn new() -> Self {
170///         Self {
171///             upsampler: PolyIIRHalfbandFilter::new(8, true),
172///             downsampler: PolyIIRHalfbandFilter::new(8, true),
173///         }
174///     }
175///
176///     fn process(&mut self, in_l: f32, in_r: f32) -> (f32, f32) {
177///         let frame = f32x4::from_array([in_l, in_r, 0.0, 0.0]);
178///         // Zero stuffing:
179///         let input = [frame, f32x4::splat(0.)];
180///         // Prepare the output:
181///         let mut output = f32x4::splat(0.);
182///         for i in 0..2 {
183///             // Upsampling:
184///             let frame = self.upsampler.process(f32x4::splat(2.) * input[i]);
185///
186///             // Apply some non linear stuff:
187///             let out = synfx_dsp::tanh_levien(frame * f32x4::splat(10.0));
188///
189///             // Downsampling:
190///             output = self.downsampler.process(out);
191///         }
192///
193///         let output = output.as_array();
194///         (output[0], output[1])
195///     }
196/// }
197///```
198#[derive(Debug, Copy, Clone)]
199pub struct PolyIIRHalfbandFilter {
200    filter_a: AllpassCascade,
201    filter_b: AllpassCascade,
202    old_out: f32x4,
203}
204
205impl PolyIIRHalfbandFilter {
206    /// Create a new [PolyIIRHalfbandFilter] with the given order.
207    /// - _order_ can be 2, 4, 6, 8, 10 or 12
208    /// - if _steep_ is `true`, it gives rejection of 69dB at order=8. Transition band is 0.01.
209    ///   if _steep_ is `false`, it gives rejection of 106dB at order=8. Transition band is 0.05.
210    pub fn new(order: usize, steep: bool) -> PolyIIRHalfbandFilter {
211        let a_coefficients: Vec<f32>;
212        let b_coefficients: Vec<f32>;
213
214        if steep {
215            //rejection=104dB, transition band=0.01
216            if order == 12 {
217                a_coefficients = vec![
218                    0.036681502163648017,
219                    0.2746317593794541,
220                    0.56109896978791948,
221                    0.769741833862266,
222                    0.8922608180038789,
223                    0.962094548378084,
224                ];
225
226                b_coefficients = vec![
227                    0.13654762463195771,
228                    0.42313861743656667,
229                    0.6775400499741616,
230                    0.839889624849638,
231                    0.9315419599631839,
232                    0.9878163707328971,
233                ];
234            }
235            //rejection=86dB, transition band=0.01
236            else if order == 10 {
237                a_coefficients = vec![
238                    0.051457617441190984,
239                    0.35978656070567017,
240                    0.6725475931034693,
241                    0.8590884928249939,
242                    0.9540209867860787,
243                ];
244
245                b_coefficients = vec![
246                    0.18621906251989334,
247                    0.529951372847964,
248                    0.7810257527489514,
249                    0.9141815687605308,
250                    0.985475023014907,
251                ];
252            }
253            //rejection=69dB, transition band=0.01
254            else if order == 8 {
255                a_coefficients = vec![
256                    0.07711507983241622,
257                    0.4820706250610472,
258                    0.7968204713315797,
259                    0.9412514277740471,
260                ];
261
262                b_coefficients = vec![
263                    0.2659685265210946,
264                    0.6651041532634957,
265                    0.8841015085506159,
266                    0.9820054141886075,
267                ];
268            }
269            //rejection=51dB, transition band=0.01
270            else if order == 6 {
271                a_coefficients = vec![0.1271414136264853, 0.6528245886369117, 0.9176942834328115];
272
273                b_coefficients = vec![0.40056789819445626, 0.8204163891923343, 0.9763114515836773];
274            }
275            //rejection=53dB,transition band=0.05
276            else if order == 4 {
277                a_coefficients = vec![0.12073211751675449, 0.6632020224193995];
278
279                b_coefficients = vec![0.3903621872345006, 0.890786832653497];
280            }
281            //order=2, rejection=36dB, transition band=0.1
282            else {
283                a_coefficients = vec![0.23647102099689224];
284                b_coefficients = vec![0.7145421497126001];
285            }
286        }
287        //softer slopes, more attenuation and less stopband ripple
288        else {
289            //rejection=150dB, transition band=0.05
290            if order == 12 {
291                a_coefficients = vec![
292                    0.01677466677723562,
293                    0.13902148819717805,
294                    0.3325011117394731,
295                    0.53766105314488,
296                    0.7214184024215805,
297                    0.8821858402078155,
298                ];
299                b_coefficients = vec![
300                    0.06501319274445962,
301                    0.23094129990840923,
302                    0.4364942348420355,
303                    0.6329609551399348, //0.06329609551399348
304                    0.80378086794111226,
305                    0.9599687404800694,
306                ];
307            }
308            //rejection=133dB, transition band=0.05
309            else if order == 10 {
310                a_coefficients = vec![
311                    0.02366831419883467,
312                    0.18989476227180174,
313                    0.43157318062118555,
314                    0.6632020224193995,
315                    0.860015542499582,
316                ];
317                b_coefficients = vec![
318                    0.09056555904993387,
319                    0.3078575723749043,
320                    0.5516782402507934,
321                    0.7652146863779808,
322                    0.95247728378667541,
323                ];
324            }
325            //rejection=106dB, transition band=0.05
326            else if order == 8 {
327                a_coefficients = vec![
328                    0.03583278843106211,
329                    0.2720401433964576,
330                    0.5720571972357003,
331                    0.827124761997324,
332                ];
333
334                b_coefficients = vec![
335                    0.1340901419430669,
336                    0.4243248712718685,
337                    0.7062921421386394,
338                    0.9415030941737551,
339                ];
340            }
341            //rejection=80dB, transition band=0.05
342            else if order == 6 {
343                a_coefficients = vec![0.06029739095712437, 0.4125907203610563, 0.7727156537429234];
344
345                b_coefficients = vec![0.21597144456092948, 0.6043586264658363, 0.9238861386532906];
346            }
347            //rejection=70dB,transition band=0.1
348            else if order == 4 {
349                a_coefficients = vec![0.07986642623635751, 0.5453536510711322];
350
351                b_coefficients = vec![0.28382934487410993, 0.8344118914807379];
352            }
353            //order=2, rejection=36dB, transition band=0.1
354            else {
355                a_coefficients = vec![0.23647102099689224];
356                b_coefficients = vec![0.7145421497126001];
357            }
358        }
359        let mut allpasses_a = [Allpass::default(); 6];
360        for i in 0..order / 2 {
361            allpasses_a[i].a = f32x4::splat(a_coefficients[i]);
362        }
363        let filter_a = AllpassCascade { num_filters: order / 2, allpasses: allpasses_a };
364        let mut allpasses_b = [Allpass::default(); 6];
365        for i in 0..order / 2 {
366            allpasses_b[i].a = f32x4::splat(b_coefficients[i]);
367        }
368        let filter_b = AllpassCascade { num_filters: order / 2, allpasses: allpasses_b };
369        PolyIIRHalfbandFilter { filter_a, filter_b, old_out: f32x4::splat(0.) }
370    }
371
372    pub fn process(&mut self, input: f32x4) -> f32x4 {
373        let output = (self.filter_a.process(input) + self.old_out) * f32x4::splat(0.5);
374        self.old_out = self.filter_b.process(input);
375        output
376    }
377}
378
379impl Default for PolyIIRHalfbandFilter {
380    fn default() -> PolyIIRHalfbandFilter {
381        let a_coefficients = vec![
382            0.01677466677723562,
383            0.13902148819717805,
384            0.3325011117394731,
385            0.53766105314488,
386            0.7214184024215805,
387            0.8821858402078155,
388        ];
389
390        let b_coefficients = vec![
391            0.06501319274445962,
392            0.23094129990840923,
393            0.4364942348420355,
394            0.6329609551399348, //0.06329609551399348
395            0.80378086794111226,
396            0.9599687404800694,
397        ];
398        let mut allpasses_a = [Allpass::default(); 6];
399        for i in 0..12 / 2 {
400            allpasses_a[i].a = f32x4::splat(a_coefficients[i]);
401        }
402        let filter_a = AllpassCascade { num_filters: 12 / 2, allpasses: allpasses_a };
403        let mut allpasses_b = [Allpass::default(); 6];
404        for i in 0..12 / 2 {
405            allpasses_b[i].a = f32x4::splat(b_coefficients[i]);
406        }
407        let filter_b = AllpassCascade { num_filters: 12 / 2, allpasses: allpasses_b };
408        PolyIIRHalfbandFilter { filter_a, filter_b, old_out: f32x4::splat(0.0) }
409    }
410}