caw_modules/
down_sample.rs

1use caw_builder_proc_macros::builder;
2use caw_core::{Buf, Filter, SigCtx, SigT};
3use itertools::izip;
4
5builder! {
6    #[constructor = "down_sample"]
7    #[constructor_doc = "Artificially reduce the sample rate of a signal"]
8    #[generic_setter_type_name = "X"]
9    pub struct Props {
10        // If the input signal exceeds the threshold then the excess will be multiplied by the ratio.
11        #[generic_with_constraint = "SigT<Item = f32>"]
12        #[generic_name = "C"]
13        scale: _,
14    }
15}
16
17impl<C> Filter for Props<C>
18where
19    C: SigT<Item = f32>,
20{
21    type ItemIn = f32;
22
23    type Out<S>
24        = Downsample<C, S>
25    where
26        S: SigT<Item = Self::ItemIn>;
27
28    fn into_sig<S>(self, sig: S) -> Self::Out<S>
29    where
30        S: SigT<Item = Self::ItemIn>,
31    {
32        Downsample {
33            props: self,
34            sig,
35            buf: Vec::new(),
36            prev_input_sample: 0.0,
37            prev_output_sample: 0.0,
38            remaining_samples: 0.0,
39        }
40    }
41}
42
43pub struct Downsample<C, S>
44where
45    C: SigT<Item = f32>,
46    S: SigT<Item = f32>,
47{
48    props: Props<C>,
49    sig: S,
50    buf: Vec<f32>,
51    prev_input_sample: f32,
52    prev_output_sample: f32,
53    remaining_samples: f32,
54}
55
56impl<C, S> SigT for Downsample<C, S>
57where
58    C: SigT<Item = f32>,
59    S: SigT<Item = f32>,
60{
61    type Item = f32;
62
63    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
64        self.buf.resize(ctx.num_samples, 0.0);
65        let sig = self.sig.sample(ctx);
66        let scale = self.props.scale.sample(ctx);
67        for (out, sample, scale) in izip! {
68            self.buf.iter_mut(),
69            sig.iter(),
70            scale.iter(),
71        } {
72            let scale = scale.max(1.0);
73            if self.remaining_samples < 1.0 {
74                self.remaining_samples = self.remaining_samples.max(0.0);
75                self.prev_output_sample = (self.prev_input_sample
76                    * self.remaining_samples)
77                    + (sample * (1.0 - self.remaining_samples));
78                // linearly interpolate between the current and previous samples
79                *out = self.prev_output_sample;
80                self.remaining_samples = scale;
81            } else {
82                *out = self.prev_output_sample;
83                self.remaining_samples -= 1.0;
84            }
85            self.prev_input_sample = sample;
86        }
87        &self.buf
88    }
89}