initial commit

This commit is contained in:
Mikkeli Matlock
2026-06-14 20:31:27 +09:00
commit c398b86d2b
+202
View File
@@ -0,0 +1,202 @@
# Codename 206
*Called 206 because the Peugeot 206 has a 'maxi' variant. Subdued lineage to the FL Studio plugin 'maximiser'*
Multiband Compressor / Limiter VST3 — Project Plan
## Overview
A VST3 multiband compressor/limiter with a custom gain curve display, inspired by FL Studio's Maximizer.
Built with **Rust** + **NIH-plug** (VST3 + CLAP output) + **egui** for the UI.
**Goals:**
- 3-band (configurable crossover points) compressor/limiter
- Look-ahead brickwall output limiter with true-peak detection
- Real-time gain reduction metering per band
- Custom gain curve visualiser
- Fully resizable vector UI
---
## Tech Stack
| Layer | Choice |
|---|---|
| Language | Rust (stable) |
| Plugin framework | [NIH-plug](https://github.com/robbert-vdh/nih-plug) |
| Plugin formats | VST3, CLAP |
| UI framework | egui (via `nih_plug_egui`) |
| Build tooling | `cargo xtask bundle` |
---
## Signal Flow
```
Input
└─ Crossover filterbank (Linkwitz-Riley LR4 @ each crossover freq)
├─ Band 1 (low) → look-ahead delay → compressor VCA → gain stage
├─ Band 2 (mid) → look-ahead delay → compressor VCA → gain stage
└─ Band 3 (high) → look-ahead delay → compressor VCA → gain stage
└─ Sum → output brickwall limiter (true-peak, 4x oversampled) → output
```
The detector for each band reads `look_ahead_ms` ahead of the VCA, so gain reduction is already ramping when the transient arrives.
---
## DSP Architecture
### Crossover Filterbank
- Linkwitz-Riley 4th-order (LR4) filters at each crossover frequency
- LR4 = two cascaded biquads (Butterworth LP or HP)
- Bands sum phase-coherently back to flat
- Crossover frequencies are user-adjustable parameters
### Per-Band Compressor
- Level detection: switchable RMS / peak, with configurable window
- Gain computer: threshold, ratio, soft knee
- Attack / release envelopes (logarithmic ballistics)
- Makeup gain per band
- Look-ahead: circular delay buffer on the audio path; detector reads ahead
### Output Limiter
- True-peak brickwall (ceiling = 0 dBFS or user-defined)
- 4x oversampling for inter-sample peak detection
- Short attack (≤ 0.1 ms), auto-release
### Latency
- Look-ahead duration must be reported via `Plugin::latency()` for DAW compensation
- All bands use equal delay to preserve phase alignment
---
## Parameters
### Global
- `input_gain` — pre-gain before filterbank (dB)
- `output_ceiling` — brickwall ceiling (dBFS, default 0.0)
- `look_ahead_ms` — look-ahead time (010 ms)
- `crossover_low_hz` — low/mid crossover frequency
- `crossover_high_hz` — mid/high crossover frequency
### Per Band (× 3, use a `#[nested]` params struct)
- `threshold_db`
- `ratio` — 1.0 (off) to ∞ (limiting)
- `attack_ms`
- `release_ms`
- `knee_db` — soft knee width
- `makeup_gain_db`
- `bypass` — per-band bypass
---
## Project Structure
```
src/
lib.rs # Plugin entry point, implements Plugin trait
params.rs # Params struct with NIH-plug #[id] attributes
dsp/
mod.rs
crossover.rs # LR4 filterbank (biquad chains)
compressor.rs # Per-band compressor + look-ahead
limiter.rs # Output true-peak brickwall limiter
biquad.rs # Generic biquad filter (Direct Form II transposed)
delay.rs # Circular buffer for look-ahead delay lines
oversampler.rs # 4x oversampler for true-peak detection
editor/
mod.rs # egui editor setup via nih_plug_egui
widgets/
gain_curve.rs # Custom egui Widget: gain curve display
band_meter.rs # Per-band gain reduction meter
level_meter.rs# Input/output level meter
```
---
## Build Steps
```bash
# Install Rust (if not already)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Clone NIH-plug cookiecutter or start fresh
cargo new --lib my_maximizer
cd my_maximizer
# Add dependencies to Cargo.toml:
# nih-plug = { git = "https://github.com/robbert-vdh/nih-plug", features = ["assert_process_allocs"] }
# nih-plug-egui = { git = "https://github.com/robbert-vdh/nih-plug" }
# Build and bundle
cargo xtask bundle my_maximizer --release
# Output: target/bundled/my_maximizer.vst3
```
---
## Implementation Order
Work through these stages in order — each stage produces a loadable, audible plugin.
### Stage 1 — Skeleton plugin
- [ ] NIH-plug "passthrough" compiling and loading in DAW
- [ ] `Params` struct with all parameters declared (no DSP yet)
- [ ] `process()` passes audio through untouched
- [ ] Verify plugin loads and parameters appear in DAW
### Stage 2 — Single-band compressor (no look-ahead, no UI)
- [ ] Implement `biquad.rs` — generic biquad, Direct Form II transposed
- [ ] Implement basic RMS level detector
- [ ] Implement gain computer (threshold, ratio, knee)
- [ ] Implement attack/release envelope on gain reduction
- [ ] Wire into `process()`, test with a sine sweep
### Stage 3 — Crossover filterbank
- [ ] Implement LR4 LP and HP biquad chains in `crossover.rs`
- [ ] Verify bands sum flat (null test: sum vs dry should be silence)
- [ ] Apply per-band compressor to each band
- [ ] Sum bands back to output
### Stage 4 — Look-ahead + brickwall limiter
- [ ] Implement `delay.rs` circular buffer
- [ ] Wire look-ahead: detector reads N samples ahead of VCA
- [ ] Report latency via `Plugin::latency()`
- [ ] Implement `oversampler.rs` (4x, use a polyphase FIR or windowed sinc)
- [ ] Implement brickwall output limiter with true-peak detection
### Stage 5 — Basic egui UI
- [ ] Add `nih_plug_egui` editor
- [ ] Knobs / sliders for all parameters
- [ ] Per-band bypass toggles
- [ ] Confirm UI controls update DSP in real time
### Stage 6 — Custom visualisations
- [ ] `level_meter.rs` — input/output RMS + peak meters
- [ ] `band_meter.rs` — per-band gain reduction meters (vertical bars)
- [ ] `gain_curve.rs` — static gain curve display per band (threshold/ratio/knee)
- [ ] Draggable crossover handles on a frequency display
---
## Key Implementation Notes
### No allocations in `process()`
Rust's borrow checker will help, but be explicit. All buffers (delay lines, filter states)
must be pre-allocated in `initialize()`. Use `assert_process_allocs` feature flag during
development to catch violations.
### Denormal flushing
Add `#[cfg(target_arch = "x86_64")] std::arch::x86_64::_MM_SET_FLUSH_ZERO_MODE(...)` in
`initialize()`, or add a small DC offset (1e-25) to filter inputs.
### Parameter smoothing
NIH-plug provides `Smoother` — use it for all gain/threshold params to avoid zipper noise.
### Thread safety
Params are atomics. The editor and audio thread communicate only through params and
`Arc<Mutex<...>>` meter data. Never pass DSP state to the UI directly.
### VST3 licensing
You must accept Steinberg's VST3 SDK licence before distributing VST3 binaries.
NIH-plug's VST3 bindings are GPLv3; if you distribute, the plugin must also be GPLv3
(or you need a commercial Steinberg licence). CLAP has no such restriction.
---
## Reference Material
- [NIH-plug repo](https://github.com/robbert-vdh/nih-plug) — read the `plugins/` examples first
- [NIH-plug docs](https://nih-plug.robbertvanderhelm.nl/)
- [Cookiecutter template](https://github.com/robbert-vdh/nih-plug-template)
- [egui docs](https://docs.rs/egui)
- Zölzer, *DAFX: Digital Audio Effects* — biquad filter cookbook
- Giannoulis et al., "Digital Dynamic Range Compressor Design" (JAES 2012) — compressor ballistics reference
- AES paper on true-peak limiting / inter-sample peaks (ITU-R BS.1770)