Move attack and release to global parameters
This commit is contained in:
parent
3c1a1c8f14
commit
fa392e61f1
|
@ -14,15 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use nih_plug::prelude::*;
|
||||||
|
use realfft::num_complex::Complex32;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use nih_plug::prelude::*;
|
use crate::SpectralCompressorParams;
|
||||||
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.
|
|
||||||
pub type CompressorParams<'a> = (&'a ThresholdParams, &'a CompressorBankParams);
|
|
||||||
|
|
||||||
/// A bank of compressors so each FFT bin can be compressed individually. The vectors in this struct
|
/// A bank of compressors so each FFT bin can be compressed individually. The vectors in this struct
|
||||||
/// will have a capacity of `MAX_WINDOW_SIZE / 2 + 1` and a size that matches the current complex
|
/// will have a capacity of `MAX_WINDOW_SIZE / 2 + 1` and a size that matches the current complex
|
||||||
|
@ -124,15 +121,6 @@ pub struct CompressorBankParams {
|
||||||
/// separate controls for both
|
/// separate controls for both
|
||||||
#[id = "ratio_hi_freq_rolloff"]
|
#[id = "ratio_hi_freq_rolloff"]
|
||||||
high_freq_ratio_rolloff: FloatParam,
|
high_freq_ratio_rolloff: FloatParam,
|
||||||
|
|
||||||
/// The compressor's attack time in milliseconds. Controls both upwards and downwards
|
|
||||||
/// compression.
|
|
||||||
#[id = "attack"]
|
|
||||||
compressor_attack_ms: FloatParam,
|
|
||||||
/// The compressor's release time in milliseconds. Controls both upwards and downwards
|
|
||||||
/// compression.
|
|
||||||
#[id = "release"]
|
|
||||||
compressor_release_ms: FloatParam,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThresholdParams {
|
impl ThresholdParams {
|
||||||
|
@ -311,29 +299,6 @@ impl CompressorBankParams {
|
||||||
.with_unit("%")
|
.with_unit("%")
|
||||||
.with_value_to_string(formatters::v2s_f32_percentage(0))
|
.with_value_to_string(formatters::v2s_f32_percentage(0))
|
||||||
.with_string_to_value(formatters::s2v_f32_percentage()),
|
.with_string_to_value(formatters::s2v_f32_percentage()),
|
||||||
|
|
||||||
compressor_attack_ms: FloatParam::new(
|
|
||||||
"Attack",
|
|
||||||
150.0,
|
|
||||||
FloatRange::Skewed {
|
|
||||||
min: 0.0,
|
|
||||||
max: 10_000.0,
|
|
||||||
factor: FloatRange::skew_factor(-2.0),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.with_unit(" ms")
|
|
||||||
.with_step_size(0.1),
|
|
||||||
compressor_release_ms: FloatParam::new(
|
|
||||||
"Release",
|
|
||||||
300.0,
|
|
||||||
FloatRange::Skewed {
|
|
||||||
min: 0.0,
|
|
||||||
max: 10_000.0,
|
|
||||||
factor: FloatRange::skew_factor(-2.0),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.with_unit(" ms")
|
|
||||||
.with_step_size(0.1),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,7 +404,7 @@ impl CompressorBank {
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &mut [Complex32],
|
buffer: &mut [Complex32],
|
||||||
channel_idx: usize,
|
channel_idx: usize,
|
||||||
params: CompressorParams,
|
params: &SpectralCompressorParams,
|
||||||
overlap_times: usize,
|
overlap_times: usize,
|
||||||
skip_bins_below: usize,
|
skip_bins_below: usize,
|
||||||
) {
|
) {
|
||||||
|
@ -455,7 +420,7 @@ impl CompressorBank {
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: &mut [Complex32],
|
buffer: &mut [Complex32],
|
||||||
channel_idx: usize,
|
channel_idx: usize,
|
||||||
(_, compressor): CompressorParams,
|
params: &SpectralCompressorParams,
|
||||||
overlap_times: usize,
|
overlap_times: usize,
|
||||||
skip_bins_below: usize,
|
skip_bins_below: usize,
|
||||||
) {
|
) {
|
||||||
|
@ -467,17 +432,19 @@ 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 compressor.compressor_attack_ms.value == 0.0 {
|
let attack_old_t = if params.global.compressor_attack_ms.value == 0.0 {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
(-1.0 / (compressor.compressor_attack_ms.value / 1000.0 * effective_sample_rate)).exp()
|
(-1.0 / (params.global.compressor_attack_ms.value / 1000.0 * effective_sample_rate))
|
||||||
|
.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 compressor.compressor_release_ms.value == 0.0 {
|
let release_old_t = if params.global.compressor_release_ms.value == 0.0 {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
(-1.0 / (compressor.compressor_release_ms.value / 1000.0 * effective_sample_rate)).exp()
|
(-1.0 / (params.global.compressor_release_ms.value / 1000.0 * effective_sample_rate))
|
||||||
|
.exp()
|
||||||
};
|
};
|
||||||
let release_new_t = 1.0 - release_old_t;
|
let release_new_t = 1.0 - release_old_t;
|
||||||
|
|
||||||
|
@ -503,16 +470,16 @@ impl CompressorBank {
|
||||||
&self,
|
&self,
|
||||||
buffer: &mut [Complex32],
|
buffer: &mut [Complex32],
|
||||||
channel_idx: usize,
|
channel_idx: usize,
|
||||||
(_, compressor): CompressorParams,
|
params: &SpectralCompressorParams,
|
||||||
skip_bins_below: usize,
|
skip_bins_below: 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
|
||||||
// than with lower knee values.
|
// than with lower knee values.
|
||||||
let downwards_knee_scaling_factor =
|
let downwards_knee_scaling_factor =
|
||||||
((compressor.downwards_knee_width_db.value * 2.0) + 2.0).log2() - 1.0;
|
((params.compressors.downwards_knee_width_db.value * 2.0) + 2.0).log2() - 1.0;
|
||||||
let upwards_knee_scaling_factor =
|
let upwards_knee_scaling_factor =
|
||||||
((compressor.upwards_knee_width_db.value * 2.0) + 2.0).log2() - 1.0;
|
((params.compressors.upwards_knee_width_db.value * 2.0) + 2.0).log2() - 1.0;
|
||||||
|
|
||||||
// Is this what they mean by zip and and ship it?
|
// Is this what they mean by zip and and ship it?
|
||||||
let downwards_values = self
|
let downwards_values = self
|
||||||
|
@ -559,17 +526,17 @@ impl CompressorBank {
|
||||||
|
|
||||||
/// Update the compressors if needed. This is called just before processing, and the compressors
|
/// 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.
|
/// are updated in accordance to the atomic flags set on this struct.
|
||||||
fn update_if_needed(&mut self, (threshold, compressor): CompressorParams) {
|
fn update_if_needed(&mut self, params: &SpectralCompressorParams) {
|
||||||
// The threshold curve is a polynomial in log-log (decibels-octaves) space. The reuslt from
|
// The threshold curve is a polynomial in log-log (decibels-octaves) space. The reuslt from
|
||||||
// evaluating this needs to be converted to linear gain for the compressors.
|
// evaluating this needs to be converted to linear gain for the compressors.
|
||||||
let intercept = threshold.threshold_db.value;
|
let intercept = params.threshold.threshold_db.value;
|
||||||
// The cheeky 3 additional dB/octave attenuation is to match pink noise with the default
|
// The cheeky 3 additional dB/octave attenuation is to match pink noise with the default
|
||||||
// settings
|
// settings
|
||||||
let slope = threshold.curve_slope.value - 3.0;
|
let slope = params.threshold.curve_slope.value - 3.0;
|
||||||
let curve = threshold.curve_curve.value;
|
let curve = params.threshold.curve_curve.value;
|
||||||
let log2_center_freq = threshold.center_frequency.value.log2();
|
let log2_center_freq = params.threshold.center_frequency.value.log2();
|
||||||
|
|
||||||
let high_freq_ratio_rolloff = compressor.high_freq_ratio_rolloff.value;
|
let high_freq_ratio_rolloff = params.compressors.high_freq_ratio_rolloff.value;
|
||||||
let log2_nyquist_freq = self
|
let log2_nyquist_freq = self
|
||||||
.log2_freqs
|
.log2_freqs
|
||||||
.last()
|
.last()
|
||||||
|
@ -580,7 +547,7 @@ impl CompressorBank {
|
||||||
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let intercept = intercept + compressor.downwards_threshold_offset_db.value;
|
let intercept = intercept + params.compressors.downwards_threshold_offset_db.value;
|
||||||
for (log2_freq, threshold) in self
|
for (log2_freq, threshold) in self
|
||||||
.log2_freqs
|
.log2_freqs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -599,7 +566,7 @@ impl CompressorBank {
|
||||||
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let intercept = intercept + compressor.upwards_threshold_offset_db.value;
|
let intercept = intercept + params.compressors.upwards_threshold_offset_db.value;
|
||||||
for (log2_freq, threshold) in self
|
for (log2_freq, threshold) in self
|
||||||
.log2_freqs
|
.log2_freqs
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -618,7 +585,7 @@ impl CompressorBank {
|
||||||
{
|
{
|
||||||
// If the high-frequency rolloff is enabled then higher frequency bins will have their
|
// If the high-frequency rolloff is enabled then higher frequency bins will have their
|
||||||
// ratios reduced to reduce harshness. This follows the octave scale.
|
// ratios reduced to reduce harshness. This follows the octave scale.
|
||||||
let target_ratio_recip = compressor.downwards_ratio.value.recip();
|
let target_ratio_recip = params.compressors.downwards_ratio.value.recip();
|
||||||
if high_freq_ratio_rolloff == 0.0 {
|
if high_freq_ratio_rolloff == 0.0 {
|
||||||
self.downwards_ratio_recips.fill(target_ratio_recip);
|
self.downwards_ratio_recips.fill(target_ratio_recip);
|
||||||
} else {
|
} else {
|
||||||
|
@ -641,7 +608,7 @@ impl CompressorBank {
|
||||||
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let target_ratio_recip = compressor.upwards_ratio.value.recip();
|
let target_ratio_recip = params.compressors.upwards_ratio.value.recip();
|
||||||
if high_freq_ratio_rolloff == 0.0 {
|
if high_freq_ratio_rolloff == 0.0 {
|
||||||
self.upwards_ratio_recips.fill(target_ratio_recip);
|
self.upwards_ratio_recips.fill(target_ratio_recip);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -78,15 +78,15 @@ struct Plan {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Params)]
|
#[derive(Params)]
|
||||||
struct SpectralCompressorParams {
|
pub struct SpectralCompressorParams {
|
||||||
/// Global parameters. These could just live in this struct but I wanted a separate generic UI
|
/// Global parameters. These could just live in this struct but I wanted a separate generic UI
|
||||||
/// just for these.
|
/// just for these.
|
||||||
#[nested = "global"]
|
#[nested = "global"]
|
||||||
global: GlobalParams,
|
global: GlobalParams,
|
||||||
|
|
||||||
/// Parameters controlling the compressor thresholds and curves.
|
/// Parameters controlling the compressor thresholds and curves.
|
||||||
#[nested = "threhold"]
|
#[nested = "threshold"]
|
||||||
threhold: compressor_bank::ThresholdParams,
|
threshold: compressor_bank::ThresholdParams,
|
||||||
/// Parameters for the compressor bank.
|
/// Parameters for the compressor bank.
|
||||||
#[nested = "compressors"]
|
#[nested = "compressors"]
|
||||||
compressors: compressor_bank::CompressorBankParams,
|
compressors: compressor_bank::CompressorBankParams,
|
||||||
|
@ -118,6 +118,15 @@ struct GlobalParams {
|
||||||
/// prevent invalid inputs).
|
/// prevent invalid inputs).
|
||||||
#[id = "stft_overlap"]
|
#[id = "stft_overlap"]
|
||||||
overlap_times_order: IntParam,
|
overlap_times_order: IntParam,
|
||||||
|
|
||||||
|
/// The compressor's attack time in milliseconds. Controls both upwards and downwards
|
||||||
|
/// compression.
|
||||||
|
#[id = "attack"]
|
||||||
|
compressor_attack_ms: FloatParam,
|
||||||
|
/// The compressor's release time in milliseconds. Controls both upwards and downwards
|
||||||
|
/// compression.
|
||||||
|
#[id = "release"]
|
||||||
|
compressor_release_ms: FloatParam,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpectralCompressor {
|
impl Default for SpectralCompressor {
|
||||||
|
@ -197,6 +206,29 @@ impl Default for GlobalParams {
|
||||||
)
|
)
|
||||||
.with_value_to_string(formatters::v2s_i32_power_of_two())
|
.with_value_to_string(formatters::v2s_i32_power_of_two())
|
||||||
.with_string_to_value(formatters::s2v_i32_power_of_two()),
|
.with_string_to_value(formatters::s2v_i32_power_of_two()),
|
||||||
|
|
||||||
|
compressor_attack_ms: FloatParam::new(
|
||||||
|
"Attack",
|
||||||
|
150.0,
|
||||||
|
FloatRange::Skewed {
|
||||||
|
min: 0.0,
|
||||||
|
max: 10_000.0,
|
||||||
|
factor: FloatRange::skew_factor(-2.0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_unit(" ms")
|
||||||
|
.with_step_size(0.1),
|
||||||
|
compressor_release_ms: FloatParam::new(
|
||||||
|
"Release",
|
||||||
|
300.0,
|
||||||
|
FloatRange::Skewed {
|
||||||
|
min: 0.0,
|
||||||
|
max: 10_000.0,
|
||||||
|
factor: FloatRange::skew_factor(-2.0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.with_unit(" ms")
|
||||||
|
.with_step_size(0.1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +242,7 @@ impl SpectralCompressorParams {
|
||||||
// will require updating the compressor bank.
|
// will require updating the compressor bank.
|
||||||
global: GlobalParams::default(),
|
global: GlobalParams::default(),
|
||||||
|
|
||||||
threhold: compressor_bank::ThresholdParams::new(compressor_bank),
|
threshold: compressor_bank::ThresholdParams::new(compressor_bank),
|
||||||
compressors: compressor_bank::CompressorBankParams::new(compressor_bank),
|
compressors: compressor_bank::CompressorBankParams::new(compressor_bank),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,7 +390,7 @@ impl Plugin for SpectralCompressor {
|
||||||
self.compressor_bank.process(
|
self.compressor_bank.process(
|
||||||
&mut self.complex_fft_buffer,
|
&mut self.complex_fft_buffer,
|
||||||
channel_idx,
|
channel_idx,
|
||||||
(&self.params.threhold, &self.params.compressors),
|
&self.params,
|
||||||
overlap_times,
|
overlap_times,
|
||||||
highest_dcish_bin_idx,
|
highest_dcish_bin_idx,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue