@@ -4,6 +4,8 @@ pub struct CompressorStage {
44 name : String ,
55 attack : f32 , // Attack coefficient (0-1)
66 release : f32 , // Release coefficient (0-1)
7+ attack_ms : f32 , // Attack time in milliseconds
8+ release_ms : f32 , // Release time in milliseconds
79 threshold : f32 , // Threshold in linear scale
810 ratio : f32 , // Compression ratio (e.g., 4.0 for 4:1)
911 makeup : f32 , // Makeup gain in linear scale
@@ -26,14 +28,17 @@ impl CompressorStage {
2628 makeup_db : f32 ,
2729 sample_rate : f32 ,
2830 ) -> Self {
29- // Convert ms to one-pole coefficients α = e^(−1/τ)
31+ // Convert ms to one-pole coefficients
32+ // For attack/release: smaller time constant = faster response = smaller coefficient
3033 let attack = ( -1.0 / ( sample_rate * 0.001 * attack_ms) ) . exp ( ) ;
3134 let release = ( -1.0 / ( sample_rate * 0.001 * release_ms) ) . exp ( ) ;
3235
3336 Self {
3437 name : name. to_string ( ) ,
3538 attack,
3639 release,
40+ attack_ms,
41+ release_ms,
3742 threshold : db_to_lin ( threshold_db) ,
3843 ratio,
3944 makeup : db_to_lin ( makeup_db) ,
@@ -42,10 +47,21 @@ impl CompressorStage {
4247 }
4348 }
4449
45- // Recalculate coefficients when parameters change
46- fn update_coefficients ( & mut self , attack_ms : f32 , release_ms : f32 ) {
47- self . attack = ( -1.0 / ( self . sample_rate * 0.001 * attack_ms) ) . exp ( ) ;
48- self . release = ( -1.0 / ( self . sample_rate * 0.001 * release_ms) ) . exp ( ) ;
50+ // Calculate coefficient from time constant in ms
51+ fn calculate_coefficient ( & self , time_ms : f32 ) -> f32 {
52+ ( -1.0 / ( self . sample_rate * 0.001 * time_ms) ) . exp ( )
53+ }
54+
55+ // Update just the attack time and coefficient
56+ fn update_attack ( & mut self , attack_ms : f32 ) {
57+ self . attack_ms = attack_ms;
58+ self . attack = self . calculate_coefficient ( attack_ms) ;
59+ }
60+
61+ // Update just the release time and coefficient
62+ fn update_release ( & mut self , release_ms : f32 ) {
63+ self . release_ms = release_ms;
64+ self . release = self . calculate_coefficient ( release_ms) ;
4965 }
5066}
5167
@@ -54,11 +70,18 @@ impl Stage for CompressorStage {
5470 // Envelope follower
5571 let level_in = input. abs ( ) . max ( 1e-10 ) ; // Avoid log(0)
5672
57- // Attack/release behavior
73+ // Attack/release behavior - using proper one-pole filter form:
74+ // y[n] = α·y[n-1] + (1-α)·x[n]
75+ //
76+ // Where α is closer to 1.0 for longer time constants
77+ // For attack (level_in > envelope): use attack coefficient (faster)
78+ // For release (level_in < envelope): use release coefficient (slower)
5879 if level_in > self . envelope {
59- self . envelope = self . attack * ( self . envelope - level_in) + level_in;
80+ // Attack phase - faster coefficient
81+ self . envelope = self . attack * self . envelope + ( 1.0 - self . attack ) * level_in;
6082 } else {
61- self . envelope = self . release * ( self . envelope - level_in) + level_in;
83+ // Release phase - slower coefficient
84+ self . envelope = self . release * self . envelope + ( 1.0 - self . release ) * level_in;
6285 }
6386
6487 // Compression gain calculation
@@ -94,15 +117,15 @@ impl Stage for CompressorStage {
94117 }
95118 "attack" => {
96119 if ( 0.1 ..=100.0 ) . contains ( & value) {
97- self . update_coefficients ( value, self . release ) ;
120+ self . update_attack ( value) ;
98121 Ok ( ( ) )
99122 } else {
100123 Err ( "Attack must be between 0.1 ms and 100 ms" )
101124 }
102125 }
103126 "release" => {
104127 if ( 10.0 ..=1000.0 ) . contains ( & value) {
105- self . update_coefficients ( self . attack , value) ;
128+ self . update_release ( value) ;
106129 Ok ( ( ) )
107130 } else {
108131 Err ( "Release must be between 10 ms and 1000 ms" )
@@ -124,9 +147,9 @@ impl Stage for CompressorStage {
124147 match name {
125148 "threshold" => Ok ( 20.0 * self . threshold . log10 ( ) ) , // Convert back to dB
126149 "ratio" => Ok ( self . ratio ) ,
127- "attack" => Ok ( - 1000.0 * self . attack . ln ( ) * self . sample_rate ) , // Convert back to ms
128- "release" => Ok ( - 1000.0 * self . release . ln ( ) * self . sample_rate ) , // Convert back to ms
129- "makeup" => Ok ( 20.0 * self . makeup . log10 ( ) ) , // Convert back to dB
150+ "attack" => Ok ( self . attack_ms ) , // Return stored ms value directly
151+ "release" => Ok ( self . release_ms ) , // Return stored ms value directly
152+ "makeup" => Ok ( 20.0 * self . makeup . log10 ( ) ) , // Convert back to dB
130153 _ => Err ( "Unknown parameter name" ) ,
131154 }
132155 }
0 commit comments