Add a not very functional process function
This commit is contained in:
parent
9aa4a64e5f
commit
147cf3f633
|
@ -18,6 +18,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
use std::sync::Arc;
|
||||
|
||||
use nih_plug::prelude::*;
|
||||
use realfft::num_complex::Complex32;
|
||||
|
||||
/// Type alias for the compressor parameters. These two are split up so the parameter list/tree
|
||||
/// looks a bit nicer.
|
||||
|
@ -58,7 +59,12 @@ pub struct CompressorBank {
|
|||
/// The current envelope value for this bin, in linear space. Indexed by
|
||||
/// `[channel_idx][compressor_idx]`.
|
||||
envelopes: Vec<Vec<f32>>,
|
||||
// TODO: Parameters for the envelope followers so we can actuall ydo soemthing useful.
|
||||
/// The window size this compressor bank was configured for. This is used to compute the
|
||||
/// coefficients for the envelope followers in the process function.
|
||||
window_size: usize,
|
||||
/// The sample rate this compressor bank was configured for. This is used to compute the
|
||||
/// coefficients for the envelope followers in the process function.
|
||||
sample_rate: f32,
|
||||
}
|
||||
|
||||
#[derive(Params)]
|
||||
|
@ -352,6 +358,8 @@ impl CompressorBank {
|
|||
upwards_ratios: Vec::with_capacity(complex_buffer_len),
|
||||
|
||||
envelopes: vec![Vec::with_capacity(complex_buffer_len); num_channels],
|
||||
window_size: 0,
|
||||
sample_rate: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,6 +410,9 @@ impl CompressorBank {
|
|||
envelopes.resize(complex_buffer_len, 0.0);
|
||||
}
|
||||
|
||||
self.window_size = window_size;
|
||||
self.sample_rate = buffer_config.sample_rate;
|
||||
|
||||
// The compressors need to be updated on the next processing cycle
|
||||
self.should_update_downwards_thresholds
|
||||
.store(true, Ordering::SeqCst);
|
||||
|
@ -420,6 +431,42 @@ impl CompressorBank {
|
|||
}
|
||||
}
|
||||
|
||||
/// Apply the magnitude compression to a buffer of FFT bins. The compressors are first updated
|
||||
/// if needed. The overlap amount is needed to compute the effective sample rate. The
|
||||
/// `skip_bins_below` argument is used to avoid compressing DC bins, or the neighbouring bins
|
||||
/// the DC signal may have been convolved into because of the Hann window function.
|
||||
pub fn process(
|
||||
&mut self,
|
||||
buffer: &mut [Complex32],
|
||||
params @ (_, compressor): CompressorParams,
|
||||
overlap_times: usize,
|
||||
skip_bins_below: usize,
|
||||
) {
|
||||
assert_eq!(buffer.len(), self.log2_freqs.len());
|
||||
|
||||
self.update_if_needed(params);
|
||||
|
||||
// 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.
|
||||
// The effective sample rate needs to compensate for the periodic nature of the STFT
|
||||
// operation. Since with a 2048 sample window and 4x overlap, you'd run this function once
|
||||
// for every 512 samples.
|
||||
let effective_sample_rate =
|
||||
self.sample_rate / (self.window_size as f32 / overlap_times as f32);
|
||||
let attack_retention = (compressor.compressor_attack_ms.value / 1000.0
|
||||
* effective_sample_rate)
|
||||
.recip()
|
||||
.exp();
|
||||
// The same as `attack_retention`, but for the release phase of the envelope follower
|
||||
let release_retention = (compressor.compressor_release_ms.value / 1000.0
|
||||
* effective_sample_rate)
|
||||
.recip()
|
||||
.exp();
|
||||
|
||||
// TODO: Actually compress things
|
||||
}
|
||||
|
||||
/// Update the compressors if needed. This is called just before processing, and the compressors
|
||||
/// are updated in accordance to the atomic flags set on this struct.
|
||||
fn update_if_needed(&mut self, (threshold, compressor): CompressorParams) {
|
||||
|
|
|
@ -298,6 +298,10 @@ 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();
|
||||
// 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 / ((self.buffer_config.sample_rate / 2.0) / num_bins as f32)).floor() as usize;
|
||||
|
||||
// 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
|
||||
|
@ -335,16 +339,17 @@ impl Plugin for SpectralCompressor {
|
|||
.process_with_scratch(real_fft_buffer, &mut self.complex_fft_buffer, &mut [])
|
||||
.unwrap();
|
||||
|
||||
// TODO: Do the thing
|
||||
// This is where the magic happens
|
||||
self.compressor_bank.process(
|
||||
&mut self.complex_fft_buffer,
|
||||
(&self.params.threhold, &self.params.compressors),
|
||||
overlap_times,
|
||||
highest_dcish_bin_idx,
|
||||
);
|
||||
|
||||
// The DC and other low frequency bins doesn't contain much semantic meaning anymore
|
||||
// after all of this, so it only ends up consuming headroom.
|
||||
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
|
||||
/ ((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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue