1
0
Fork 0

Add triple buffers to share analyzer data in SC

This commit is contained in:
Robbert van der Helm 2023-03-19 14:42:04 +01:00
parent 510bc3f131
commit 9647f8f3dd
6 changed files with 46 additions and 5 deletions

1
Cargo.lock generated
View file

@ -3762,6 +3762,7 @@ dependencies = [
"open", "open",
"realfft", "realfft",
"serde", "serde",
"triple_buffer",
] ]
[[package]] [[package]]

View file

@ -30,8 +30,8 @@ const SPECTRUM_WINDOW_OVERLAP: usize = 2;
/// peak meter. /// peak meter.
const SMOOTHING_DECAY_MS: f32 = 100.0; 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 /// The amplitudes of all frequency bins in a windowed FFT of Diopser's output. Also includes the DC
/// bin which we don't draw, just to make this a bit less confusing. /// offset bin which we don't draw, just to make this a bit less confusing.
pub type Spectrum = [f32; SPECTRUM_WINDOW_SIZE / 2 + 1]; pub type Spectrum = [f32; SPECTRUM_WINDOW_SIZE / 2 + 1];
/// A receiver for a spectrum computed by [`SpectrumInput`]. /// A receiver for a spectrum computed by [`SpectrumInput`].
pub type SpectrumOutput = triple_buffer::Output<Spectrum>; pub type SpectrumOutput = triple_buffer::Output<Spectrum>;

View file

@ -19,3 +19,4 @@ realfft = "3.0"
crossbeam = "0.8" crossbeam = "0.8"
open = "3.0" open = "3.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
triple_buffer = "6.2"

View file

@ -7,9 +7,13 @@
/// This pulls the data directly from the spectral compression part of Spectral Compressor, so the /// 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 /// 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. /// current window size is 2048, then only the first `2048 / 2 + 1` elements in the arrays are used.
#[derive(Debug)]
pub struct AnalyzerData { pub struct AnalyzerData {
/// The amplitudes of all frequency bins in a windowed FFT of Spectral Compressor's output. Also /// 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. /// 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], pub spectrum: [f32; crate::MAX_WINDOW_SIZE / 2 + 1],
/// The gain reduction applied to each band, in decibels. Positive values mean that a band /// 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) /// 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 // 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. // 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],
}
}
}

View file

@ -19,6 +19,7 @@ use realfft::num_complex::Complex32;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use crate::analyzer::AnalyzerData;
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.
@ -97,6 +98,11 @@ pub struct CompressorBank {
/// The sample rate this compressor bank was configured for. This is used to compute the /// The sample rate this compressor bank was configured for. This is used to compute the
/// coefficients for the envelope followers in the process function. /// coefficients for the envelope followers in the process function.
sample_rate: f32, 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<AnalyzerData>,
} }
#[derive(Params)] #[derive(Params)]
@ -393,7 +399,11 @@ impl CompressorParams {
impl CompressorBank { impl CompressorBank {
/// Set up the compressor for the given channel count and maximum FFT window size. The /// Set up the compressor for the given channel count and maximum FFT window size. The
/// compressors won't be initialized yet. /// 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<AnalyzerData>,
num_channels: usize,
max_window_size: usize,
) -> Self {
let complex_buffer_len = max_window_size / 2 + 1; let complex_buffer_len = max_window_size / 2 + 1;
CompressorBank { CompressorBank {
@ -423,6 +433,8 @@ impl CompressorBank {
], ],
window_size: 0, window_size: 0,
sample_rate: 1.0, sample_rate: 1.0,
analyzer_input_data,
} }
} }

View file

@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use analyzer::AnalyzerData;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use editor::EditorMode; use editor::EditorMode;
use nih_plug::prelude::*; use nih_plug::prelude::*;
use nih_plug_vizia::ViziaState; use nih_plug_vizia::ViziaState;
use realfft::num_complex::Complex32; use realfft::num_complex::Complex32;
use realfft::{ComplexToReal, RealFftPlanner, RealToComplex}; use realfft::{ComplexToReal, RealFftPlanner, RealToComplex};
use std::sync::Arc; use std::sync::{Arc, Mutex};
use triple_buffer::TripleBuffer;
mod analyzer; mod analyzer;
mod compressor_bank; mod compressor_bank;
@ -69,6 +71,10 @@ pub struct SpectralCompressor {
plan_for_order: Option<[Plan; MAX_WINDOW_ORDER - MIN_WINDOW_ORDER + 1]>, plan_for_order: Option<[Plan; MAX_WINDOW_ORDER - MIN_WINDOW_ORDER + 1]>,
/// The output of our real->complex FFT. /// The output of our real->complex FFT.
complex_fft_buffer: Vec<Complex32>, complex_fft_buffer: Vec<Complex32>,
/// 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<Mutex<triple_buffer::Output<AnalyzerData>>>,
} }
/// An FFT plan for a specific window size, all of which will be precomputed during initilaization. /// 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 { impl Default for SpectralCompressor {
fn default() -> Self { 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 // 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 // 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 { SpectralCompressor {
params: Arc::new(SpectralCompressorParams::new(&compressor_bank)), params: Arc::new(SpectralCompressorParams::new(&compressor_bank)),
@ -165,6 +177,8 @@ impl Default for SpectralCompressor {
// the plugin is initialized // the plugin is initialized
plan_for_order: None, plan_for_order: None,
complex_fft_buffer: Vec::with_capacity(MAX_WINDOW_SIZE / 2 + 1), complex_fft_buffer: Vec::with_capacity(MAX_WINDOW_SIZE / 2 + 1),
analyzer_output_data: Arc::new(Mutex::new(analyzer_output_data)),
} }
} }
} }