Snap envelopes to the current value after reset
This results in much less unexpected behavior.
This commit is contained in:
parent
7e04c118dd
commit
f6ef95db3e
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue