Mikkeli Matlock c398b86d2b initial commit
2026-06-14 20:31:27 +09:00
2026-06-14 20:31:27 +09:00

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
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

# 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 — read the plugins/ examples first
  • NIH-plug docs
  • Cookiecutter template
  • egui docs
  • 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)
S
Description
No description provided
Readme GPL-3.0 410 KiB
Languages
Rust 95.8%
PowerShell 4%
Batchfile 0.2%