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