diff --git a/plugins/diopser/src/spectrum.rs b/plugins/diopser/src/spectrum.rs
index 083838b1..5ee1e4b1 100644
--- a/plugins/diopser/src/spectrum.rs
+++ b/plugins/diopser/src/spectrum.rs
@@ -15,6 +15,7 @@
// along with this program. If not, see .
use nih_plug::prelude::*;
+use nih_plug::util::window::multiply_with_window;
use realfft::num_complex::Complex32;
use realfft::{RealFftPlanner, RealToComplex};
use std::f32;
@@ -81,9 +82,10 @@ impl SpectrumInput {
pub fn compute(&mut self, buffer: &Buffer) {
self.stft.process_analyze_only(
buffer,
- &self.compensated_window_function,
SPECTRUM_WINDOW_OVERLAP,
|channel_idx, real_fft_scratch_buffer| {
+ multiply_with_window(real_fft_scratch_buffer, &self.compensated_window_function);
+
// Forward FFT, the helper has already applied window function
self.plan
.process_with_scratch(
diff --git a/plugins/examples/stft/src/lib.rs b/plugins/examples/stft/src/lib.rs
index 535ed8ae..ddecb3aa 100644
--- a/plugins/examples/stft/src/lib.rs
+++ b/plugins/examples/stft/src/lib.rs
@@ -12,7 +12,7 @@ struct Stft {
/// An adapter that performs most of the overlap-add algorithm for us.
stft: util::StftHelper,
- /// A Hann window function, passed to the overlap-add helper.
+ /// A Hann window function, applied after the IDFT operation to minimize time domain aliasing.
window_function: Vec,
/// The FFT of a simple low-pass FIR filter.
@@ -126,11 +126,8 @@ impl Plugin for Stft {
// IDFT operation
const GAIN_COMPENSATION: f32 = f32::consts::E / OVERLAP_TIMES as f32 / WINDOW_SIZE as f32;
- self.stft.process_overlap_add(
- buffer,
- &self.window_function,
- OVERLAP_TIMES,
- |_channel_idx, real_fft_buffer| {
+ self.stft
+ .process_overlap_add(buffer, OVERLAP_TIMES, |_channel_idx, real_fft_buffer| {
// Forward FFT, the helper has already applied window function
self.r2c_plan
.process_with_scratch(real_fft_buffer, &mut self.complex_fft_buffer, &mut [])
@@ -151,8 +148,10 @@ impl Plugin for Stft {
self.c2r_plan
.process_with_scratch(&mut self.complex_fft_buffer, real_fft_buffer, &mut [])
.unwrap();
- },
- );
+
+ // Apply the window function. We can do this either before the DFT or after the IDFT
+ util::window::multiply_with_window(real_fft_buffer, &self.window_function);
+ });
ProcessStatus::Normal
}
diff --git a/plugins/puberty_simulator/src/lib.rs b/plugins/puberty_simulator/src/lib.rs
index 04dbae26..1946b622 100644
--- a/plugins/puberty_simulator/src/lib.rs
+++ b/plugins/puberty_simulator/src/lib.rs
@@ -205,7 +205,8 @@ impl Plugin for PubertySimulator {
let window_size = self.window_size();
let overlap_times = self.overlap_times();
let sample_rate = context.transport().sample_rate;
- let gain_compensation: f32 = 1.0 / (overlap_times as f32).log2() / window_size as f32;
+ let gain_compensation: f32 =
+ (overlap_times as f32 / 2.0).sqrt().recip() / window_size as f32;
// If the window size has changed since the last process call, reset the buffers and chance
// our latency. All of these buffers already have enough capacity
@@ -221,11 +222,8 @@ impl Plugin for PubertySimulator {
[self.params.window_size_order.value as usize - MIN_WINDOW_ORDER];
let mut smoothed_pitch_value = 0.0;
- self.stft.process_overlap_add(
- buffer,
- &self.window_function,
- overlap_times,
- |channel_idx, real_fft_buffer| {
+ self.stft
+ .process_overlap_add(buffer, overlap_times, |channel_idx, real_fft_buffer| {
// This loop runs whenever there's a block ready, so we can't easily do any post- or
// pre-processing without muddying up the interface. But if this is channel 0, then
// we're dealing with a new block. We'll use this for our parameter smoothing.
@@ -298,8 +296,10 @@ impl Plugin for PubertySimulator {
.c2r_plan
.process_with_scratch(&mut self.complex_fft_buffer, real_fft_buffer, &mut [])
.unwrap();
- },
- );
+
+ // Apply the window function. We can do this either before the DFT or after the IDFT
+ util::window::multiply_with_window(real_fft_buffer, &self.window_function);
+ });
ProcessStatus::Normal
}
diff --git a/src/util/stft.rs b/src/util/stft.rs
index 5fc61ea6..ae35d1d4 100644
--- a/src/util/stft.rs
+++ b/src/util/stft.rs
@@ -1,6 +1,5 @@
//! Utilities for buffering audio, likely used as part of a short-term Fourier transform.
-use super::window::multiply_with_window;
use crate::buffer::{Block, Buffer};
/// Some buffer that can be used with the [`StftHelper`].
@@ -30,6 +29,7 @@ pub trait StftInputMut: StftInput {
/// the same number of channels as the main input.
///
/// TODO: Better name?
+/// TODO: This needs an option that adds padding to the `real_fft_window`
/// TODO: We may need something like this purely for analysis, e.g. for showing spectrums in a GUI.
/// Figure out the cleanest way to adapt this for the non-processing use case.
pub struct StftHelper {
@@ -232,7 +232,8 @@ impl StftHelper {
/// Process the audio in `main_buffer` in small overlapping blocks with a window function
/// applied, adding up the results for the main buffer so they can be written back to the host.
- /// The window overlap amount is compensated automatically when adding up these samples.
+ /// Since there are a couple ways to do it, the window function needs to be applied in the
+ /// process callbacks. Check the [`nih_plug::util::window`] module for more information.
/// Whenever a new block is available, `process_cb()` gets called with a new audio block of the
/// specified size with the windowing function already applied. The summed reults will then be
/// written back to `main_buffer` exactly one block later, which means that this function will
@@ -252,8 +253,8 @@ impl StftHelper {
/// # Panics
///
/// Panics if `main_buffer` or the buffers in `sidechain_buffers` do not have the same number of
- /// channels as this [`StftHelper`], if the sidechain buffers do not contain the same number of
- /// samples as the main buffer, or if the window function does not match the block size.
+ /// channels as this [`StftHelper`], or if the sidechain buffers do not contain the same number of
+ /// samples as the main buffer.
///
/// TODO: Add more useful ways to do STFT and other buffered operations. I just went with this
/// approach because it's what I needed myself, but generic combinators like this could
@@ -261,7 +262,6 @@ impl StftHelper {
pub fn process_overlap_add(
&mut self,
main_buffer: &mut M,
- window_function: &[f32],
overlap_times: usize,
mut process_cb: F,
) where
@@ -271,7 +271,6 @@ impl StftHelper {
self.process_overlap_add_sidechain(
main_buffer,
[&NoSidechain; NUM_SIDECHAIN_INPUTS],
- window_function,
overlap_times,
|channel_idx, sidechain_idx, real_fft_scratch_buffer| {
if sidechain_idx.is_none() {
@@ -290,7 +289,6 @@ impl StftHelper {
&mut self,
main_buffer: &mut M,
sidechain_buffers: [&S; NUM_SIDECHAIN_INPUTS],
- window_function: &[f32],
overlap_times: usize,
mut process_cb: F,
) where
@@ -302,7 +300,6 @@ impl StftHelper {
main_buffer.num_channels(),
self.main_input_ring_buffers.len()
);
- assert_eq!(window_function.len(), self.main_input_ring_buffers[0].len());
assert!(overlap_times > 0);
// We'll copy samples from `*_buffer` into `*_ring_buffers` while simultaneously copying
@@ -395,7 +392,6 @@ impl StftHelper {
self.current_pos,
sidechain_ring_buffer,
);
- multiply_with_window(&mut self.scratch_buffer, window_function);
process_cb(channel_idx, Some(sidechain_idx), &mut self.scratch_buffer);
}
}
@@ -411,11 +407,9 @@ impl StftHelper {
self.current_pos,
input_ring_buffer,
);
- multiply_with_window(&mut self.scratch_buffer, window_function);
process_cb(channel_idx, None, &mut self.scratch_buffer);
// The actual overlap-add part of the equation
- multiply_with_window(&mut self.scratch_buffer, window_function);
add_scratch_to_ring_buffer(
&self.scratch_buffer,
self.current_pos,
@@ -433,7 +427,6 @@ impl StftHelper {
pub fn process_analyze_only(
&mut self,
buffer: &B,
- window_function: &[f32],
overlap_times: usize,
mut analyze_cb: F,
) where
@@ -441,7 +434,6 @@ impl StftHelper {
F: FnMut(usize, &mut [f32]),
{
assert_eq!(buffer.num_channels(), self.main_input_ring_buffers.len());
- assert_eq!(window_function.len(), self.main_input_ring_buffers[0].len());
assert!(overlap_times > 0);
// See `process_overlap_add_sidechain` for an annotated version
@@ -486,7 +478,6 @@ impl StftHelper {
self.current_pos,
input_ring_buffer,
);
- multiply_with_window(&mut self.scratch_buffer, window_function);
analyze_cb(channel_idx, &mut self.scratch_buffer);
}
}