Move actual compression routines to functions
This commit is contained in:
parent
2813f3d827
commit
49d5ba147a
|
@ -642,37 +642,14 @@ impl CompressorBank {
|
||||||
(downwards_knee_start, downwards_knee_end),
|
(downwards_knee_start, downwards_knee_end),
|
||||||
) = downwards_values;
|
) = downwards_values;
|
||||||
if *downwards_ratio_recip != 1.0 {
|
if *downwards_ratio_recip != 1.0 {
|
||||||
// The soft-knee option will fade in the compression curve when reaching the knee
|
scale *= compress_downwards(
|
||||||
// start until it mtaches the hard-knee curve at the knee-end
|
*envelope,
|
||||||
if envelope >= downwards_knee_end {
|
*downwards_threshold,
|
||||||
// Because we're working in the linear domain, we care about the ratio between
|
*downwards_ratio_recip,
|
||||||
// the threshold and the envelope's current value. And log-space division
|
*downwards_knee_start,
|
||||||
// becomes linear-space exponentiation by the reciprocal, or taking the nth
|
*downwards_knee_end,
|
||||||
// root.
|
downwards_knee_scaling_factor,
|
||||||
let threshold_ratio = envelope / downwards_threshold;
|
);
|
||||||
scale /= threshold_ratio / threshold_ratio.powf(*downwards_ratio_recip);
|
|
||||||
} else if envelope >= downwards_knee_start {
|
|
||||||
// When the knee width is set to 0 dB, `downwards_knee_start ==
|
|
||||||
// downwards_knee_end` and this branch is never hit
|
|
||||||
let linear_knee_width = downwards_knee_end - downwards_knee_start;
|
|
||||||
let raw_knee_t = (envelope - downwards_knee_start) / linear_knee_width;
|
|
||||||
nih_debug_assert!((0.0..=1.0).contains(&raw_knee_t));
|
|
||||||
|
|
||||||
// TODO: Apart from a small discontinuety in the derivative/slope at the start
|
|
||||||
// of the knee this equation does exactly what you'd expect it to, but it
|
|
||||||
// feels a bit weird. Should probably look for a cleaner way to calculate
|
|
||||||
// this soft knee in linear-space at some point.
|
|
||||||
let knee_t = (1.0 - raw_knee_t).powf(downwards_knee_scaling_factor);
|
|
||||||
nih_debug_assert!((0.0..=1.0).contains(&knee_t));
|
|
||||||
|
|
||||||
// We'll linearly interpolate between compression at the knee start and at the
|
|
||||||
// actual threshold based on `knee_t`
|
|
||||||
let knee_ratio = envelope / downwards_knee_start;
|
|
||||||
let threshold_ratio = envelope / downwards_threshold;
|
|
||||||
scale /= (knee_t * (knee_ratio / knee_ratio.powf(*downwards_ratio_recip)))
|
|
||||||
+ ((1.0 - knee_t)
|
|
||||||
* (threshold_ratio / threshold_ratio.powf(*downwards_ratio_recip)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upwards compression should not happen when the signal is _too_ quiet as we'd only be
|
// Upwards compression should not happen when the signal is _too_ quiet as we'd only be
|
||||||
|
@ -680,29 +657,14 @@ impl CompressorBank {
|
||||||
let ((upwards_threshold, upwards_ratio_recip), (upwards_knee_start, upwards_knee_end)) =
|
let ((upwards_threshold, upwards_ratio_recip), (upwards_knee_start, upwards_knee_end)) =
|
||||||
upwards_values;
|
upwards_values;
|
||||||
if *upwards_ratio_recip != 1.0 && *envelope > 1e-6 {
|
if *upwards_ratio_recip != 1.0 && *envelope > 1e-6 {
|
||||||
// This goes the other way around compared to the downwards compression
|
scale *= compress_upwards(
|
||||||
if envelope <= upwards_knee_start {
|
*envelope,
|
||||||
// Notice how these ratios are reversed here
|
*upwards_threshold,
|
||||||
let threshold_ratio = upwards_threshold / envelope;
|
*upwards_ratio_recip,
|
||||||
scale /= threshold_ratio.powf(*upwards_ratio_recip) / threshold_ratio;
|
*upwards_knee_start,
|
||||||
} else if envelope <= upwards_knee_end {
|
*upwards_knee_end,
|
||||||
// When the knee width is set to 0 dB, `upwards_knee_start == upwards_knee_end`
|
upwards_knee_scaling_factor,
|
||||||
// and this branch is never hit
|
);
|
||||||
let linear_knee_width = upwards_knee_end - upwards_knee_start;
|
|
||||||
let raw_knee_t = (envelope - upwards_knee_start) / linear_knee_width;
|
|
||||||
nih_debug_assert!((0.0..=1.0).contains(&raw_knee_t));
|
|
||||||
|
|
||||||
// TODO: Some note the downwards version
|
|
||||||
let knee_t = (1.0 - raw_knee_t).powf(upwards_knee_scaling_factor);
|
|
||||||
nih_debug_assert!((0.0..=1.0).contains(&knee_t));
|
|
||||||
|
|
||||||
// The ratios are again inverted here compared to the downwards version
|
|
||||||
let knee_ratio = upwards_knee_start / envelope;
|
|
||||||
let threshold_ratio = upwards_threshold / envelope;
|
|
||||||
scale /= (knee_t * (knee_ratio.powf(*upwards_ratio_recip) / knee_ratio))
|
|
||||||
+ ((1.0 - knee_t)
|
|
||||||
* (threshold_ratio.powf(*upwards_ratio_recip) / threshold_ratio));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*bin *= scale;
|
*bin *= scale;
|
||||||
|
@ -841,3 +803,85 @@ impl CompressorBank {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the compression scaling factor for downwards compression with the supplied parameters. The
|
||||||
|
/// input signal can be multiplied by this factor to get the compressed output signal. All
|
||||||
|
/// parameters are linear gain values.
|
||||||
|
fn compress_downwards(
|
||||||
|
envelope: f32,
|
||||||
|
threshold: f32,
|
||||||
|
ratio_recip: f32,
|
||||||
|
knee_start: f32,
|
||||||
|
knee_end: f32,
|
||||||
|
knee_scaling_factor: f32,
|
||||||
|
) -> f32 {
|
||||||
|
// The soft-knee option will fade in the compression curve when reaching the knee
|
||||||
|
// start until it mtaches the hard-knee curve at the knee-end
|
||||||
|
if envelope >= knee_end {
|
||||||
|
// Because we're working in the linear domain, we care about the ratio between
|
||||||
|
// the threshold and the envelope's current value. And log-space division
|
||||||
|
// becomes linear-space exponentiation by the reciprocal, or taking the nth
|
||||||
|
// root.
|
||||||
|
let threshold_ratio = envelope / threshold;
|
||||||
|
threshold_ratio.powf(ratio_recip) / threshold_ratio
|
||||||
|
} else if envelope >= knee_start {
|
||||||
|
// When the knee width is set to 0 dB, `downwards_knee_start ==
|
||||||
|
// downwards_knee_end` and this branch is never hit
|
||||||
|
let linear_knee_width = knee_end - knee_start;
|
||||||
|
let raw_knee_t = (envelope - knee_start) / linear_knee_width;
|
||||||
|
nih_debug_assert!((0.0..=1.0).contains(&raw_knee_t));
|
||||||
|
|
||||||
|
// TODO: Apart from a small discontinuety in the derivative/slope at the start
|
||||||
|
// of the knee this equation does exactly what you'd expect it to, but it
|
||||||
|
// feels a bit weird. Should probably look for a cleaner way to calculate
|
||||||
|
// this soft knee in linear-space at some point.
|
||||||
|
let knee_t = (1.0 - raw_knee_t).powf(knee_scaling_factor);
|
||||||
|
nih_debug_assert!((0.0..=1.0).contains(&knee_t));
|
||||||
|
|
||||||
|
// We'll linearly interpolate between compression at the knee start and at the
|
||||||
|
// actual threshold based on `knee_t`
|
||||||
|
let knee_ratio = envelope / knee_start;
|
||||||
|
let threshold_ratio = envelope / threshold;
|
||||||
|
(knee_t * (knee_ratio.powf(ratio_recip) / knee_ratio))
|
||||||
|
+ ((1.0 - knee_t) * (threshold_ratio.powf(ratio_recip) / threshold_ratio))
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the compression scaling factor for upwards compression with the supplied parameters. The
|
||||||
|
/// input signal can be multiplied by this factor to get the compressed output signal. All
|
||||||
|
/// parameters are linear gain values.
|
||||||
|
fn compress_upwards(
|
||||||
|
envelope: f32,
|
||||||
|
threshold: f32,
|
||||||
|
ratio_recip: f32,
|
||||||
|
knee_start: f32,
|
||||||
|
knee_end: f32,
|
||||||
|
knee_scaling_factor: f32,
|
||||||
|
) -> f32 {
|
||||||
|
// This goes the other way around compared to the downwards compression
|
||||||
|
if envelope <= knee_start {
|
||||||
|
// Notice how these ratios are reversed here
|
||||||
|
let threshold_ratio = threshold / envelope;
|
||||||
|
threshold_ratio / threshold_ratio.powf(ratio_recip)
|
||||||
|
} else if envelope <= knee_end {
|
||||||
|
// When the knee width is set to 0 dB, `upwards_knee_start == upwards_knee_end`
|
||||||
|
// and this branch is never hit
|
||||||
|
let linear_knee_width = knee_end - knee_start;
|
||||||
|
let raw_knee_t = (envelope - knee_start) / linear_knee_width;
|
||||||
|
nih_debug_assert!((0.0..=1.0).contains(&raw_knee_t));
|
||||||
|
|
||||||
|
// TODO: Some note the downwards version
|
||||||
|
let knee_t = (1.0 - raw_knee_t).powf(knee_scaling_factor);
|
||||||
|
nih_debug_assert!((0.0..=1.0).contains(&knee_t));
|
||||||
|
|
||||||
|
// The ratios are again inverted here compared to the downwards version
|
||||||
|
let knee_ratio = knee_start / envelope;
|
||||||
|
let threshold_ratio = threshold / envelope;
|
||||||
|
(knee_t * (knee_ratio / knee_ratio.powf(ratio_recip)))
|
||||||
|
+ ((1.0 - knee_t) * (threshold_ratio / threshold_ratio.powf(ratio_recip)))
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue