audio_gate/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//!
//!
//!
//!
//!
//!
//!
//!
//!

/// The Noise Gate
/// This should be implemented outside of your main audio loop so that the open/close thresholds, and other settings can persist across the stream
pub struct NoiseGate {
    /// The open threshold as db (eg -36.0)
    open_threshold: f32,
    /// The close threshold as db (eg -56.0)
    close_threshold: f32,
    /// The sample rate in hz (eg 48000.0)
    sample_rate: f32,
    /// The relesae rate, in ms (eg 150)
    release_rate: f32,
    /// The attack rate in ms
    attack_rate: f32,
    decay_rate: f32,
    /// How long the gate should be held open for in ms
    hold_time: f32,
    /// The number of audio channels in your stream
    channels: usize,
    is_open: bool,
    attenuation: f32,
    level: f32,
    held_time: f32,
}

impl NoiseGate {
    /// Create a new noise gate.
    pub fn new(
        open_threshold: f32,
        close_threshold: f32,
        sample_rate: f32,
        channels: usize,
        release_rate: f32,
        attack_rate: f32,
        hold_time: f32
    ) -> Self {
        let threshold_diff = open_threshold - close_threshold;
        let min_decay_period = (1.0 / 75.0) * sample_rate;

        Self {
            open_threshold: match open_threshold.is_finite() {
                true => (10_f32).powf(open_threshold / 20.0),
                false => 0.0,
            },
            close_threshold: match close_threshold.is_finite() {
                true => (10_f32).powf(close_threshold / 20.0),
                false => 0.0,
            },
            sample_rate: 1.0 / sample_rate,
            channels: channels,
            release_rate: 1.0 / (release_rate * 0.001 * sample_rate),
            attack_rate: 1.0 / (attack_rate * 0.001 * sample_rate),
            decay_rate: threshold_diff / min_decay_period,
            hold_time: hold_time * 0.001,
            is_open: false,
            attenuation: 0.0,
            level: 0.0,
            held_time: 0.0,
        }
    }

    /// Takes a frame and returns a new frame that has been attenuated by the gate
    pub fn process_frame(&mut self, frame: &[f32]) -> Vec<f32> {
        let mut channel_frames = Vec::<Vec<f32>>::new();
        for _ in 0..self.channels {
            channel_frames.push(Vec::<f32>::with_capacity(frame.len() / self.channels));
        }

        for c in 0..self.channels {
            for (_, u) in frame.iter().enumerate().skip(c).step_by(self.channels) {
                channel_frames[c].push(*u);
            }
        }

        let mut resample = Vec::<f32>::with_capacity(frame.len());

        for i in 0..channel_frames[0].len() {
            let mut current_level = f32::abs(channel_frames[0][i]);

            for j in 0..self.channels {
                current_level = f32::max(current_level, channel_frames[j][i]);
            }

            if current_level > self.open_threshold && !self.is_open {
                self.is_open = true;
            }

            if self.level < self.close_threshold && self.is_open {
                self.held_time = 0.0;
                self.is_open = false;
            }

            self.level = f32::max(self.level, current_level) - self.decay_rate;

            if self.is_open {
                self.attenuation = f32::min(1.0, self.attenuation + self.attack_rate);
            } else {
                self.held_time += self.sample_rate;
                if self.held_time > self.hold_time {
                    self.attenuation = f32::max(0.0, self.attenuation - self.release_rate);
                }
            }

            for c in 0..self.channels {
                channel_frames[c][i] *= self.attenuation;
            }
        }

        // We need to flatten this back down to a single vec
        // For each channel
        // Grab the next element and push it to resample
        for i in 0..channel_frames[0].len() {
            for c in 0..self.channels {
                resample.push(channel_frames[c][i]);
            }
        }

        return resample.into();
    }
}