caw_modules/
envelope_follower.rs

1use crate::low_pass_butterworth::{self, LowPassButterworth};
2use caw_builder_proc_macros::builder;
3use caw_core::{Buf, Filter, Sig, SigAbs, SigCtx, SigT};
4
5pub const DEFAULT_SENSITIVITY_HZ: f32 = 60.0;
6
7builder! {
8    #[constructor = "envelope_follower"]
9    #[constructor_doc = "Approximates the loudness of its input signal"]
10    #[generic_setter_type_name = "X"]
11    pub struct Props {
12        #[generic_with_constraint = "SigT<Item = f32>"]
13        #[generic_name = "S"]
14        #[default = DEFAULT_SENSITIVITY_HZ]
15        sensitivity_hz: f32,
16    }
17}
18
19impl<S> Filter for Props<S>
20where
21    S: SigT<Item = f32>,
22{
23    type ItemIn = f32;
24
25    type Out<I>
26        = EnvelopeFollower<I, S>
27    where
28        I: SigT<Item = Self::ItemIn>;
29
30    fn into_sig<I>(self, sig: I) -> Self::Out<I>
31    where
32        I: SigT<Item = Self::ItemIn>,
33    {
34        let low_pass_filter =
35            low_pass_butterworth::low_pass_butterworth(self.sensitivity_hz)
36                .into_sig(Sig(sig).abs().0);
37        EnvelopeFollower { low_pass_filter }
38    }
39}
40
41pub struct EnvelopeFollower<I, S>
42where
43    I: SigT<Item = f32>,
44    S: SigT<Item = f32>,
45{
46    // apply a low pass filter to the absolute value of samples from the input
47    low_pass_filter: LowPassButterworth<SigAbs<I>, S>,
48}
49
50impl<I, S> SigT for EnvelopeFollower<I, S>
51where
52    I: SigT<Item = f32>,
53    S: SigT<Item = f32>,
54{
55    type Item = f32;
56
57    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
58        self.low_pass_filter.sample(ctx)
59    }
60}