audio_gate/
lib.rs

1//!
2//!
3//!
4//!
5//!
6//!
7//!
8//!
9//!
10
11/// The Noise Gate
12/// This should be implemented outside of your main audio loop so that the open/close thresholds, and other settings can persist across the stream
13pub struct NoiseGate {
14    /// The open threshold as db (eg -36.0)
15    open_threshold: f32,
16    /// The close threshold as db (eg -56.0)
17    close_threshold: f32,
18    /// The sample rate in hz (eg 48000.0)
19    sample_rate: f32,
20    /// The relesae rate, in ms (eg 150)
21    release_rate: f32,
22    /// The attack rate in ms
23    attack_rate: f32,
24    decay_rate: f32,
25    /// How long the gate should be held open for in ms
26    hold_time: f32,
27    /// The number of audio channels in your stream
28    channels: usize,
29    is_open: bool,
30    attenuation: f32,
31    level: f32,
32    held_time: f32,
33}
34
35impl NoiseGate {
36    /// Create a new noise gate.
37    pub fn new(
38        open_threshold: f32,
39        close_threshold: f32,
40        sample_rate: f32,
41        channels: usize,
42        release_rate: f32,
43        attack_rate: f32,
44        hold_time: f32
45    ) -> Self {
46        let threshold_diff = open_threshold - close_threshold;
47        let min_decay_period = (1.0 / 75.0) * sample_rate;
48
49        Self {
50            open_threshold: match open_threshold.is_finite() {
51                true => (10_f32).powf(open_threshold / 20.0),
52                false => 0.0,
53            },
54            close_threshold: match close_threshold.is_finite() {
55                true => (10_f32).powf(close_threshold / 20.0),
56                false => 0.0,
57            },
58            sample_rate: 1.0 / sample_rate,
59            channels: channels,
60            release_rate: 1.0 / (release_rate * 0.001 * sample_rate),
61            attack_rate: 1.0 / (attack_rate * 0.001 * sample_rate),
62            decay_rate: threshold_diff / min_decay_period,
63            hold_time: hold_time * 0.001,
64            is_open: false,
65            attenuation: 0.0,
66            level: 0.0,
67            held_time: 0.0,
68        }
69    }
70
71    pub fn update(
72        &mut self,
73        open_threshold: f32,
74        close_threshold: f32,
75        release_rate: f32,
76        attack_rate: f32,
77        hold_time: f32
78    ) {
79        let threshold_diff = open_threshold - close_threshold;
80        let min_decay_period = (1.0 / 75.0) * self.sample_rate;
81
82        self.open_threshold = match open_threshold.is_finite() {
83            true => (10_f32).powf(open_threshold / 20.0),
84            false => 0.0,
85        };
86        self.close_threshold = match close_threshold.is_finite() {
87            true => (10_f32).powf(close_threshold / 20.0),
88            false => 0.0,
89        };
90        self.release_rate = 1.0 / (release_rate * 0.001 * self.sample_rate);
91        self.attack_rate = 1.0 / (attack_rate * 0.001 * self.sample_rate);
92        self.decay_rate = threshold_diff / min_decay_period;
93        self.hold_time = hold_time * 0.001;
94    }
95
96    /// Takes a frame and returns a new frame that has been attenuated by the gate
97    pub fn process_frame(&mut self, frame: &[f32]) -> Vec<f32> {
98        let mut channel_frames = Vec::<Vec<f32>>::new();
99        for _ in 0..self.channels {
100            channel_frames.push(Vec::<f32>::with_capacity(frame.len() / self.channels));
101        }
102
103        for c in 0..self.channels {
104            for (_, u) in frame.iter().enumerate().skip(c).step_by(self.channels) {
105                channel_frames[c].push(*u);
106            }
107        }
108
109        let mut resample = Vec::<f32>::with_capacity(frame.len());
110
111        for i in 0..channel_frames[0].len() {
112            let mut current_level = f32::abs(channel_frames[0][i]);
113
114            for j in 0..self.channels {
115                current_level = f32::max(current_level, channel_frames[j][i]);
116            }
117
118            if current_level > self.open_threshold && !self.is_open {
119                self.is_open = true;
120            }
121
122            if self.level < self.close_threshold && self.is_open {
123                self.held_time = 0.0;
124                self.is_open = false;
125            }
126
127            self.level = f32::max(self.level, current_level) - self.decay_rate;
128
129            if self.is_open {
130                self.attenuation = f32::min(1.0, self.attenuation + self.attack_rate);
131            } else {
132                self.held_time += self.sample_rate;
133                if self.held_time > self.hold_time {
134                    self.attenuation = f32::max(0.0, self.attenuation - self.release_rate);
135                }
136            }
137
138            for c in 0..self.channels {
139                channel_frames[c][i] *= self.attenuation;
140            }
141        }
142
143        // We need to flatten this back down to a single vec
144        // For each channel
145        // Grab the next element and push it to resample
146        for i in 0..channel_frames[0].len() {
147            for c in 0..self.channels {
148                resample.push(channel_frames[c][i]);
149            }
150        }
151
152        return resample.into();
153    }
154}