diff --git a/plugins/spectral_compressor/src/compressor_bank.rs b/plugins/spectral_compressor/src/compressor_bank.rs index 3ab2f217..109bfc1d 100644 --- a/plugins/spectral_compressor/src/compressor_bank.rs +++ b/plugins/spectral_compressor/src/compressor_bank.rs @@ -34,6 +34,10 @@ pub struct CompressorBank { /// The same as `should_update_downwards_ratios`, but for upwards ratios. pub should_update_upwards_ratios: Arc, + /// For each compressor bin, `log2(freq)` where `freq` is the frequency associated with that + /// compressor. This is precomputed since all update functions need it. + log2_freqs: Vec, + /// Downwards compressor thresholds, in linear space. downwards_thresholds: Vec, /// Upwards compressor thresholds, in linear space. @@ -336,6 +340,8 @@ impl CompressorBank { should_update_downwards_ratios: Arc::new(AtomicBool::new(true)), should_update_upwards_ratios: Arc::new(AtomicBool::new(true)), + log2_freqs: Vec::with_capacity(complex_buffer_len), + downwards_thresholds: Vec::with_capacity(complex_buffer_len), upwards_thresholds: Vec::with_capacity(complex_buffer_len), downwards_ratios: Vec::with_capacity(complex_buffer_len), @@ -350,6 +356,9 @@ impl CompressorBank { pub fn update_capacity(&mut self, num_channels: usize, max_window_size: usize) { let complex_buffer_len = max_window_size / 2 + 1; + self.log2_freqs + .reserve_exact(complex_buffer_len.saturating_sub(self.log2_freqs.len())); + self.downwards_thresholds .reserve_exact(complex_buffer_len.saturating_sub(self.downwards_thresholds.len())); self.upwards_thresholds @@ -365,12 +374,21 @@ impl CompressorBank { } } - /// Resize the number of compressors to match the current window size. + /// Resize the number of compressors to match the current window size. Also precomputes the + /// 2-log frequencies for each bin. /// /// If the window size is larger than the maximum window size, then this will allocate. - pub fn resize(&mut self, window_size: usize) { + pub fn resize(&mut self, buffer_config: &BufferConfig, window_size: usize) { let complex_buffer_len = window_size / 2 + 1; + // These 2-log frequencies are needed when updating the compressor parameters, so we'll just + // precompute them to avoid having to repeat the same expensive computations all the time + self.log2_freqs.resize(complex_buffer_len, 0.0); + for (i, log2_freq) in self.log2_freqs.iter_mut().enumerate() { + let freq = (i as f32 / window_size as f32) * buffer_config.sample_rate; + *log2_freq = freq.log2(); + } + self.downwards_thresholds.resize(complex_buffer_len, 1.0); self.upwards_thresholds.resize(complex_buffer_len, 1.0); self.downwards_ratios.resize(complex_buffer_len, 1.0); diff --git a/plugins/spectral_compressor/src/lib.rs b/plugins/spectral_compressor/src/lib.rs index eb3a3ba8..695def85 100644 --- a/plugins/spectral_compressor/src/lib.rs +++ b/plugins/spectral_compressor/src/lib.rs @@ -48,6 +48,9 @@ struct SpectralCompressor { params: Arc, editor_state: Arc, + /// The current buffer config, used for updating the compressors. + buffer_config: BufferConfig, + /// An adapter that performs most of the overlap-add algorithm for us. stft: util::StftHelper, /// Contains a Hann window function of the current window length, passed to the overlap-add @@ -122,6 +125,13 @@ impl Default for SpectralCompressor { params: Arc::new(SpectralCompressorParams::new(&compressor_bank)), editor_state: editor::default_state(), + buffer_config: BufferConfig { + sample_rate: 1.0, + min_buffer_size: None, + max_buffer_size: 0, + process_mode: ProcessMode::Realtime, + }, + // These three will be set to the correct values in the initialize function stft: util::StftHelper::new(Self::DEFAULT_NUM_OUTPUTS as usize, MAX_WINDOW_SIZE, 0), window_function: Vec::with_capacity(MAX_WINDOW_SIZE), @@ -222,6 +232,9 @@ impl Plugin for SpectralCompressor { buffer_config: &BufferConfig, context: &mut impl InitContext, ) -> bool { + // Needed to update the compressors later + self.buffer_config = *buffer_config; + // This plugin can accept any number of channels, so we need to resize channel-dependent // data structures accordinly if self.stft.num_channels() != bus_config.num_output_channels as usize { @@ -284,7 +297,6 @@ impl Plugin for SpectralCompressor { let fft_plan = &mut self.plan_for_order.as_mut().unwrap() [self.params.window_size_order.value as usize - MIN_WINDOW_ORDER]; let num_bins = self.complex_fft_buffer.len(); - let sample_rate = context.transport().sample_rate; // The overlap gain compensation is based on a squared Hann window, which will sum perfectly // at four times overlap or higher. We'll apply a regular Hann window before the analysis @@ -329,8 +341,9 @@ impl Plugin for SpectralCompressor { if self.params.dc_filter.value { // The Hann window function spreads the DC signal out slightly, so we'll clear // all 0-20 Hz bins for this. - let highest_dcish_bin_idx = - (20.0 / ((sample_rate / 2.0) / num_bins as f32)).floor() as usize; + let highest_dcish_bin_idx = (20.0 + / ((self.buffer_config.sample_rate / 2.0) / num_bins as f32)) + .floor() as usize; self.complex_fft_buffer[..highest_dcish_bin_idx + 1].fill(Complex32::default()); } @@ -386,7 +399,8 @@ impl SpectralCompressor { .resize(window_size / 2 + 1, Complex32::default()); // This also causes the thresholds and ratios to be updated on the next STFT process cycle. - self.compressor_bank.resize(window_size); + self.compressor_bank + .resize(&self.buffer_config, window_size); } }