math_audio_dsp/
envelope.rs1#[derive(Debug, Clone, Copy)]
13pub struct DualRelease {
14 fast_coeff: f32,
15 slow_coeff: f32,
16 current_coeff: f32,
18 sustain_tracker: f32,
20 sustain_coeff: f32,
22 prev_gr: f32,
24}
25
26impl DualRelease {
27 pub fn new(fast_ms: f32, slow_ms: f32, sample_rate: u32) -> Self {
33 Self {
34 fast_coeff: Self::time_to_coeff(fast_ms, sample_rate),
35 slow_coeff: Self::time_to_coeff(slow_ms, sample_rate),
36 current_coeff: Self::time_to_coeff(fast_ms, sample_rate),
37 sustain_tracker: 0.0,
38 sustain_coeff: Self::time_to_coeff(200.0, sample_rate), prev_gr: 0.0,
40 }
41 }
42
43 fn time_to_coeff(time_ms: f32, sample_rate: u32) -> f32 {
44 if time_ms <= 0.0 {
45 0.0
46 } else {
47 (-1.0 / (time_ms * 0.001 * sample_rate as f32)).exp()
48 }
49 }
50
51 #[inline]
55 pub fn process(&mut self, gain_reduction_db: f32) -> f32 {
56 let delta = (gain_reduction_db - self.prev_gr).abs();
58 self.prev_gr = gain_reduction_db;
59
60 let sustained = if delta < 0.01 { 1.0 } else { 0.0 };
64 self.sustain_tracker = sustained + self.sustain_coeff * (self.sustain_tracker - sustained);
65
66 let blend = self.sustain_tracker.clamp(0.0, 1.0);
68 self.current_coeff = self.fast_coeff * (1.0 - blend) + self.slow_coeff * blend;
69
70 self.current_coeff
71 }
72
73 #[inline]
75 pub fn coeff(&self) -> f32 {
76 self.current_coeff
77 }
78
79 pub fn set_times(&mut self, fast_ms: f32, slow_ms: f32, sample_rate: u32) {
81 self.fast_coeff = Self::time_to_coeff(fast_ms, sample_rate);
82 self.slow_coeff = Self::time_to_coeff(slow_ms, sample_rate);
83 }
84
85 pub fn reset(&mut self) {
86 self.sustain_tracker = 0.0;
87 self.prev_gr = 0.0;
88 self.current_coeff = self.fast_coeff;
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_fast_release_on_transient() {
98 let mut dr = DualRelease::new(50.0, 500.0, 48000);
99 let coeff = dr.process(20.0);
101 let expected_fast = (-1.0f32 / (50.0 * 0.001 * 48000.0)).exp();
103 assert!((coeff - expected_fast).abs() < 0.01);
104 }
105
106 #[test]
107 fn test_slow_release_on_sustained() {
108 let mut dr = DualRelease::new(50.0, 500.0, 48000);
109 for _ in 0..48000 {
111 dr.process(10.0);
112 }
113 let coeff = dr.coeff();
114 let expected_slow = (-1.0f32 / (500.0 * 0.001 * 48000.0)).exp();
115 assert!((coeff - expected_slow).abs() < 0.01);
117 }
118
119 #[test]
120 fn test_reset() {
121 let mut dr = DualRelease::new(50.0, 500.0, 48000);
122 for _ in 0..10000 {
123 dr.process(10.0);
124 }
125 dr.reset();
126 assert_eq!(dr.sustain_tracker, 0.0);
127 assert_eq!(dr.prev_gr, 0.0);
128 }
129}