Skip to content

Commit dda166c

Browse files
committed
Compressor
1 parent 4568013 commit dda166c

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

src/sim/stages/compressor.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)