From f6ef95db3e220093eb40b16f3cd0cfa9762a2f40 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 22 Mar 2023 12:19:29 +0100 Subject: [PATCH] Snap envelopes to the current value after reset This results in much less unexpected behavior. --- plugins/spectral_compressor/CHANGELOG.md | 6 +++ .../src/compressor_bank.rs | 51 +++++++++++++------ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/plugins/spectral_compressor/CHANGELOG.md b/plugins/spectral_compressor/CHANGELOG.md index 500bb1c1..b19a2d5b 100644 --- a/plugins/spectral_compressor/CHANGELOG.md +++ b/plugins/spectral_compressor/CHANGELOG.md @@ -16,6 +16,12 @@ Versioning](https://semver.org/spec/v2.0.0.html). ### 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 are not affected. - On Windows, clicking on the plugin's name no longer takes you to Spectral diff --git a/plugins/spectral_compressor/src/compressor_bank.rs b/plugins/spectral_compressor/src/compressor_bank.rs index 11b34b54..c91f6f0d 100644 --- a/plugins/spectral_compressor/src/compressor_bank.rs +++ b/plugins/spectral_compressor/src/compressor_bank.rs @@ -89,6 +89,11 @@ pub struct CompressorBank { /// The current envelope value for this bin, in linear space. Indexed by /// `[channel_idx][compressor_idx]`. envelopes: Vec>, + /// 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 /// for the current block. The compressor thresholds and knee values are multiplied by these /// values to get the effective thresholds. @@ -446,6 +451,7 @@ impl CompressorBank { upwards_knee_parabola_intercept: Vec::with_capacity(complex_buffer_len), envelopes: vec![Vec::with_capacity(complex_buffer_len); num_channels], + envelopes_were_reset: true, sidechain_spectrum_magnitudes: vec![ Vec::with_capacity(complex_buffer_len); num_channels @@ -557,9 +563,10 @@ impl CompressorBank { /// Clear out the envelope followers. pub fn reset(&mut self) { - for envelopes in self.envelopes.iter_mut() { - envelopes.fill(ENVELOPE_INIT_VALUE); - } + // This will make the timings instant for the first iteration after a reset so it can settle + // 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 } @@ -678,6 +685,15 @@ impl CompressorBank { params: &SpectralCompressorParams, 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 // 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. @@ -686,19 +702,17 @@ impl CompressorBank { // for every 512 samples. let effective_sample_rate = 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 } else { - (-1.0 / (params.global.compressor_attack_ms.value() / 1000.0 * effective_sample_rate)) - .exp() + (-1.0 / (attack_ms / 1000.0 * effective_sample_rate)).exp() }; let attack_new_t = 1.0 - attack_old_t; // 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 } else { - (-1.0 / (params.global.compressor_release_ms.value() / 1000.0 * effective_sample_rate)) - .exp() + (-1.0 / (release_ms / 1000.0 * effective_sample_rate)).exp() }; let release_new_t = 1.0 - release_old_t; @@ -724,21 +738,28 @@ impl CompressorBank { params: &SpectralCompressorParams, 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()` let effective_sample_rate = 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 } else { - (-1.0 / (params.global.compressor_attack_ms.value() / 1000.0 * effective_sample_rate)) - .exp() + (-1.0 / (attack_ms / 1000.0 * effective_sample_rate)).exp() }; 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 } else { - (-1.0 / (params.global.compressor_release_ms.value() / 1000.0 * effective_sample_rate)) - .exp() + (-1.0 / (release_ms / 1000.0 * effective_sample_rate)).exp() }; let release_new_t = 1.0 - release_old_t;