caw_modules/
compressor.rs1use caw_builder_proc_macros::builder;
2use caw_core::{Buf, Filter, SigCtx, SigT};
3use itertools::izip;
4
5builder! {
6 #[constructor = "compressor"]
7 #[constructor_doc = "Soft clamps a signal within a configurable threshold"]
8 #[generic_setter_type_name = "X"]
9 pub struct Props {
10 #[generic_with_constraint = "SigT<Item = f32>"]
12 #[generic_name = "T"]
13 #[default = 1.0]
14 threshold: f32,
15 #[generic_with_constraint = "SigT<Item = f32>"]
16 #[generic_name = "R"]
17 #[default = 0.0]
18 ratio: f32,
19 #[generic_with_constraint = "SigT<Item = f32>"]
21 #[generic_name = "S"]
22 #[default = 1.0]
23 scale: f32,
24 }
25}
26
27impl<T, R, S> Filter for Props<T, R, S>
28where
29 T: SigT<Item = f32>,
30 R: SigT<Item = f32>,
31 S: SigT<Item = f32>,
32{
33 type ItemIn = f32;
34
35 type Out<I>
36 = Compressor<I, T, R, S>
37 where
38 I: SigT<Item = Self::ItemIn>;
39
40 fn into_sig<I>(self, sig: I) -> Self::Out<I>
41 where
42 I: SigT<Item = Self::ItemIn>,
43 {
44 Compressor {
45 props: self,
46 sig,
47 buf: Vec::new(),
48 }
49 }
50}
51
52pub struct Compressor<I, T, R, S>
53where
54 I: SigT<Item = f32>,
55 T: SigT<Item = f32>,
56 R: SigT<Item = f32>,
57 S: SigT<Item = f32>,
58{
59 props: Props<T, R, S>,
60 sig: I,
61 buf: Vec<f32>,
62}
63
64impl<I, T, R, S> SigT for Compressor<I, T, R, S>
65where
66 I: SigT<Item = f32>,
67 T: SigT<Item = f32>,
68 R: SigT<Item = f32>,
69 S: SigT<Item = f32>,
70{
71 type Item = f32;
72
73 fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
74 self.buf.resize(ctx.num_samples, 0.0);
75 let sig = self.sig.sample(ctx);
76 let threshold = self.props.threshold.sample(ctx);
77 let ratio = self.props.ratio.sample(ctx);
78 let scale = self.props.scale.sample(ctx);
79 for (out, sample, threshold, ratio, scale) in izip! {
80 self.buf.iter_mut(),
81 sig.iter(),
82 threshold.iter(),
83 ratio.iter(),
84 scale.iter(),
85 } {
86 let sample = sample * scale;
87 let sample_abs = sample.abs();
88 *out = if sample_abs > threshold {
89 let delta = sample_abs - threshold;
90 let delta_scaled = delta * ratio;
91 (threshold + delta_scaled) * sample.signum()
92 } else {
93 sample
94 };
95 }
96 &self.buf
97 }
98}