Move curve calculation to a dedicated struct
So we can reuse this in the analyzer.
This commit is contained in:
parent
2de1fd563b
commit
ea4dd8ead2
|
@ -20,6 +20,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::analyzer::AnalyzerData;
|
use crate::analyzer::AnalyzerData;
|
||||||
|
use crate::curve::{Curve, CurveParams};
|
||||||
use crate::SpectralCompressorParams;
|
use crate::SpectralCompressorParams;
|
||||||
|
|
||||||
// These are the parameter name prefixes used for the downwards and upwards compression parameters.
|
// These are the parameter name prefixes used for the downwards and upwards compression parameters.
|
||||||
|
@ -983,32 +984,34 @@ impl CompressorBank {
|
||||||
/// 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, params: &SpectralCompressorParams) {
|
fn update_if_needed(&mut self, params: &SpectralCompressorParams) {
|
||||||
// The threshold curve is a polynomial in log-log (decibels-octaves) space
|
// The threshold curve is a polynomial in log-log (decibels-octaves) space
|
||||||
let intercept = params.threshold.threshold_db.value();
|
let curve_params = CurveParams {
|
||||||
// The cheeky 3 additional dB/octave attenuation is to match pink noise with the default
|
intercept: params.threshold.threshold_db.value(),
|
||||||
// settings. When using sidechaining we explicitly don't want this because the curve should
|
center_frequency: params.threshold.center_frequency.value(),
|
||||||
// be a flat offset to the sidechain input at the default settings.
|
// The cheeky 3 additional dB/octave attenuation is to match pink noise with the
|
||||||
let slope = match params.threshold.mode.value() {
|
// default settings. When using sidechaining we explicitly don't want this because
|
||||||
ThresholdMode::Internal => params.threshold.curve_slope.value() - 3.0,
|
// the curve should be a flat offset to the sidechain input at the default settings.
|
||||||
ThresholdMode::SidechainMatch | ThresholdMode::SidechainCompress => {
|
slope: match params.threshold.mode.value() {
|
||||||
params.threshold.curve_slope.value()
|
ThresholdMode::Internal => params.threshold.curve_slope.value() - 3.0,
|
||||||
}
|
ThresholdMode::SidechainMatch | ThresholdMode::SidechainCompress => {
|
||||||
|
params.threshold.curve_slope.value()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
curve: params.threshold.curve_curve.value(),
|
||||||
};
|
};
|
||||||
let curve = params.threshold.curve_curve.value();
|
let curve = Curve::new(&curve_params);
|
||||||
let log2_center_freq = params.threshold.center_frequency.value().log2();
|
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.should_update_downwards_thresholds
|
.should_update_downwards_thresholds
|
||||||
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let intercept = intercept + params.compressors.downwards.threshold_offset_db.value();
|
let downwards_intercept = params.compressors.downwards.threshold_offset_db.value();
|
||||||
for (log2_freq, threshold_db) in self
|
for (log2_freq, threshold_db) in self
|
||||||
.log2_freqs
|
.log2_freqs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.downwards_thresholds_db.iter_mut())
|
.zip(self.downwards_thresholds_db.iter_mut())
|
||||||
{
|
{
|
||||||
let offset = log2_freq - log2_center_freq;
|
*threshold_db = curve.evaluate_log2(*log2_freq) + downwards_intercept;
|
||||||
*threshold_db = intercept + (slope * offset) + (curve * offset * offset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,14 +1020,13 @@ 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 + params.compressors.upwards.threshold_offset_db.value();
|
let upwards_intercept = params.compressors.upwards.threshold_offset_db.value();
|
||||||
for (log2_freq, threshold_db) in self
|
for (log2_freq, threshold_db) in self
|
||||||
.log2_freqs
|
.log2_freqs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.upwards_thresholds_db.iter_mut())
|
.zip(self.upwards_thresholds_db.iter_mut())
|
||||||
{
|
{
|
||||||
let offset = log2_freq - log2_center_freq;
|
*threshold_db = curve.evaluate_log2(*log2_freq) + upwards_intercept;
|
||||||
*threshold_db = intercept + (slope * offset) + (curve * offset * offset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
61
plugins/spectral_compressor/src/curve.rs
Normal file
61
plugins/spectral_compressor/src/curve.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
//! Abstractions for the parameterized threshold curve.
|
||||||
|
//!
|
||||||
|
//! This was previously computed directly inside of the `CompressorBank` but this makes it easier to
|
||||||
|
//! reuse it when drawing the GUI.
|
||||||
|
|
||||||
|
/// Parameters for a curve, similar to the fields found in `ThresholdParams` but using plain floats
|
||||||
|
/// instead of parameters.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CurveParams {
|
||||||
|
/// The compressor threshold at the center frequency. When sidechaining is enabled, the input
|
||||||
|
/// signal is gained by the inverse of this value. This replaces the input gain in the original
|
||||||
|
/// Spectral Compressor. In the polynomial below, this is the intercept.
|
||||||
|
pub intercept: f32,
|
||||||
|
/// The center frqeuency for the target curve when sidechaining is not enabled. The curve is a
|
||||||
|
/// polynomial `threshold_db + curve_slope*x + curve_curve*(x^2)` that evaluates to a decibel
|
||||||
|
/// value, where `x = log2(center_frequency) - log2(bin_frequency)`. In other words, this is
|
||||||
|
/// evaluated in the log/log domain for decibels and octaves.
|
||||||
|
pub center_frequency: f32,
|
||||||
|
/// The slope for the curve, in the log/log domain. See the polynomial above.
|
||||||
|
pub slope: f32,
|
||||||
|
/// The, uh, 'curve' for the curve, in the logarithmic domain. This is the third coefficient in
|
||||||
|
/// the quadratic polynomial and controls the parabolic behavior. Positive values turn the curve
|
||||||
|
/// into a v-shaped curve, while negative values attenuate everything outside of the center
|
||||||
|
/// frequency. See the polynomial above.
|
||||||
|
pub curve: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the quadratic threshold curve. This used to be calculated directly inside of the
|
||||||
|
/// compressor bank since it's so simple, but the editor also needs to compute this so it makes
|
||||||
|
/// sense to deduplicate it a bit.
|
||||||
|
///
|
||||||
|
/// The curve is evaluated in log-log space (so with octaves being the independent variable and gain
|
||||||
|
/// in decibels being the output of the equation).
|
||||||
|
pub struct Curve<'a> {
|
||||||
|
params: &'a CurveParams,
|
||||||
|
/// The 2-logarithm of [`CurveParams::cemter_frequency`].
|
||||||
|
log2_center_frequency: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Curve<'a> {
|
||||||
|
pub fn new(params: &'a CurveParams) -> Self {
|
||||||
|
Self {
|
||||||
|
params,
|
||||||
|
log2_center_frequency: params.center_frequency.log2(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate the curve for the 2-logarithm of the frequency value. This can be used as an
|
||||||
|
/// optimization to avoid computing these logarithms all the time.
|
||||||
|
#[inline]
|
||||||
|
pub fn evaluate_log2(&self, log2_freq: f32) -> f32 {
|
||||||
|
let offset = log2_freq - self.log2_center_frequency;
|
||||||
|
self.params.intercept + (self.params.slope * offset) + (self.params.curve * offset * offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate the curve for a value in Hertz.
|
||||||
|
#[inline]
|
||||||
|
pub fn evaluate_plain(&self, freq: f32) -> f32 {
|
||||||
|
self.evaluate_log2(freq.log2())
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ use triple_buffer::TripleBuffer;
|
||||||
|
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
mod compressor_bank;
|
mod compressor_bank;
|
||||||
|
mod curve;
|
||||||
mod dry_wet_mixer;
|
mod dry_wet_mixer;
|
||||||
mod editor;
|
mod editor;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue