1
0
Fork 0

Add a simple filter example with FFTW

This commit is contained in:
Robbert van der Helm 2022-03-06 17:54:23 +01:00
parent 295e5493ec
commit 0f78b02685
3 changed files with 450 additions and 20 deletions

367
Cargo.lock generated
View file

@ -18,6 +18,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e"
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.6"
@ -29,6 +35,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
dependencies = [
"memchr 0.1.11",
]
[[package]]
name = "anyhow"
version = "1.0.55"
@ -71,7 +86,7 @@ dependencies = [
"objc",
"raw-window-handle",
"uuid",
"winapi",
"winapi 0.3.9",
"x11",
"xcb 0.9.0",
"xcb-util",
@ -89,6 +104,33 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bzip2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cargo-nih-plug"
version = "0.1.0"
@ -108,6 +150,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
dependencies = [
"num",
"time",
]
[[package]]
name = "clap-sys"
version = "0.1.0"
@ -120,7 +172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342"
dependencies = [
"lazy-bytes-cast",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -209,6 +261,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam"
version = "0.8.1"
@ -252,7 +313,7 @@ checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
dependencies = [
"cfg-if",
"crossbeam-utils",
"lazy_static",
"lazy_static 1.4.0",
"memoffset",
"scopeguard",
]
@ -274,7 +335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
dependencies = [
"cfg-if",
"lazy_static",
"lazy_static 1.4.0",
]
[[package]]
@ -349,6 +410,57 @@ dependencies = [
"nohash-hasher",
]
[[package]]
name = "fftw"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dc6a84bbb6bac64521bfb5a544701ce118f329d550cf9b9054d1954fe61adef"
dependencies = [
"bitflags",
"fftw-sys",
"lazy_static 1.4.0",
"ndarray",
"num-complex",
"num-traits",
"thiserror",
]
[[package]]
name = "fftw-src"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08962470ab0e91e74ec7d338c8731476c28ed4e503a3080b0f001692e395a7c"
dependencies = [
"anyhow",
"cc",
"fs_extra",
"ftp",
"zip",
]
[[package]]
name = "fftw-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8e3951d695cc2f17610cd041e87ebc15078d1af5eb8c6be77921381fc98b3fd"
dependencies = [
"fftw-src",
"libc",
"num-complex",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -364,6 +476,23 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "fs_extra"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
[[package]]
name = "ftp"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "542951aad0071952c27409e3bd7cb62d1a3ad419c4e7314106bf994e0083ad5d"
dependencies = [
"chrono",
"lazy_static 0.1.16",
"regex",
]
[[package]]
name = "gain"
version = "0.1.0"
@ -429,6 +558,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "keyboard-types"
version = "0.6.2"
@ -450,6 +589,12 @@ version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b"
[[package]]
name = "lazy_static"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -469,7 +614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -499,6 +644,24 @@ dependencies = [
"libc",
]
[[package]]
name = "matrixmultiply"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1"
dependencies = [
"rawpointer",
]
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.4.1"
@ -529,6 +692,29 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "ndarray"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c0d5c9540a691d153064dc47a4db2504587a75eae07bf1d73f7a596ebc73c04"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "nih_plug"
version = "0.0.0"
@ -539,7 +725,7 @@ dependencies = [
"cfg-if",
"clap-sys",
"crossbeam",
"lazy_static",
"lazy_static 1.4.0",
"nih_plug_derive",
"parking_lot",
"raw-window-handle",
@ -566,7 +752,7 @@ dependencies = [
"crossbeam",
"egui",
"egui-baseview",
"lazy_static",
"lazy_static 1.4.0",
"nih_plug",
"parking_lot",
]
@ -607,11 +793,61 @@ version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
dependencies = [
"memchr",
"memchr 2.4.1",
"minimal-lexical",
"version_check",
]
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
dependencies = [
"num-integer",
"num-iter",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -706,7 +942,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b"
dependencies = [
"memchr",
"memchr 2.4.1",
]
[[package]]
@ -727,6 +963,12 @@ dependencies = [
"cty",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "redox_syscall"
version = "0.2.11"
@ -742,9 +984,28 @@ version = "0.1.3"
source = "git+https://github.com/nicokoch/reflink?rev=e8d93b465f5d9ad340cd052b64bbc77b8ee107e2#e8d93b465f5d9ad340cd052b64bbc77b8ee107e2"
dependencies = [
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "regex"
version = "0.1.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
dependencies = [
"aho-corasick",
"memchr 0.1.11",
"regex-syntax",
"thread_local",
"utf8-ranges",
]
[[package]]
name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
[[package]]
name = "ryu"
version = "1.0.9"
@ -835,7 +1096,7 @@ checksum = "1325f292209cee78d5035530932422a30aa4c8fda1a16593ac083c1de211e68a"
dependencies = [
"bitflags",
"dlib",
"lazy_static",
"lazy_static 1.4.0",
"log",
"memmap2",
"nix",
@ -859,6 +1120,7 @@ dependencies = [
name = "stft"
version = "0.1.0"
dependencies = [
"fftw",
"nih_plug",
]
@ -873,6 +1135,55 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
dependencies = [
"kernel32-sys",
"libc",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
dependencies = [
"thread-id",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]]
name = "toml"
version = "0.5.8"
@ -894,6 +1205,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
[[package]]
name = "uuid"
version = "0.8.2"
@ -1021,7 +1338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4"
dependencies = [
"dlib",
"lazy_static",
"lazy_static 1.4.0",
"pkg-config",
]
@ -1031,6 +1348,12 @@ version = "1.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6f1efe828a707edf85994a4501734ac1c1b9d244cfcf4de235f11c4125ace8f"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@ -1041,6 +1364,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -1181,3 +1510,17 @@ version = "0.1.0"
dependencies = [
"nih_plug_xtask",
]
[[package]]
name = "zip"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815"
dependencies = [
"byteorder",
"bzip2",
"crc32fast",
"flate2",
"thiserror",
"time",
]

View file

@ -10,3 +10,5 @@ crate-type = ["cdylib"]
[dependencies]
nih_plug = { path = "../../../", features = ["assert_process_allocs"] }
fftw = "0.7.0"

View file

@ -1,3 +1,6 @@
use fftw::array::AlignedVec;
use fftw::plan::{C2RPlan, C2RPlan32, R2CPlan, R2CPlan32};
use fftw::types::{c32, Flag};
use nih_plug::prelude::*;
use std::pin::Pin;
@ -7,25 +10,82 @@ const OVERLAP_TIMES: usize = 4;
struct Stft {
params: Pin<Box<StftParams>>,
/// An adapter that performs most of the overlap-add algorithm for us.
stft: util::StftHelper,
/// A Hann window window, passed to the overlap-add helper.
window_function: Vec<f32>,
/// The FFT of a simple low pass FIR filter.
lp_filter_kernel: Vec<c32>,
/// The algorithms for the FFT and IFFT operations.
plan: Plan,
/// Scratch buffers for computing our FFT. The [`StftHelper`] already contains a buffer for the
/// real values.
complex_fft_scratch_buffer: AlignedVec<c32>,
}
/// FFTW uses raw pointers which aren't Send+Sync, so we'll wrap this in a separate struct.
struct Plan {
r2c_plan: R2CPlan32,
c2r_plan: C2RPlan32,
}
unsafe impl Send for Plan {}
unsafe impl Sync for Plan {}
#[derive(Params)]
#[allow(clippy::derivable_impls)]
struct StftParams {}
impl Default for Stft {
fn default() -> Self {
let mut r2c_plan: R2CPlan32 = R2CPlan32::aligned(&[WINDOW_SIZE], Flag::MEASURE).unwrap();
let c2r_plan: C2RPlan32 = C2RPlan32::aligned(&[WINDOW_SIZE], Flag::MEASURE).unwrap();
let mut real_fft_scratch_buffer: AlignedVec<f32> = AlignedVec::new(WINDOW_SIZE);
let mut complex_fft_scratch_buffer: AlignedVec<c32> = AlignedVec::new(WINDOW_SIZE / 2 + 1);
// Build a super simple low pass filter from one of the built in window function
const FILTER_WINDOW_SIZE: usize = 33;
let filter_window = util::window::hann(FILTER_WINDOW_SIZE);
real_fft_scratch_buffer[0..FILTER_WINDOW_SIZE].copy_from_slice(&filter_window);
// Our STFT functions will have a window function applied, so we need to do the same thing
// with this filter
let window_function = util::window::hann(WINDOW_SIZE);
util::window::multiply_with_window(&mut real_fft_scratch_buffer, &window_function);
// And make sure to normalize this so convolution sums to 1
let filter_sum_recip = real_fft_scratch_buffer.iter().sum::<f32>().recip();
for sample in real_fft_scratch_buffer.as_slice_mut() {
*sample *= filter_sum_recip;
}
r2c_plan
.r2c(
&mut real_fft_scratch_buffer,
&mut complex_fft_scratch_buffer,
)
.unwrap();
Self {
params: Box::pin(StftParams::default()),
stft: util::StftHelper::new(2, WINDOW_SIZE),
window_function: util::window::hann(WINDOW_SIZE),
window_function,
lp_filter_kernel: complex_fft_scratch_buffer
.iter()
.take(WINDOW_SIZE)
.copied()
.collect(),
plan: Plan { r2c_plan, c2r_plan },
complex_fft_scratch_buffer,
}
}
}
#[allow(clippy::derivable_impls)]
impl Default for StftParams {
fn default() -> Self {
Self {}
@ -73,18 +133,43 @@ impl Plugin for Stft {
buffer: &mut Buffer,
_context: &mut impl ProcessContext,
) -> ProcessStatus {
const GAIN_COMPENSATION: f32 = 2.0 / OVERLAP_TIMES as f32;
const GAIN_COMPENSATION: f32 = 1.0 / OVERLAP_TIMES as f32 / WINDOW_SIZE as f32;
self.stft.process_overlap_add(
buffer,
[],
&self.window_function,
OVERLAP_TIMES,
|_channel_idx, _, block| {
for sample in block {
// TODO: Use the FFTW bindings and do some STFT operation here instead of
// reducing the gain at a 2048 sample latency...
*sample *= GAIN_COMPENSATION;
|_channel_idx, _, real_fft_scratch_buffer| {
// Forward FFT, the helper has already applied window function
self.plan
.r2c_plan
.r2c(
real_fft_scratch_buffer,
&mut self.complex_fft_scratch_buffer,
)
.unwrap();
// As per the convolution theorem we can simply multiply these two buffers. We'll
// also apply the gain compensation at this point.
for (fft_bin, kernel_bin) in self
.complex_fft_scratch_buffer
.as_slice_mut()
.iter_mut()
.zip(&self.lp_filter_kernel)
{
*fft_bin *= *kernel_bin * GAIN_COMPENSATION;
}
// Inverse FFT back into the scratch buffer. This will be added to a ring buffer
// which gets written back to the host at a one block delay.
self.plan
.c2r_plan
.c2r(
&mut self.complex_fft_scratch_buffer,
real_fft_scratch_buffer,
)
.unwrap();
},
);