Skip to main content

wavecraft_dsp/builtins/
gain.rs

1//! Gain processor - amplifies or attenuates audio signals.
2
3use crate::traits::{Processor, ProcessorParams, Transport};
4
5/// Parameter struct for gain processor.
6#[derive(Debug, Default, Clone)]
7pub struct GainParams {
8    /// Gain level in linear amplitude (0.0 = silence, 1.0 = unity, >1.0 = boost).
9    pub level: f32,
10}
11
12impl ProcessorParams for GainParams {
13    fn param_specs() -> &'static [crate::traits::ParamSpec] {
14        use crate::traits::{ParamRange, ParamSpec};
15
16        static SPECS: [ParamSpec; 1] = [ParamSpec {
17            name: "Level",
18            id_suffix: "level",
19            range: ParamRange::Skewed {
20                min: 0.0,
21                max: 2.0,
22                factor: 2.5, // Logarithmic feel
23            },
24            default: 1.0,
25            unit: "x",
26            group: None,
27        }];
28        &SPECS
29    }
30}
31
32/// Gain processor - applies amplitude scaling to audio.
33///
34/// This is a simple but essential DSP building block that multiplies
35/// all samples by a gain factor.
36#[derive(Debug, Default)]
37pub struct GainDsp {
38    _sample_rate: f32,
39}
40
41impl Processor for GainDsp {
42    type Params = GainParams;
43
44    fn process(
45        &mut self,
46        buffer: &mut [&mut [f32]],
47        _transport: &Transport,
48        params: &Self::Params,
49    ) {
50        let gain = params.level;
51
52        for channel in buffer.iter_mut() {
53            for sample in channel.iter_mut() {
54                *sample *= gain;
55            }
56        }
57    }
58
59    fn set_sample_rate(&mut self, sample_rate: f32) {
60        self._sample_rate = sample_rate;
61    }
62
63    fn reset(&mut self) {
64        // No state to reset
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_unity_gain() {
74        let mut processor = GainDsp::default();
75        let mut left = [0.5, -0.5, 0.25];
76        let mut right = [0.3, -0.3, 0.1];
77        let mut buffer = [&mut left[..], &mut right[..]];
78
79        let params = GainParams { level: 1.0 };
80        let transport = Transport::default();
81
82        processor.process(&mut buffer, &transport, &params);
83
84        assert!((left[0] - 0.5).abs() < 1e-6);
85        assert!((left[1] + 0.5).abs() < 1e-6);
86        assert!((right[0] - 0.3).abs() < 1e-6);
87    }
88
89    #[test]
90    fn test_boost() {
91        let mut processor = GainDsp::default();
92        let mut left = [1.0];
93        let mut buffer = [&mut left[..]];
94
95        let params = GainParams { level: 2.0 };
96        let transport = Transport::default();
97
98        processor.process(&mut buffer, &transport, &params);
99
100        assert!((left[0] - 2.0).abs() < 1e-6);
101    }
102
103    #[test]
104    fn test_attenuation() {
105        let mut processor = GainDsp::default();
106        let mut left = [1.0];
107        let mut buffer = [&mut left[..]];
108
109        let params = GainParams { level: 0.5 };
110        let transport = Transport::default();
111
112        processor.process(&mut buffer, &transport, &params);
113
114        assert!((left[0] - 0.5).abs() < 1e-6);
115    }
116
117    #[test]
118    fn test_param_specs() {
119        let specs = GainParams::param_specs();
120        assert_eq!(specs.len(), 1);
121        assert_eq!(specs[0].name, "Level");
122        assert_eq!(specs[0].id_suffix, "level");
123        assert_eq!(specs[0].default, 1.0);
124        assert_eq!(specs[0].unit, "x");
125    }
126}