From 9647f8f3dd4a3a9ce6c73b64c42e7a0660d750a1 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 19 Mar 2023 14:42:04 +0100 Subject: [PATCH] Add triple buffers to share analyzer data in SC --- Cargo.lock | 1 + plugins/diopser/src/spectrum.rs | 4 ++-- plugins/spectral_compressor/Cargo.toml | 1 + plugins/spectral_compressor/src/analyzer.rs | 13 +++++++++++++ .../spectral_compressor/src/compressor_bank.rs | 14 +++++++++++++- plugins/spectral_compressor/src/lib.rs | 18 ++++++++++++++++-- 6 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc3e39d0..5f50ab8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3762,6 +3762,7 @@ dependencies = [ "open", "realfft", "serde", + "triple_buffer", ] [[package]] diff --git a/plugins/diopser/src/spectrum.rs b/plugins/diopser/src/spectrum.rs index 361511ac..b54b828b 100644 --- a/plugins/diopser/src/spectrum.rs +++ b/plugins/diopser/src/spectrum.rs @@ -30,8 +30,8 @@ const SPECTRUM_WINDOW_OVERLAP: usize = 2; /// peak meter. const SMOOTHING_DECAY_MS: f32 = 100.0; -/// The amplitudes of all frequency bins in a windowed FFT of the input. Also includes the DC offset -/// bin which we don't draw, just to make this a bit less confusing. +/// The amplitudes of all frequency bins in a windowed FFT of Diopser's output. Also includes the DC +/// offset bin which we don't draw, just to make this a bit less confusing. pub type Spectrum = [f32; SPECTRUM_WINDOW_SIZE / 2 + 1]; /// A receiver for a spectrum computed by [`SpectrumInput`]. pub type SpectrumOutput = triple_buffer::Output; diff --git a/plugins/spectral_compressor/Cargo.toml b/plugins/spectral_compressor/Cargo.toml index b2715678..13d57060 100644 --- a/plugins/spectral_compressor/Cargo.toml +++ b/plugins/spectral_compressor/Cargo.toml @@ -19,3 +19,4 @@ realfft = "3.0" crossbeam = "0.8" open = "3.0" serde = { version = "1.0", features = ["derive"] } +triple_buffer = "6.2" diff --git a/plugins/spectral_compressor/src/analyzer.rs b/plugins/spectral_compressor/src/analyzer.rs index 24b14f8a..ca413aba 100644 --- a/plugins/spectral_compressor/src/analyzer.rs +++ b/plugins/spectral_compressor/src/analyzer.rs @@ -7,9 +7,13 @@ /// This pulls the data directly from the spectral compression part of Spectral Compressor, so the /// window size and overlap amounts are equal to the ones used by SC's main algorithm. If the /// current window size is 2048, then only the first `2048 / 2 + 1` elements in the arrays are used. +#[derive(Debug)] pub struct AnalyzerData { /// The amplitudes of all frequency bins in a windowed FFT of Spectral Compressor's output. Also /// includes the DC offset bin which we don't draw, just to make this a bit less confusing. + /// + /// This data is taken directly from the envelope followers, so it has the same rise and fall + /// time as what is used by the compressors. pub spectrum: [f32; crate::MAX_WINDOW_SIZE / 2 + 1], /// The gain reduction applied to each band, in decibels. Positive values mean that a band /// becomes louder, and negative values mean a band got attenuated. Does not (and should not) @@ -19,3 +23,12 @@ pub struct AnalyzerData { // threshold curve or to also show the individual upwards/downwards thresholds. Or omit // this and implement it in a nicer way for the premium Spectral Compressor. } + +impl Default for AnalyzerData { + fn default() -> Self { + Self { + spectrum: [0.0; crate::MAX_WINDOW_SIZE / 2 + 1], + gain_reduction_db: [0.0; crate::MAX_WINDOW_SIZE / 2 + 1], + } + } +} diff --git a/plugins/spectral_compressor/src/compressor_bank.rs b/plugins/spectral_compressor/src/compressor_bank.rs index e102566e..eb7eea57 100644 --- a/plugins/spectral_compressor/src/compressor_bank.rs +++ b/plugins/spectral_compressor/src/compressor_bank.rs @@ -19,6 +19,7 @@ use realfft::num_complex::Complex32; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use crate::analyzer::AnalyzerData; use crate::SpectralCompressorParams; // These are the parameter name prefixes used for the downwards and upwards compression parameters. @@ -97,6 +98,11 @@ pub struct CompressorBank { /// The sample rate this compressor bank was configured for. This is used to compute the /// coefficients for the envelope followers in the process function. sample_rate: f32, + + /// The input data for the spectrum analyzer. Stores both the spectrum analyzer values and the + /// current gain reduction. Used to draw the spectrum analyzer and gain reduction display in the + /// editor. + analyzer_input_data: triple_buffer::Input, } #[derive(Params)] @@ -393,7 +399,11 @@ impl CompressorParams { impl CompressorBank { /// Set up the compressor for the given channel count and maximum FFT window size. The /// compressors won't be initialized yet. - pub fn new(num_channels: usize, max_window_size: usize) -> Self { + pub fn new( + analyzer_input_data: triple_buffer::Input, + num_channels: usize, + max_window_size: usize, + ) -> Self { let complex_buffer_len = max_window_size / 2 + 1; CompressorBank { @@ -423,6 +433,8 @@ impl CompressorBank { ], window_size: 0, sample_rate: 1.0, + + analyzer_input_data, } } diff --git a/plugins/spectral_compressor/src/lib.rs b/plugins/spectral_compressor/src/lib.rs index 30f55355..e9cf7b86 100644 --- a/plugins/spectral_compressor/src/lib.rs +++ b/plugins/spectral_compressor/src/lib.rs @@ -14,13 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use analyzer::AnalyzerData; use crossbeam::atomic::AtomicCell; use editor::EditorMode; use nih_plug::prelude::*; use nih_plug_vizia::ViziaState; use realfft::num_complex::Complex32; use realfft::{ComplexToReal, RealFftPlanner, RealToComplex}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use triple_buffer::TripleBuffer; mod analyzer; mod compressor_bank; @@ -69,6 +71,10 @@ pub struct SpectralCompressor { plan_for_order: Option<[Plan; MAX_WINDOW_ORDER - MIN_WINDOW_ORDER + 1]>, /// The output of our real->complex FFT. complex_fft_buffer: Vec, + + /// The output for the analyzer data computed in `CompressorBank` while the editor is open. This + /// can be cloned and moved into the editor. + analyzer_output_data: Arc>>, } /// An FFT plan for a specific window size, all of which will be precomputed during initilaization. @@ -141,9 +147,15 @@ pub struct GlobalParams { impl Default for SpectralCompressor { fn default() -> Self { + // The spectrum analyzer and gain reduction data is computed directly in the spectral + // compression routine in `compressor_bank`. `analyzer_output_data` can then be used in the + // editor to draw the data. + let (analyzer_input_data, analyzer_output_data) = TripleBuffer::default().split(); + // Changing any of the compressor threshold or ratio parameters will set an atomic flag in // this object that causes the compressor thresholds and ratios to be recalcualted - let compressor_bank = compressor_bank::CompressorBank::new(2, MAX_WINDOW_SIZE); + let compressor_bank = + compressor_bank::CompressorBank::new(analyzer_input_data, 2, MAX_WINDOW_SIZE); SpectralCompressor { params: Arc::new(SpectralCompressorParams::new(&compressor_bank)), @@ -165,6 +177,8 @@ impl Default for SpectralCompressor { // the plugin is initialized plan_for_order: None, complex_fft_buffer: Vec::with_capacity(MAX_WINDOW_SIZE / 2 + 1), + + analyzer_output_data: Arc::new(Mutex::new(analyzer_output_data)), } } }