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    fn from_param_defaults() -> Self {
32        Self { level: 1.0 }
33    }
34}
35
36/// Gain processor - applies amplitude scaling to audio.
37///
38/// This is a simple but essential DSP building block that multiplies
39/// all samples by a gain factor.
40#[derive(Debug, Default)]
41pub struct GainDsp {
42    _sample_rate: f32,
43}
44
45impl Processor for GainDsp {
46    type Params = GainParams;
47
48    fn process(
49        &mut self,
50        buffer: &mut [&mut [f32]],
51        _transport: &Transport,
52        params: &Self::Params,
53    ) {
54        let gain = params.level;
55
56        for channel in buffer.iter_mut() {
57            for sample in channel.iter_mut() {
58                *sample *= gain;
59            }
60        }
61    }
62
63    fn set_sample_rate(&mut self, sample_rate: f32) {
64        self._sample_rate = sample_rate;
65    }
66
67    fn reset(&mut self) {
68        // No state to reset
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_unity_gain() {
78        let mut processor = GainDsp::default();
79        let mut left = [0.5, -0.5, 0.25];
80        let mut right = [0.3, -0.3, 0.1];
81        let mut buffer = [&mut left[..], &mut right[..]];
82
83        let params = GainParams { level: 1.0 };
84        let transport = Transport::default();
85
86        processor.process(&mut buffer, &transport, &params);
87
88        assert!((left[0] - 0.5).abs() < 1e-6);
89        assert!((left[1] + 0.5).abs() < 1e-6);
90        assert!((right[0] - 0.3).abs() < 1e-6);
91    }
92
93    #[test]
94    fn test_boost() {
95        let mut processor = GainDsp::default();
96        let mut left = [1.0];
97        let mut buffer = [&mut left[..]];
98
99        let params = GainParams { level: 2.0 };
100        let transport = Transport::default();
101
102        processor.process(&mut buffer, &transport, &params);
103
104        assert!((left[0] - 2.0).abs() < 1e-6);
105    }
106
107    #[test]
108    fn test_attenuation() {
109        let mut processor = GainDsp::default();
110        let mut left = [1.0];
111        let mut buffer = [&mut left[..]];
112
113        let params = GainParams { level: 0.5 };
114        let transport = Transport::default();
115
116        processor.process(&mut buffer, &transport, &params);
117
118        assert!((left[0] - 0.5).abs() < 1e-6);
119    }
120
121    #[test]
122    fn test_param_specs() {
123        let specs = GainParams::param_specs();
124        assert_eq!(specs.len(), 1);
125        assert_eq!(specs[0].name, "Level");
126        assert_eq!(specs[0].id_suffix, "level");
127        assert_eq!(specs[0].default, 1.0);
128        assert_eq!(specs[0].unit, "x");
129    }
130
131    #[test]
132    fn test_from_param_defaults_uses_spec_default() {
133        let defaults = GainParams::from_param_defaults();
134        assert!((defaults.level - 1.0).abs() < 1e-6);
135    }
136}