1
0
Fork 0

Snap envelopes to the current value after reset

This results in much less unexpected behavior.
This commit is contained in:
Robbert van der Helm 2023-03-22 12:19:29 +01:00
parent 7e04c118dd
commit f6ef95db3e
2 changed files with 42 additions and 15 deletions

View file

@ -16,6 +16,12 @@ Versioning](https://semver.org/spec/v2.0.0.html).
### Changed ### Changed
- The envelope follower resetting behavior has changed to immediately snap to
the current value after the plugin is reset. When the plugin resets after
being suspending or after changing the window size, previously the envelopes
would be reset to a fixed value. This could result in loud spikes when the
plugin resumed from suspend when using extreme ratios and threshold settings.
Resets are now handled much more gracefully.
- The default window overlap amount setting has changed to 16x. Existing patches - The default window overlap amount setting has changed to 16x. Existing patches
are not affected. are not affected.
- On Windows, clicking on the plugin's name no longer takes you to Spectral - On Windows, clicking on the plugin's name no longer takes you to Spectral

View file

@ -89,6 +89,11 @@ pub struct CompressorBank {
/// The current envelope value for this bin, in linear space. Indexed by /// The current envelope value for this bin, in linear space. Indexed by
/// `[channel_idx][compressor_idx]`. /// `[channel_idx][compressor_idx]`.
envelopes: Vec<Vec<f32>>, envelopes: Vec<Vec<f32>>,
/// This is set to `true` for the first cycle after [`CompressorBank::reset()`] was called. This
/// causes the timings to briefly be zero so they'll initialize to the buffer's current value.
/// This results in a more natural/less unexpected behavior because with extreme settings you
/// might otherwise get a huge spike after a reset
envelopes_were_reset: bool,
/// When sidechaining is enabled, this contains the per-channel frqeuency spectrum magnitudes /// When sidechaining is enabled, this contains the per-channel frqeuency spectrum magnitudes
/// for the current block. The compressor thresholds and knee values are multiplied by these /// for the current block. The compressor thresholds and knee values are multiplied by these
/// values to get the effective thresholds. /// values to get the effective thresholds.
@ -446,6 +451,7 @@ impl CompressorBank {
upwards_knee_parabola_intercept: Vec::with_capacity(complex_buffer_len), upwards_knee_parabola_intercept: Vec::with_capacity(complex_buffer_len),
envelopes: vec![Vec::with_capacity(complex_buffer_len); num_channels], envelopes: vec![Vec::with_capacity(complex_buffer_len); num_channels],
envelopes_were_reset: true,
sidechain_spectrum_magnitudes: vec![ sidechain_spectrum_magnitudes: vec![
Vec::with_capacity(complex_buffer_len); Vec::with_capacity(complex_buffer_len);
num_channels num_channels
@ -557,9 +563,10 @@ impl CompressorBank {
/// Clear out the envelope followers. /// Clear out the envelope followers.
pub fn reset(&mut self) { pub fn reset(&mut self) {
for envelopes in self.envelopes.iter_mut() { // This will make the timings instant for the first iteration after a reset so it can settle
envelopes.fill(ENVELOPE_INIT_VALUE); // in. Otherwise suspending and resetting the plugin, or changing the window size, may
} // result in some huge spikes.
self.envelopes_were_reset = true;
// Sidechain data doesn't need to be reset as it will be overwritten immediately before use // Sidechain data doesn't need to be reset as it will be overwritten immediately before use
} }
@ -678,6 +685,15 @@ impl CompressorBank {
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
overlap_times: usize, overlap_times: usize,
) { ) {
let (attack_ms, release_ms) = if self.envelopes_were_reset {
(0.0, 0.0)
} else {
(
params.global.compressor_attack_ms.value(),
params.global.compressor_release_ms.value(),
)
};
// The coefficient the old envelope value is multiplied by when the current rectified sample // The coefficient the old envelope value is multiplied by when the current rectified sample
// value is above the envelope's value. The 0 to 1 step response retains 36.8% of the old // value is above the envelope's value. The 0 to 1 step response retains 36.8% of the old
// value after the attack time has elapsed, and current value is 63.2% of the way towards 1. // value after the attack time has elapsed, and current value is 63.2% of the way towards 1.
@ -686,19 +702,17 @@ impl CompressorBank {
// for every 512 samples. // for every 512 samples.
let effective_sample_rate = let effective_sample_rate =
self.sample_rate / (self.window_size as f32 / overlap_times as f32); self.sample_rate / (self.window_size as f32 / overlap_times as f32);
let attack_old_t = if params.global.compressor_attack_ms.value() == 0.0 { let attack_old_t = if attack_ms == 0.0 {
0.0 0.0
} else { } else {
(-1.0 / (params.global.compressor_attack_ms.value() / 1000.0 * effective_sample_rate)) (-1.0 / (attack_ms / 1000.0 * effective_sample_rate)).exp()
.exp()
}; };
let attack_new_t = 1.0 - attack_old_t; let attack_new_t = 1.0 - attack_old_t;
// The same as `attack_old_t`, but for the release phase of the envelope follower // The same as `attack_old_t`, but for the release phase of the envelope follower
let release_old_t = if params.global.compressor_release_ms.value() == 0.0 { let release_old_t = if release_ms == 0.0 {
0.0 0.0
} else { } else {
(-1.0 / (params.global.compressor_release_ms.value() / 1000.0 * effective_sample_rate)) (-1.0 / (release_ms / 1000.0 * effective_sample_rate)).exp()
.exp()
}; };
let release_new_t = 1.0 - release_old_t; let release_new_t = 1.0 - release_old_t;
@ -724,21 +738,28 @@ impl CompressorBank {
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
overlap_times: usize, overlap_times: usize,
) { ) {
let (attack_ms, release_ms) = if self.envelopes_were_reset {
(0.0, 0.0)
} else {
(
params.global.compressor_attack_ms.value(),
params.global.compressor_release_ms.value(),
)
};
// See `update_envelopes()` // See `update_envelopes()`
let effective_sample_rate = let effective_sample_rate =
self.sample_rate / (self.window_size as f32 / overlap_times as f32); self.sample_rate / (self.window_size as f32 / overlap_times as f32);
let attack_old_t = if params.global.compressor_attack_ms.value() == 0.0 { let attack_old_t = if attack_ms == 0.0 {
0.0 0.0
} else { } else {
(-1.0 / (params.global.compressor_attack_ms.value() / 1000.0 * effective_sample_rate)) (-1.0 / (attack_ms / 1000.0 * effective_sample_rate)).exp()
.exp()
}; };
let attack_new_t = 1.0 - attack_old_t; let attack_new_t = 1.0 - attack_old_t;
let release_old_t = if params.global.compressor_release_ms.value() == 0.0 { let release_old_t = if release_ms == 0.0 {
0.0 0.0
} else { } else {
(-1.0 / (params.global.compressor_release_ms.value() / 1000.0 * effective_sample_rate)) (-1.0 / (release_ms / 1000.0 * effective_sample_rate)).exp()
.exp()
}; };
let release_new_t = 1.0 - release_old_t; let release_new_t = 1.0 - release_old_t;