1
0
Fork 0

Apply downwards compression to DC bins

In Spectral Compressor. We avoided this because it messes up upwards
compression, but downwards compression is perfectly fine.
This commit is contained in:
Robbert van der Helm 2022-09-04 15:07:23 +02:00
parent 5faed9e2e7
commit 08b1e43a15

View file

@ -512,39 +512,35 @@ impl CompressorBank {
/// Apply the magnitude compression to a buffer of FFT bins. The compressors are first updated /// 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 /// 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 /// `first_non_dc_bin` argument is used to avoid upwards compression on the DC bins, or the
/// the DC signal may have been convolved into because of the Hann window function. /// neighbouring bins the DC signal may have been convolved into because of the Hann window
/// function.
pub fn process( pub fn process(
&mut self, &mut self,
buffer: &mut [Complex32], buffer: &mut [Complex32],
channel_idx: usize, channel_idx: usize,
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
overlap_times: usize, overlap_times: usize,
skip_bins_below: usize, first_non_dc_bin: usize,
) { ) {
nih_debug_assert_eq!(buffer.len(), self.log2_freqs.len()); nih_debug_assert_eq!(buffer.len(), self.log2_freqs.len());
self.update_if_needed(params); self.update_if_needed(params);
match params.threshold.mode.value() { match params.threshold.mode.value() {
ThresholdMode::Internal => { ThresholdMode::Internal => {
self.update_envelopes(buffer, channel_idx, params, overlap_times, skip_bins_below); self.update_envelopes(buffer, channel_idx, params, overlap_times);
self.compress(buffer, channel_idx, params, skip_bins_below) self.compress(buffer, channel_idx, params, first_non_dc_bin)
} }
ThresholdMode::SidechainMatch => { ThresholdMode::SidechainMatch => {
self.update_envelopes(buffer, channel_idx, params, overlap_times, skip_bins_below); self.update_envelopes(buffer, channel_idx, params, overlap_times);
self.compress_sidechain_match(buffer, channel_idx, params, skip_bins_below) self.compress_sidechain_match(buffer, channel_idx, params, first_non_dc_bin)
} }
ThresholdMode::SidechainCompress => { ThresholdMode::SidechainCompress => {
// This mode uses regular compression, but the envelopes are computed from the // This mode uses regular compression, but the envelopes are computed from the
// sidechain input magnitudes. These are already set in `process_sidechain`. This // sidechain input magnitudes. These are already set in `process_sidechain`. This
// separate envelope updating function is needed for the channel linking. // separate envelope updating function is needed for the channel linking.
self.update_envelopes_sidechain( self.update_envelopes_sidechain(channel_idx, params, overlap_times);
channel_idx, self.compress(buffer, channel_idx, params, first_non_dc_bin)
params,
overlap_times,
skip_bins_below,
);
self.compress(buffer, channel_idx, params, skip_bins_below)
} }
}; };
} }
@ -565,7 +561,6 @@ impl CompressorBank {
channel_idx: usize, channel_idx: usize,
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
overlap_times: usize, overlap_times: usize,
skip_bins_below: usize,
) { ) {
// 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
@ -591,11 +586,7 @@ impl CompressorBank {
}; };
let release_new_t = 1.0 - release_old_t; let release_new_t = 1.0 - release_old_t;
for (bin, envelope) in buffer for (bin, envelope) in buffer.iter().zip(self.envelopes[channel_idx].iter_mut()) {
.iter()
.zip(self.envelopes[channel_idx].iter_mut())
.skip(skip_bins_below)
{
let magnitude = bin.norm(); let magnitude = bin.norm();
if *envelope > magnitude { if *envelope > magnitude {
// Release stage // Release stage
@ -616,7 +607,6 @@ impl CompressorBank {
channel_idx: usize, channel_idx: usize,
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
overlap_times: usize, overlap_times: usize,
skip_bins_below: usize,
) { ) {
// See `update_envelopes()` // See `update_envelopes()`
let effective_sample_rate = let effective_sample_rate =
@ -641,11 +631,7 @@ impl CompressorBank {
let other_channels_t = params.threshold.sc_channel_link.value / num_channels; let other_channels_t = params.threshold.sc_channel_link.value / num_channels;
let this_channel_t = 1.0 - (other_channels_t * (num_channels - 1.0)); let this_channel_t = 1.0 - (other_channels_t * (num_channels - 1.0));
for (bin_idx, envelope) in self.envelopes[channel_idx] for (bin_idx, envelope) in self.envelopes[channel_idx].iter_mut().enumerate() {
.iter_mut()
.enumerate()
.skip(skip_bins_below)
{
// In this mode the envelopes are set based on the sidechain signal, taking channel // In this mode the envelopes are set based on the sidechain signal, taking channel
// linking into account // linking into account
let sidechain_magnitude: f32 = self let sidechain_magnitude: f32 = self
@ -697,7 +683,7 @@ impl CompressorBank {
buffer: &mut [Complex32], buffer: &mut [Complex32],
channel_idx: usize, channel_idx: usize,
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
skip_bins_below: usize, first_non_dc_bin: usize,
) { ) {
// Well I'm not sure at all why this scaling works, but it does. With higher knee // Well I'm not sure at all why this scaling works, but it does. With higher knee
// bandwidths, the middle values needs to be pushed more towards the post-knee threshold // bandwidths, the middle values needs to be pushed more towards the post-knee threshold
@ -723,7 +709,6 @@ impl CompressorBank {
.iter_mut() .iter_mut()
.zip(self.envelopes[channel_idx].iter()) .zip(self.envelopes[channel_idx].iter())
.enumerate() .enumerate()
.skip(skip_bins_below)
{ {
// This works by computing a scaling factor, and then scaling the bin magnitudes by that. // This works by computing a scaling factor, and then scaling the bin magnitudes by that.
let mut scale = 1.0; let mut scale = 1.0;
@ -752,7 +737,7 @@ impl CompressorBank {
let upwards_ratio_recip = unsafe { self.upwards_ratio_recips.get_unchecked(bin_idx) }; let upwards_ratio_recip = unsafe { self.upwards_ratio_recips.get_unchecked(bin_idx) };
let upwards_knee_start = unsafe { self.upwards_knee_starts.get_unchecked(bin_idx) }; let upwards_knee_start = unsafe { self.upwards_knee_starts.get_unchecked(bin_idx) };
let upwards_knee_end = unsafe { self.upwards_knee_ends.get_unchecked(bin_idx) }; let upwards_knee_end = unsafe { self.upwards_knee_ends.get_unchecked(bin_idx) };
if *upwards_ratio_recip != 1.0 && *envelope > 1e-6 { if bin_idx >= first_non_dc_bin && *upwards_ratio_recip != 1.0 && *envelope > 1e-6 {
scale *= compress_upwards( scale *= compress_upwards(
*envelope, *envelope,
*upwards_threshold, *upwards_threshold,
@ -779,7 +764,7 @@ impl CompressorBank {
buffer: &mut [Complex32], buffer: &mut [Complex32],
channel_idx: usize, channel_idx: usize,
params: &SpectralCompressorParams, params: &SpectralCompressorParams,
skip_bins_below: usize, first_non_dc_bin: usize,
) { ) {
// See `compress` for more details // See `compress` for more details
let downwards_knee_scaling_factor = let downwards_knee_scaling_factor =
@ -805,7 +790,6 @@ impl CompressorBank {
.iter_mut() .iter_mut()
.zip(self.envelopes[channel_idx].iter()) .zip(self.envelopes[channel_idx].iter())
.enumerate() .enumerate()
.skip(skip_bins_below)
{ {
// The idea here is that we scale the compressor thresholds/knee values by the sidechain // The idea here is that we scale the compressor thresholds/knee values by the sidechain
// signal, thus sort of creating a dynamic multiband compressor // signal, thus sort of creating a dynamic multiband compressor
@ -855,7 +839,7 @@ impl CompressorBank {
unsafe { self.upwards_knee_starts.get_unchecked(bin_idx) * sidechain_scale }; unsafe { self.upwards_knee_starts.get_unchecked(bin_idx) * sidechain_scale };
let upwards_knee_end = let upwards_knee_end =
unsafe { self.upwards_knee_ends.get_unchecked(bin_idx) * sidechain_scale }; unsafe { self.upwards_knee_ends.get_unchecked(bin_idx) * sidechain_scale };
if *upwards_ratio_recip != 1.0 && *envelope > 1e-6 { if bin_idx >= first_non_dc_bin && *upwards_ratio_recip != 1.0 && *envelope > 1e-6 {
scale *= compress_upwards( scale *= compress_upwards(
*envelope, *envelope,
upwards_threshold, upwards_threshold,