From c9332d332bec0cf5d10fb12c880b96f60472f8b0 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 29 May 2022 16:57:39 +0200 Subject: [PATCH] Somewhat mitigate phasing in crossovers --- plugins/crossover/src/crossover/iir.rs | 26 +++++++++++++++++++------- plugins/crossover/src/lib.rs | 24 +++++++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/plugins/crossover/src/crossover/iir.rs b/plugins/crossover/src/crossover/iir.rs index 06e78312..70c5e964 100644 --- a/plugins/crossover/src/crossover/iir.rs +++ b/plugins/crossover/src/crossover/iir.rs @@ -100,19 +100,31 @@ impl IirCrossover { } /// Update the crossover frequencies for all filters. If the frequencies are not monotonic then - /// this function will ensure that they are. - pub fn update(&mut self, sample_rate: f32, mut frequencies: [f32; NUM_BANDS - 1]) { - // Make sure the frequencies are monotonic - for frequency_idx in 1..NUM_BANDS - 1 { - if frequencies[frequency_idx] < frequencies[frequency_idx - 1] { - frequencies[frequency_idx] = frequencies[frequency_idx - 1]; + /// this function will ensure that they are. The active number of bands is used to make sure + /// unused bands are not part of the normalization. + pub fn update( + &mut self, + sample_rate: f32, + 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 { IirCrossoverType::LinkwitzRiley24 => { 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 hp_coefs = BiquadCoefficients::highpass(sample_rate, frequency, Q); crossover.update_coefficients(lp_coefs, hp_coefs); diff --git a/plugins/crossover/src/lib.rs b/plugins/crossover/src/lib.rs index 65d3a2fd..33099639 100644 --- a/plugins/crossover/src/lib.rs +++ b/plugins/crossover/src/lib.rs @@ -21,6 +21,7 @@ compile_error!("Compiling without SIMD support is currently not supported"); use crossover::iir::{IirCrossover, IirCrossoverType}; use nih_plug::prelude::*; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; mod crossover; @@ -39,6 +40,8 @@ struct Crossover { /// Provides the LR24 crossover. iir_crossover: IirCrossover, + /// Set when the number of bands has changed and the filters must be updated. + should_update_filters: Arc, } // 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, } -impl Default for CrossoverParams { - fn default() -> Self { +impl CrossoverParams { + fn new(should_update_filters: Arc) -> Self { let crossover_range = FloatRange::Skewed { min: MIN_CROSSOVER_FREQUENCY, max: MAX_CROSSOVER_FREQUENCY, @@ -82,7 +85,10 @@ impl Default for CrossoverParams { min: 2, max: NUM_BANDS as i32, }, - ), + ) + .with_callback(Arc::new(move |_| { + should_update_filters.store(true, Ordering::Relaxed) + })), // TODO: More sensible default frequencies crossover_1_freq: FloatParam::new("Crossover 1", 200.0, crossover_range) .with_smoother(crossover_smoothing_style) @@ -106,8 +112,10 @@ impl Default for CrossoverParams { impl Default for Crossover { fn default() -> Self { + let should_update_filters = Arc::new(AtomicBool::new(false)); + Crossover { - params: Arc::new(CrossoverParams::default()), + params: Arc::new(CrossoverParams::new(should_update_filters.clone())), buffer_config: BufferConfig { sample_rate: 1.0, @@ -117,6 +125,7 @@ impl Default for Crossover { }, iir_crossover: IirCrossover::new(IirCrossoverType::LinkwitzRiley24), + should_update_filters, } } } @@ -238,7 +247,11 @@ impl Plugin for Crossover { impl Crossover { /// Update the filter coefficients for the crossovers, but only if it's needed. 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_3_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 self.iir_crossover.update( self.buffer_config.sample_rate, + self.params.num_bands.value as usize, [ self.params.crossover_1_freq.smoothed.next(), self.params.crossover_2_freq.smoothed.next(),