twang/ops/gate.rs
1// Copyright © 2018-2022 The Twang Contributors.
2//
3// Licensed under any of:
4// - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
5// - Boost Software License, Version 1.0 (https://www.boost.org/LICENSE_1_0.txt)
6// - MIT License (https://mit-license.org/)
7// At your choosing (See accompanying files LICENSE_APACHE_2_0.txt,
8// LICENSE_MIT.txt and LICENSE_BOOST_1_0.txt).
9
10use fon::chan::{Ch32, Channel};
11
12/// Noise gate.
13///
14/// - `input`: The signal the gate is being applied to.
15/// - `key`: The signal that is triggering the gate (often same as input).
16/// - `range`: How much the signal below the noise threshold is attenuated.
17/// - `open_threshold`: The level at which the gate opens.
18/// - `close_threshold`: The level at which the gate closes.
19/// - `attack`: How long it takes for gate to fully open (seconds).
20/// - `hold`: How long the signal is held before release once below threshold
21/// (seconds).
22/// - `release`: How long it takes for gate to fully close (seconds).
23#[derive(Debug, Clone, Copy, Default)]
24pub struct Gate {
25 /// The level of the gate (1.0 is fully closed, 0.0 is fully open).
26 level: f32,
27 /// Non-Infinity if gate is closing.
28 hold: f32,
29}
30
31impl Gate {
32 /// Create a new gate.
33 #[inline(always)]
34 pub fn new() -> Self {
35 Self::default()
36 }
37
38 /// Get next sample processed through the noise gate.
39 #[inline(always)]
40 pub fn step(&mut self, gate: &GateParams) -> Ch32 {
41 // If gate should open.
42 if gate.key.to_f32() >= gate.open_threshold.to_f32() {
43 // Adjust level based on attack parameter.
44 self.level = if gate.attack == 0.0 {
45 0.0
46 } else {
47 (self.level - (1.0 / 48_000.0) / gate.attack).max(0.0)
48 };
49 // Reset hold value now that open.
50 self.hold = f32::INFINITY;
51 }
52 // If gate should close.
53 if gate.key.to_f32() < gate.close_threshold.to_f32() {
54 // If gate state changes to closing, set hold time.
55 if self.hold == f32::INFINITY {
56 self.hold = gate.hold;
57 }
58 // If hold is over, start increasing attenuation level.
59 if self.hold == 0.0 {
60 // Adjust level based on release parameter.
61 self.level = if gate.release == 0.0 {
62 1.0
63 } else {
64 (self.level + (1.0 / 48_000.0) / gate.release).min(1.0)
65 };
66 }
67 }
68 // Adjust hold time.
69 if self.hold != f32::INFINITY {
70 self.hold = (self.hold - (1.0 / 48_000.0)).max(0.0);
71 }
72 // Calculate attenuation level.
73 let level = 1.0 - gate.range.to_f32() * self.level;
74 // Attenuate noise level of signal.
75 Ch32::from(level * gate.input.to_f32())
76 }
77}
78
79/// Parameters of the Noise Gate.
80#[derive(Debug, Copy, Clone)]
81pub struct GateParams {
82 /// The signal the gate is being applied to.
83 pub input: Ch32,
84 /// The signal that is triggering the gate (often same as input).
85 pub key: Ch32,
86 /// How much the signal below the noise threshold is attenuated (reduced).
87 ///
88 /// Set to 1 for silence below threshold, 0 makes the gate have no effect.
89 pub range: Ch32,
90 /// The level at which the gate opens.
91 pub open_threshold: Ch32,
92 /// The level at which the gate closes.
93 pub close_threshold: Ch32,
94 /// How long it takes for gate to fully open (seconds).
95 pub attack: f32,
96 /// How long the signal is held before release once below threshold
97 /// (seconds).
98 pub hold: f32,
99 /// How long it takes for gate to fully close (seconds).
100 pub release: f32,
101}