1
0
Fork 0

Somewhat mitigate phasing in crossovers

This commit is contained in:
Robbert van der Helm 2022-05-29 16:57:39 +02:00
parent ebe2b24146
commit c9332d332b
2 changed files with 38 additions and 12 deletions

View file

@ -100,19 +100,31 @@ impl IirCrossover {
} }
/// Update the crossover frequencies for all filters. If the frequencies are not monotonic then /// Update the crossover frequencies for all filters. If the frequencies are not monotonic then
/// this function will ensure that they are. /// this function will ensure that they are. The active number of bands is used to make sure
pub fn update(&mut self, sample_rate: f32, mut frequencies: [f32; NUM_BANDS - 1]) { /// unused bands are not part of the normalization.
// Make sure the frequencies are monotonic pub fn update(
for frequency_idx in 1..NUM_BANDS - 1 { &mut self,
if frequencies[frequency_idx] < frequencies[frequency_idx - 1] { sample_rate: f32,
frequencies[frequency_idx] = frequencies[frequency_idx - 1]; num_bands: usize,
mut frequencies: [f32; NUM_BANDS - 1],
) {
// Make sure the frequencies are monotonic by pushing bands down when they are too close to
// the next band
for frequency_idx in (1..num_bands - 1).rev() {
if frequencies[frequency_idx - 1] > frequencies[frequency_idx] / 2.0 {
frequencies[frequency_idx - 1] = frequencies[frequency_idx] / 2.0;
} }
} }
match self.mode { match self.mode {
IirCrossoverType::LinkwitzRiley24 => { IirCrossoverType::LinkwitzRiley24 => {
const Q: f32 = std::f32::consts::FRAC_1_SQRT_2; const Q: f32 = std::f32::consts::FRAC_1_SQRT_2;
for (crossover, frequency) in self.crossovers.iter_mut().zip(frequencies) { for (crossover, frequency) in self
.crossovers
.iter_mut()
.zip(frequencies)
.take(num_bands - 1)
{
let lp_coefs = BiquadCoefficients::lowpass(sample_rate, frequency, Q); let lp_coefs = BiquadCoefficients::lowpass(sample_rate, frequency, Q);
let hp_coefs = BiquadCoefficients::highpass(sample_rate, frequency, Q); let hp_coefs = BiquadCoefficients::highpass(sample_rate, frequency, Q);
crossover.update_coefficients(lp_coefs, hp_coefs); crossover.update_coefficients(lp_coefs, hp_coefs);

View file

@ -21,6 +21,7 @@ compile_error!("Compiling without SIMD support is currently not supported");
use crossover::iir::{IirCrossover, IirCrossoverType}; use crossover::iir::{IirCrossover, IirCrossoverType};
use nih_plug::prelude::*; use nih_plug::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
mod crossover; mod crossover;
@ -39,6 +40,8 @@ struct Crossover {
/// Provides the LR24 crossover. /// Provides the LR24 crossover.
iir_crossover: IirCrossover, iir_crossover: IirCrossover,
/// Set when the number of bands has changed and the filters must be updated.
should_update_filters: Arc<AtomicBool>,
} }
// TODO: Add multiple crossover types. Haven't added the control for that yet because the current // TODO: Add multiple crossover types. Haven't added the control for that yet because the current
@ -63,8 +66,8 @@ struct CrossoverParams {
pub crossover_4_freq: FloatParam, pub crossover_4_freq: FloatParam,
} }
impl Default for CrossoverParams { impl CrossoverParams {
fn default() -> Self { fn new(should_update_filters: Arc<AtomicBool>) -> Self {
let crossover_range = FloatRange::Skewed { let crossover_range = FloatRange::Skewed {
min: MIN_CROSSOVER_FREQUENCY, min: MIN_CROSSOVER_FREQUENCY,
max: MAX_CROSSOVER_FREQUENCY, max: MAX_CROSSOVER_FREQUENCY,
@ -82,7 +85,10 @@ impl Default for CrossoverParams {
min: 2, min: 2,
max: NUM_BANDS as i32, max: NUM_BANDS as i32,
}, },
), )
.with_callback(Arc::new(move |_| {
should_update_filters.store(true, Ordering::Relaxed)
})),
// TODO: More sensible default frequencies // TODO: More sensible default frequencies
crossover_1_freq: FloatParam::new("Crossover 1", 200.0, crossover_range) crossover_1_freq: FloatParam::new("Crossover 1", 200.0, crossover_range)
.with_smoother(crossover_smoothing_style) .with_smoother(crossover_smoothing_style)
@ -106,8 +112,10 @@ impl Default for CrossoverParams {
impl Default for Crossover { impl Default for Crossover {
fn default() -> Self { fn default() -> Self {
let should_update_filters = Arc::new(AtomicBool::new(false));
Crossover { Crossover {
params: Arc::new(CrossoverParams::default()), params: Arc::new(CrossoverParams::new(should_update_filters.clone())),
buffer_config: BufferConfig { buffer_config: BufferConfig {
sample_rate: 1.0, sample_rate: 1.0,
@ -117,6 +125,7 @@ impl Default for Crossover {
}, },
iir_crossover: IirCrossover::new(IirCrossoverType::LinkwitzRiley24), iir_crossover: IirCrossover::new(IirCrossoverType::LinkwitzRiley24),
should_update_filters,
} }
} }
} }
@ -238,7 +247,11 @@ impl Plugin for Crossover {
impl Crossover { impl Crossover {
/// Update the filter coefficients for the crossovers, but only if it's needed. /// Update the filter coefficients for the crossovers, but only if it's needed.
fn maybe_update_filters(&mut self) { fn maybe_update_filters(&mut self) {
if self.params.crossover_1_freq.smoothed.is_smoothing() if self
.should_update_filters
.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
|| self.params.crossover_1_freq.smoothed.is_smoothing()
|| self.params.crossover_2_freq.smoothed.is_smoothing() || self.params.crossover_2_freq.smoothed.is_smoothing()
|| self.params.crossover_3_freq.smoothed.is_smoothing() || self.params.crossover_3_freq.smoothed.is_smoothing()
|| self.params.crossover_4_freq.smoothed.is_smoothing() || self.params.crossover_4_freq.smoothed.is_smoothing()
@ -253,6 +266,7 @@ impl Crossover {
// crossover 2 being lower than crossover 1 // crossover 2 being lower than crossover 1
self.iir_crossover.update( self.iir_crossover.update(
self.buffer_config.sample_rate, self.buffer_config.sample_rate,
self.params.num_bands.value as usize,
[ [
self.params.crossover_1_freq.smoothed.next(), self.params.crossover_1_freq.smoothed.next(),
self.params.crossover_2_freq.smoothed.next(), self.params.crossover_2_freq.smoothed.next(),