Files
codename-206/README.md
T
Mikkeli Matlock e9337473b9 minor desc change
2026-06-14 21:24:43 +09:00

238 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Codename 206
*Called 206 because the Peugeot 206 has a 'maxi' variant. You'll know this is a Maximizer knockoff if you can follow that trail of thoughts.*
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
- An 'All' aggregate channel: a 4th comp/lim stack on the summed bands, so bypassing all bands turns the plugin into a simple full-band compressor (mirrors FL's Maximizer)
- 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 ─┐ (bypassable)
├─ Band 2 (mid) → look-ahead delay → compressor VCA → gain stage ─┤ (bypassable)
└─ Band 3 (high) → look-ahead delay → compressor VCA → gain stage ─┤ (bypassable)
Sum of bands ◄──────────────────────────────────────────────------┘
└─ 'All' channel → look-ahead delay → compressor VCA → gain stage
└─ 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.
**The 'All' aggregate channel** (mirrors FL's Maximizer): the three bands are summed and the
result passes through a *fourth*, full-band compressor/limiter stack before the output limiter.
Because the LR4 filterbank sums phase-coherently flat, **bypassing all three bands leaves the
summed signal identical to the input** — so the plugin collapses into a plain single-band
compressor/limiter driven entirely by the 'All' channel. That makes "multiband off = simple comp"
a first-class mode, not an afterthought.
---
## 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
### 'All' Aggregate Channel
- Structurally **identical to a per-band compressor** — reuse the same comp/lim code/params, just fed the summed signal instead of a filtered band
- Runs after the three bands are summed, before the output brickwall limiter
- Bands are individually bypassable; with all three bypassed the (phase-coherent) crossover sum equals the dry input, so the 'All' channel alone acts as a full-band comp/lim
- Has its own look-ahead delay; total reported latency = max(band look-ahead) + 'All' look-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-Channel Compressor (× 4: low, mid, high, **all** — one `#[nested]` params struct reused)
- `threshold_db`
- `ratio` — 1.0 (off) to ∞ (limiting)
- `attack_ms`
- `release_ms`
- `knee_db` — soft knee width
- `makeup_gain_db`
- `bypass` — per-channel bypass (bypassing low+mid+high = simple full-band comp via the 'all' channel)
The 'all' channel uses the same struct so its UI and DSP are identical to a band; it just sits after the band sum.
---
## 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
The project is already scaffolded (NIH-plug + nih_plug_egui, pinned to a fixed git rev in
`Cargo.toml`). You do **not** need the Steinberg VST3 SDK — NIH-plug bundles its own bindings.
**Prerequisites (Windows):**
- Rust stable (`rustup``winget install Rustlang.Rustup`)
- Visual Studio 2022 with the "Desktop development with C++" workload (provides the MSVC linker)
```powershell
# Build + bundle the VST3 and CLAP
cargo xtask bundle codename_206 --release
# Output: target\bundled\Codename 206.vst3 and Codename 206.clap
```
### Deployment
FL Studio scans `C:\Program Files\Common Files\VST3` by default, **ignores directory junctions**
(so a symlinked bundle is invisible to its scanner), and caches failed scans. So deployment must
copy a *real* bundle into a folder FL scans, then FL must be told to rescan failed plugins.
Use the provided script (no need to remember the details):
```powershell
.\deploy.ps1 # build, then copy to the global VST3/CLAP folders (one UAC prompt)
.\deploy.ps1 -SkipBuild # reinstall the last build without rebuilding
.\deploy.ps1 -User # copy to %LOCALAPPDATA%\Programs\Common\VST3 instead (no admin) — best for a dev loop
```
`deploy.bat` is a double-click wrapper around the same script.
After deploying, in FL Studio: **Options → Manage plugins → tick "Rescan previously failed
plugins" → Find installed plugins**, then search for **Codename 206**. (The rescan-failed step
is essential — without it FL silently skips a plugin it has seen before.)
---
## 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)
- [ ] Add per-band bypass; with all bands bypassed, output must null against dry (proves the "simple comp" mode path)
- [ ] Apply per-band compressor to each band
- [ ] Sum bands back together
- [ ] Run the summed signal through the 'All' channel comp/lim (reuse the per-band compressor) before 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)