Check and Windows preview / check (patina) (push) Successful in 2m20s
Check and Windows preview / check (rivet) (push) Successful in 1m38s
Check and Windows preview / previews (push) Successful in 1m6s
Check and Windows preview / windows (push) Successful in 1m50s
Independent plugin release / publish (push) Successful in 8s
Independent plugin release / plan (push) Successful in 7s
Independent plugin release / build (push) Successful in 2m21s
556 lines
31 KiB
Markdown
556 lines
31 KiB
Markdown
# Patina: resonance-compressor research and design
|
||
|
||
Research date: **2026-11-05**. This document preserves the research and design
|
||
proposal. The first implementation is now in the repository; see the
|
||
[implementation and setup guide](README.md).
|
||
|
||
## 1. What we want to build
|
||
|
||
A Rust audio effect for FL Studio that reduces objectionable spectral peaks
|
||
while retaining the source's intended tone, movement, and punch. The conventional
|
||
name for this category is **dynamic resonance suppressor**. “Resonance compressor”
|
||
is a useful working name: gain reduction varies with both frequency and time.
|
||
|
||
The main application here is aggressive, harmonically rich riddim sound design,
|
||
with melodic parts and full mixes as additional evaluation material. A useful
|
||
result is a bass that keeps its vowel, metallic edge, and rhythm while a painful
|
||
ring becomes less distracting. Making every spectrum smooth is not the goal.
|
||
|
||
The recommendation is an independently designed **STFT spectral attenuation
|
||
engine**, first delivered as a separate CLAP effect. Start with explicit controls,
|
||
conservative reduction, linked stereo, delta monitoring, and correct host latency.
|
||
Add transient protection after validating the basic detector. Defer automatic
|
||
harmonic protection, multiple resolution engines, and zero-latency processing.
|
||
|
||
Throughout this document:
|
||
|
||
- **Documented behavior** means a manufacturer, paper, or source code describes it.
|
||
- **Proposed design** means our engineering choice, not a disclosed commercial
|
||
algorithm or a claim of equivalent sound quality.
|
||
- **Listening hypothesis** means something to test with producers.
|
||
|
||
No commercial binaries were reverse engineered or benchmarked. Product claims
|
||
below were researched from public primary sources; they are not listening results.
|
||
|
||
## 2. What the reference tools actually offer
|
||
|
||
### Quance — CodWaves
|
||
|
||
This is the product referred to as “quance.” At the research date its official
|
||
page lists **€49.99 launch pricing**, a crossed-out **€79.99** price, and a sale
|
||
ending November 30, 2026. These are temporary advertised prices, not a guaranteed
|
||
future checkout price. Formats listed are VST3, AU, and AAX for Windows/macOS.
|
||
|
||
Its useful workflow references include editable tame/protect regions, learn,
|
||
tone/transient protection, delta monitoring, and multiple processing grades.
|
||
The developer describes both multi-resolution studio processing and a separate
|
||
causal filter-bank audio path for zero added sample latency.
|
||
[Official product page](https://codwaves.com/quance/).
|
||
|
||
The manual describes a detector based on neighboring spectral content and an
|
||
adaptive longer-term reference. Reduction uses a soft knee and temporal
|
||
smoothing. Its zero-latency audio path still uses an STFT for detection: detection
|
||
therefore reacts late even though audio is not delayed. This distinction is
|
||
important for our design. The manual also documents stereo linking, dry alignment,
|
||
and an optional reduction ceiling. These descriptions do not disclose all tuning
|
||
or implementation details.
|
||
[Quance manual, especially §§9 and 13](https://codwaves.com/assets/Quance-User-Manual.pdf).
|
||
|
||
### Soothe2 — oeksound
|
||
|
||
Soothe2 documents adaptive resonance reduction with frequency-dependent attack
|
||
and release. Sharpness controls the shape of cuts; selectivity controls which
|
||
resonances qualify. A frequency curve steers sensitivity rather than applying
|
||
ordinary static EQ. Delta, external sidechain detection, stereo options, and
|
||
quality controls support contextual audition. The manual warns that extreme
|
||
settings can damage the sound.
|
||
|
||
Design lesson: keep **where to work**, **what qualifies**, **how deeply to cut**,
|
||
and **how quickly to react** understandable as separate decisions. This is an
|
||
interpretation of the workflow, not a reconstruction of Soothe2's DSP.
|
||
[Official Soothe2 manual](https://oeksound.com/manuals/soothe2/).
|
||
|
||
### Soothe3 — the current oeksound reference
|
||
|
||
Research should include Soothe3 rather than treating Soothe2 as the newest
|
||
version. Its manual describes a level-adaptive soft mode and an absolute-level
|
||
hard mode, a Detail control, maximum-cut limiting, and minimum-phase processing
|
||
by default with a linear-phase option. It also documents frequency-dependent
|
||
timing adjustments. These are useful references for control design and phase
|
||
tradeoffs; our first STFT processor would not reproduce these modes.
|
||
[Official Soothe3 manual](https://oeksound.com/manuals/soothe3/).
|
||
|
||
Oeksound's product listing currently advertises **$259** for Soothe3. Price alone
|
||
does not establish comparative value or sound quality.
|
||
[Official product listing](https://oeksound.com/plugins/).
|
||
|
||
### Useful lower-cost comparison points
|
||
|
||
| Tool | Why compare it | What the comparison does not establish |
|
||
| --- | --- | --- |
|
||
| TDR Nova | Free parallel dynamic EQ with four dynamic bands: a practical manual corrective baseline | It is not the same workflow as automatic dense resonance suppression |
|
||
| NIH-plug Spectral Compressor | Public Rust example of FFT-domain dynamics and sidechain spectral processing | Many-band compression is not automatically a resonance detector |
|
||
| Static parametric EQ | Tests whether a simple fixed cut solves the problem adequately | A changing resonance may need different treatment over time |
|
||
|
||
Sources: [Tokyo Dawn's Nova page](https://www.tokyodawn.net/tdr-nova/),
|
||
[Spectral Compressor's author README](https://github.com/robbert-vdh/nih-plug/blob/master/plugins/spectral_compressor/README.md).
|
||
|
||
We should compare output against bypass and a sensible manual correction before
|
||
claiming an advantage over either commercial reference.
|
||
|
||
## 3. The perceptual problem
|
||
|
||
### A peak is observable; an unwanted peak is a judgment
|
||
|
||
A spectral peak can be a fundamental, a formant, an instrument resonance, a room
|
||
mode, deliberate filter emphasis, or an analysis artifact. Height alone cannot
|
||
classify it as a defect. A stable peak may be the exact feature that makes a bass
|
||
recognizable. Noise-like sibilance can be objectionable without a narrow peak.
|
||
|
||
For this project, use **excess relative to a local spectral reference** as an
|
||
operational detection rule, and expose decisions to the producer. It is not a
|
||
physical measurement of the source's resonator and not an emotion detector.
|
||
|
||
### Frequency neighborhoods should reflect listening, not just FFT indices
|
||
|
||
Human auditory filtering provides a reason to explore perceptual frequency
|
||
coordinates. Glasberg and Moore's research derives auditory-filter shapes from
|
||
notched-noise experiments; its scope is hearing, not a universal rule for desirable
|
||
EQ. An ERB-spaced or logarithmic neighborhood is a useful engineering hypothesis.
|
||
It should not be marketed as a complete model of perception.
|
||
[Glasberg and Moore, 1990](https://pubmed.ncbi.nlm.nih.gov/2228789/).
|
||
|
||
### Relevant research on automatic resonance equalization
|
||
|
||
Grachten, Deruty, and Tanguy describe a spectral resonance attenuator separately
|
||
from a neural network that predicts attenuation strength. Their detector compares
|
||
a smoothed spectral reference with a perceptually weighted spectrum on a
|
||
log-frequency representation. Their listening experiment involved 15 professional
|
||
participants and 150 excerpts, with substantial variation in preferred treatment.
|
||
|
||
This supports trying a transparent, non-neural detector first and evaluating
|
||
preferred strength through listening. It does not demonstrate equivalence to
|
||
Soothe, success on riddim stems, or that their long analysis windows suit live
|
||
monitoring. Use the explicitly linked **2018 v1** when reproducing this account;
|
||
the unversioned arXiv record has subsequently changed.
|
||
[Paper, v1, §§2–3](https://arxiv.org/pdf/1807.08636v1).
|
||
|
||
Corbach and colleagues study automated room-resonance suppression using measured
|
||
room behavior and parametric filtering. It is relevant to targeted filters, but
|
||
room-response identification is a different problem from deciding which peaks
|
||
to remove from an arbitrary musical recording.
|
||
[DAFx-09 paper](https://dafx.de/paper-archive/2009/papers/paper_28.pdf).
|
||
|
||
### Consequences for riddim and melodic bass
|
||
|
||
The following are project-specific listening hypotheses, not findings established
|
||
by the cited papers:
|
||
|
||
- A resonant vowel sweep may be the musical gesture. Protect its body and reduce
|
||
only a distracting upper whistle instead of flattening the entire sweep.
|
||
- Distortion and multiband dynamics can change which partial dominates from note
|
||
to note. Evaluate after the intended processing chain as well as before it.
|
||
- A repeated metallic resonance can function as a hook. Persistence alone is
|
||
insufficient evidence that it should be removed.
|
||
- A snare's initial crack and its later ring need different treatment. Preserving
|
||
the attack while controlling the tail may retain impact.
|
||
- Cleaning a soloed bass can reduce its ability to cut through the mix. Make final
|
||
decisions against the drums and melodic context, at comparable listening level.
|
||
|
||
The Devath reference work in this repository measured broad signal changes. It
|
||
did not establish which resonances were intentional or identify an ideal correction.
|
||
Use original stems and controlled synthetic examples for development; full mastered
|
||
references cannot isolate cause or provide a clean target.
|
||
|
||
## 4. Architecture choices
|
||
|
||
| Approach | Advantages | Main difficulties | Recommendation |
|
||
| --- | --- | --- | --- |
|
||
| STFT → gain surface → inverse STFT | Dense frequency control; straightforward delta and analysis; testable reconstruction | Window latency, time/frequency tradeoff, ringing and modulation artifacts | First implementation |
|
||
| Detector → a few tracked dynamic notches | Economical audio path; can target persistent rings | Peak tracking, filter stability, crossing peaks, phase movement | Later experiment |
|
||
| Dense causal filter bank | Audio path can have no added buffering delay | Finite band selectivity, detector lag, interacting filters, moving phase | Separate live-mode project |
|
||
| Multi-resolution spectral processing | Longer low-frequency observation and shorter high-frequency observation | Crossovers, alignment, phase, extra CPU and more validation | After single-resolution baseline |
|
||
| Learned detector or parameter estimator | Could model preferences or source categories | Dataset, labels, domain shift, inference cost, explainability | Defer |
|
||
|
||
These are engineering tradeoffs. Zero audio delay does not imply instantaneous
|
||
detection. Minimum phase does not imply no phase change. Retaining FFT phase does
|
||
not make a time-varying processor equivalent to one fixed linear-phase filter.
|
||
|
||
## 5. Proposed first DSP pipeline
|
||
|
||
```text
|
||
main audio ── analysis window / FFT ──────────────── complex-bin gains
|
||
│ │
|
||
└─ detector spectrum inverse FFT
|
||
local reference synthesis window
|
||
prominence overlap-add
|
||
sensitivity mask │
|
||
knee + strength + cap wet audio
|
||
frequency/time smoothing │
|
||
└───────────────────────────────┘
|
||
main audio ── matching delay ── dry ── mix / delta / bypass ── output trim
|
||
```
|
||
|
||
This is our proposed baseline, not either manufacturer's internal block diagram.
|
||
An external sidechain can later replace the detector input without replacing the
|
||
main audio being filtered. Spectral ducking needs its own detection semantics;
|
||
simply feeding a resonance-only detector with a sidechain will not duck all its
|
||
musically relevant energy.
|
||
|
||
### 5.1 Reconstruction before suppression
|
||
|
||
Implement weighted overlap-add (WOLA), then demonstrate a unity-gain null against
|
||
the delayed input before adding detection. With analysis window `wa`, synthesis
|
||
window `ws`, and hop `H`, reconstruction requires a constant window-product sum:
|
||
|
||
```text
|
||
sum_m wa[n - mH] * ws[n - mH] = C
|
||
```
|
||
|
||
Normalize by `C`, or use a corresponding reconstruction-normalization buffer.
|
||
Use a compatible periodic window/hop pair and explicitly test stream startup,
|
||
tail flushing, and arbitrary host block boundaries. The analysis window alone
|
||
is not sufficient when a synthesis window is also applied.
|
||
[Julius O. Smith, Weighted Overlap Add](https://ccrma.stanford.edu/~jos/sasp/Weighted_Overlap_Add.html).
|
||
|
||
For the prototype, try a periodic square-root Hann pair with `H = N/4` and
|
||
the appropriate product-sum normalization. Use the FFT library's required
|
||
round-trip normalization separately. RealFFT does not normalize either transform;
|
||
a forward/inverse pair needs a total `1/N` factor.
|
||
[RealFFT scaling documentation](https://docs.rs/realfft/latest/realfft/#scaling).
|
||
|
||
### 5.2 Detector and local reference
|
||
|
||
The following equations define an original experimental detector. They are
|
||
starting points to measure and tune, not validated optimal settings.
|
||
|
||
For channel `c`, frame `t`, bin `k`:
|
||
|
||
```text
|
||
P[c,t,k] = calibrated_power(|X[c,t,k]|²)
|
||
L[c,t,k] = 10 * log10(max(P[c,t,k], epsilon))
|
||
B[c,t,k] = robust_local_reference(L[c,t,*], frequency[k], neighborhood_width)
|
||
E[c,t,k] = max(0, L[c,t,k] - B[c,t,k])
|
||
```
|
||
|
||
`calibrated_power` must define its units. Correct for FFT size, window gain, and
|
||
one-sided spectrum conventions before introducing any absolute-level threshold.
|
||
Do not interchange per-bin power, band energy, and power spectral density.
|
||
|
||
Start with a log-frequency neighborhood and a bounded weighted mean baseline;
|
||
compare a median/percentile baseline offline. Excluding a small central region
|
||
can keep a strong peak from raising its own reference too much, but can also make
|
||
leakage look like multiple resonances. Define edge behavior near DC/Nyquist.
|
||
Precompute interpolation maps and neighbor lists outside the audio callback.
|
||
|
||
Adding a constant gain to the signal shifts both `L` and `B`; their difference is
|
||
approximately level-invariant above the numerical floor. That is useful for a
|
||
relative mode. An absolute gate, saturation, and noise-floor handling break this
|
||
invariance and must be documented and tested.
|
||
|
||
A smooth spectrum can still sound harsh, while harmonics can have large `E`.
|
||
Keep detection range and manual protection available from the first usable build.
|
||
Do not promise automatic separation of tone from unwanted resonance.
|
||
|
||
### 5.3 Independent controls and reduction law
|
||
|
||
Use these meanings consistently:
|
||
|
||
- **Sensitivity:** local-prominence threshold `T` in dB.
|
||
- **Amount:** slope `a` of attenuation above that threshold.
|
||
- **Max cut:** explicit upper bound `Dmax` on the applied attenuation surface.
|
||
- **Detail:** neighborhood/reduction width, with the actual mapping documented.
|
||
- **Focus/protect curve:** steers sensitivity or reduction; label which it does.
|
||
|
||
A simple continuous knee function for positive excess `u = E - T`, width `W > 0`:
|
||
|
||
```text
|
||
K(u,W) = 0 if u <= -W/2
|
||
(u + W/2)² / (2W) if -W/2 < u < W/2
|
||
u if u >= W/2
|
||
|
||
Dtarget = clamp(a * K(E - T,W), 0, Dmax)
|
||
```
|
||
|
||
For `W = 0`, use `max(0,u)` explicitly. This is a knee on **relative prominence**,
|
||
not the complete static transfer curve of an ordinary broadband compressor.
|
||
|
||
Apply smooth frequency limits and protection weights. Smooth reduction across
|
||
frequency to avoid isolated, rapidly appearing holes. Reapply the mask and cap
|
||
after operations that could spread or overshoot reduction into excluded regions.
|
||
Transition mask edits smoothly rather than abruptly changing gains.
|
||
|
||
### 5.4 Temporal behavior
|
||
|
||
Smooth attenuation in dB at the frame rate, with separate attack and release:
|
||
|
||
```text
|
||
alpha = exp(-H / (sample_rate * tau_seconds))
|
||
D[t,k] = alpha * D[t-1,k] + (1-alpha) * Dtarget[t,k]
|
||
```
|
||
|
||
Choose attack when the target attenuation increases and release when it decreases.
|
||
Handle zero time explicitly. The time constants describe this smoother, not total
|
||
response latency: windows, hop, and detector averaging also contribute.
|
||
|
||
Compare frequency-scaled timing against uniform timing; do not assume one wins.
|
||
Very fast changing narrow cuts can produce audible movement and sidebands.
|
||
Very slow cuts can miss a short whistle or remain after it disappears. Inspect
|
||
step response and note transitions as well as steady-state attenuation.
|
||
|
||
### 5.5 Apply the gain surface
|
||
|
||
```text
|
||
G[t,k] = 10^(-D[t,k]/20)
|
||
Y[t,k] = G[t,k] * X[t,k]
|
||
```
|
||
|
||
Use amplitude gains on complex bins, not power ratios directly. Preserve the
|
||
real-spectrum constraints at DC and Nyquist and keep reconstruction scaling
|
||
consistent. Initially retain each bin's phase; evaluate ringing and transient
|
||
smear honestly. This is not a claimed minimum-phase implementation.
|
||
|
||
`G <= 1` is a spectral attenuation constraint. It does **not** guarantee that
|
||
every output sample peak is lower than the input peak: changing frequency
|
||
components can change their time-domain cancellation. This is not a limiter.
|
||
|
||
### 5.6 Stereo and monitoring
|
||
|
||
Default to a shared attenuation surface for left and right. One proposed rule is
|
||
to compute both channels' target reductions and use their per-bin maximum before
|
||
smoothing. This avoids oppositely panned tones canceling in a summed detector.
|
||
Identical gains preserve per-bin L/R relationships, though tonal balance changes.
|
||
|
||
Add dual-mono and mid/side only after linked stereo passes image and mono checks.
|
||
Use an explicit reversible M/S normalization and test it with no attenuation.
|
||
|
||
For monitoring, define the signal taps precisely:
|
||
|
||
```text
|
||
delta = delayed_dry - wet_before_output_trim
|
||
mixed = (1-mix) * delayed_dry + mix * wet
|
||
```
|
||
|
||
Use a linear dry/wet law so identical dry/wet signals remain unity at all mix
|
||
positions. Bypass keeps the same delay and crossfades to dry. Delta bypasses makeup
|
||
gain and loudness matching so the subtraction remains interpretable. It reveals
|
||
changed material, including phase/time changes; it is not proof that everything
|
||
heard there was unwanted. Restore normal monitoring when loading a preset.
|
||
|
||
## 6. Resolution, latency, and phase
|
||
|
||
For a length-`N` transform, nominal bin spacing is `sample_rate/N`. This is not
|
||
the full practical ability to distinguish neighboring tones: the window matters.
|
||
Zero-padding adds interpolated spectral samples, not more observation time.
|
||
|
||
Calculated examples at **48 kHz**, with `H=N/4`:
|
||
|
||
| FFT length | Observation length | Bin spacing | Hop duration |
|
||
| --- | --- | --- | --- |
|
||
| 1024 | 21.33 ms | 46.875 Hz | 5.33 ms |
|
||
| 2048 | 42.67 ms | 23.438 Hz | 10.67 ms |
|
||
| 4096 | 85.33 ms | 11.719 Hz | 21.33 ms |
|
||
| 8192 | 170.67 ms | 5.859 Hz | 42.67 ms |
|
||
|
||
These are calculations, not measured plugin latencies. A causal implementation's
|
||
actual buffering schedule determines its reported delay. In the repository's
|
||
pinned NIH-plug revision, `StftHelper::latency_samples()` returns its active
|
||
window length. With that helper, `N=2048` reports 2048 samples, not `N/2` or `H`.
|
||
Report the actual helper delay to the host and verify it with an impulse.
|
||
[Pinned STFT helper source](https://github.com/robbert-vdh/nih-plug/blob/de421011f41a6d10fc8c7a6084e4f4dee0143683/src/util/stft.rs).
|
||
|
||
Start with one fixed configuration: `N=2048`, `H=512` at 44.1/48 kHz. Consider
|
||
doubling both at 88.2/96 kHz to retain similar time/frequency behavior. This choice
|
||
is primarily for mid/high resonance work; it is not precise sub-bass surgery.
|
||
Increasing FFT length while retaining 75% overlap also increases time between
|
||
updates. Greater overlap can improve update rate at extra CPU cost.
|
||
|
||
Do not advertise zero latency for this first engine. Keep quality changes out
|
||
of ordinary automation initially. Later mode switches need state transitions,
|
||
host latency updates, and aligned crossfades or a controlled processing restart.
|
||
Offline rendering should initially use the same DSP configuration as playback.
|
||
|
||
Linear-phase-style spectral shaping can ring before transients. Minimum-phase
|
||
shaping changes phase and can complicate parallel blending. Neither removes the
|
||
time/frequency tradeoff. A future minimum-phase option needs its own validated
|
||
filter construction, not just a label on the existing gain surface.
|
||
|
||
## 7. Protection features worth researching next
|
||
|
||
| Feature | Candidate approach | Failure to evaluate |
|
||
| --- | --- | --- |
|
||
| Transient protection | Positive spectral flux or fast/slow energy comparison reduces suppression briefly; separate onset and tail | Missing a short painful peak, or ducking protection too often on distorted bass |
|
||
| Harmonic protection | Confidence-weighted fundamental/harmonic evidence relaxes reduction | Polyphonic, inharmonic, noisy, or rapidly changing tones; a harmful ring may itself be pitched |
|
||
| Learn | Aggregate prominence statistics over a representative passage and propose editable focus points | Learning the hook as a defect or treating one passage as the entire song |
|
||
| Fixed protect regions | User draws a smooth exclusion around the bass body or snare | Protecting a problem along with the desired tone |
|
||
| External spectral ducking | Derive a smooth masking/priority curve from a routed reference | Removing too much accompaniment, noisy sidechains, missing input routing |
|
||
|
||
For a first protection feature, prefer an optional transient guard with a visible
|
||
effect and an off switch. Explicit protect regions are a more dependable initial
|
||
interface than promising perfect automatic harmonic preservation.
|
||
|
||
If Learn is added, keep the result editable and undoable; show exactly which
|
||
parameters changed. It should suggest where to investigate rather than declare
|
||
that detected peaks are mistakes.
|
||
|
||
## 8. Professional workflow
|
||
|
||
1. Insert on the problem source or bus and loop the offending passage in context.
|
||
2. Restrict the processing range; protect the source's body or deliberate vowel.
|
||
3. Raise Amount until the problem is clearly affected, then back off. Use an
|
||
explicit modest Max cut as an initial experiment, not a universal preset.
|
||
4. Listen to delta briefly. If it carries the melody, vowel, or whole snare attack,
|
||
reassess what is being removed. This is a diagnostic, not an automatic verdict.
|
||
5. Adjust sensitivity/detail and timing. Check the quieter phrase and the next
|
||
note as well as the loudest part.
|
||
6. Compare bypass and processed sound at similar loudness in the whole mix.
|
||
7. Save A/B states and automation only when the choice improves the musical role.
|
||
|
||
Useful starting tasks: upper-mid whistle on a growl, a moving metallic partial,
|
||
snare-tail ring, a bright melodic layer masking another part, and harsh buildup
|
||
on a bass bus. Evaluate these separately: one universal setting is unlikely.
|
||
|
||
The first interface should expose Amount, Sensitivity, Detail, Max cut, Attack,
|
||
Release, frequency limits, linked stereo, Mix, output trim, Delta, and Bypass.
|
||
Show input spectrum and actual reduction as distinct traces, with a fixed
|
||
logarithmic frequency axis and readable reduction units. The UI should distinguish
|
||
frequency sensitivity from static EQ gain.
|
||
|
||
Loudness matching can initially be a manual output-trim workflow. A later automatic
|
||
comparison feature should use bounded, slow compensation with silence handling;
|
||
avoid makeup gain that disguises excessive removal or boosts between phrases.
|
||
|
||
## 9. Rust integration in this repository
|
||
|
||
Current components, kept separate from Rivet’s musical generator:
|
||
|
||
```text
|
||
plugins/patina/core/ detector, gain law, smoothing, fixed DSP state
|
||
plugins/patina/render/ offline WAV processing and measurement harness
|
||
plugins/patina/ui/ shared native editor and font assets
|
||
plugins/patina/plugin/ independent NIH-plug CLAP effect wrapper
|
||
```
|
||
|
||
Reuse the existing pinned NIH-plug wrapper strategy and packaging conventions,
|
||
but register a new plugin identifier and an **audio effect** layout with mono and
|
||
stereo input/output. Do not inherit the generator's instrument classification,
|
||
MIDI scheduling, or sound-generation path. External audio sidechain is a later
|
||
optional port with explicit host-routing tests.
|
||
|
||
Candidate FFT implementation: `realfft` backed by `rustfft`. Plan transforms and
|
||
allocate all ring buffers, spectra, smoothing state, neighbor maps, and scratch
|
||
storage during initialization. RustFFT's convenient `process()` allocates scratch;
|
||
use the supplied-scratch methods in the processing path and query their required
|
||
sizes during setup.
|
||
[RustFFT API](https://docs.rs/rustfft/latest/rustfft/trait.Fft.html).
|
||
|
||
Additional engineering requirements:
|
||
|
||
- No heap allocation/deallocation, blocking locks, file I/O, or FFT planning in
|
||
the audio callback; prevent an overwritten shared buffer from freeing there.
|
||
- Bound detector work. A naive all-bin neighborhood scan is quadratic; use
|
||
precomputed bounded neighborhoods, running sums, or a coarser detector grid.
|
||
- Process arbitrary host buffer lengths through the ring-buffer scheduler.
|
||
- Smooth automatable parameters; keep latency-changing parameters nonautomatable
|
||
until the transition contract is implemented.
|
||
- Decimate analyzer data into preallocated UI buffers; rendering must not drive
|
||
processing or stall the callback.
|
||
- Reset histories and delay buffers consistently on reinitialization. Bound and
|
||
version state; default to normal monitoring, never unexpected delta playback.
|
||
- Sanitize invalid numerical state and handle silence/denormals without noise
|
||
bursts. Flush the delayed tail correctly in offline rendering.
|
||
|
||
### Existing source-code reference and licensing distinction
|
||
|
||
NIH-plug itself has an ISC license, but its **Spectral Compressor example is
|
||
GPL-3.0-or-later**, including in the locally cached revision used by this project.
|
||
The example's manifest and source header were checked. Treat it as a reference;
|
||
do not assume the framework license applies to every bundled plugin or copy that
|
||
implementation into an ISC-labeled plugin without resolving the differing terms.
|
||
[Framework license](https://github.com/robbert-vdh/nih-plug/blob/master/LICENSE),
|
||
[example manifest](https://github.com/robbert-vdh/nih-plug/blob/master/plugins/spectral_compressor/Cargo.toml).
|
||
|
||
## 10. Validation before calling it useful
|
||
|
||
### Deterministic DSP checks
|
||
|
||
| Check | Required observation |
|
||
| --- | --- |
|
||
| Unity reconstruction | With attenuation disabled, null against delayed input within a declared floating-point tolerance, including startup/tail |
|
||
| Reported latency | Impulse delay agrees with host-reported samples at each supported sample rate |
|
||
| Dry/mix/bypass | Unity response at every Mix position when wet equals dry; crossfades and delta use aligned taps |
|
||
| Synthetic narrow ring | A gated tone over a shaped noise bed triggers localized reduction with predictable timing |
|
||
| Sweeping tone | No unstable hopping, clicks, or unexpectedly wide removal as a peak crosses bins |
|
||
| Harmonic stack | Fundamentals and intentional harmonics reveal the detector's false-positive behavior |
|
||
| Silence and fades | No gain bursts, NaNs, denormal CPU spikes, or stale reduction after silence |
|
||
| Gain scaling | Relative mode behaves similarly above its gate/floor after input-level changes |
|
||
| Stereo | In-phase, opposite-polarity, hard-panned, and decorrelated inputs retain expected linking behavior |
|
||
| Parameter motion | Extreme valid automation does not click, allocate, or exceed the defined attenuation cap |
|
||
| Block invariance | Equivalent stream output for fixed, tiny, irregular, and changing host buffer sizes |
|
||
| Real-time performance | Allocation counter stays zero during processing; worst-frame CPU tested with many instances |
|
||
|
||
Measure the gain-surface bound separately from output sample/true-peak behavior.
|
||
Use swept and stepped resonances to measure attack, release, and detector lag;
|
||
steady-state plots alone are insufficient. Benchmark on named hardware with
|
||
stereo, sample rate, FFT size, overlap, host buffer size, and instance count stated.
|
||
|
||
### Listening evaluation
|
||
|
||
Use producer-owned dry stems, synthetic controls, and several source categories.
|
||
For each, compare bypass, a manual EQ/dynamic-EQ correction, our output, and
|
||
commercial references only when legitimately available. Randomize labels and
|
||
match listening level before judging. Include cases where bypass is preferred.
|
||
|
||
Ask concrete questions: Is the whistle less distracting? Does the vowel still
|
||
move? Does the snare retain impact? Does the melodic layer retain its identity?
|
||
Can a producer achieve the result faster? Repeat on the complete arrangement.
|
||
|
||
Record dulling, loss of body, pre-ringing, warbling, transient smear, image changes,
|
||
and the amount of useful adjustment. A visually flatter spectrum and a larger
|
||
reduction number are not success metrics.
|
||
|
||
### FL Studio acceptance
|
||
|
||
Verify discovery as an effect, insert routing, latency compensation, parallel
|
||
mixing, automation, save/reopen, sample-rate changes, render tails, and repeated
|
||
instance loading. Test mono/stereo channel layouts and multiple buffer sizes.
|
||
When sidechain support arrives, test absent/connected/changed routes explicitly.
|
||
Do not infer host compatibility solely from a successful Rust build.
|
||
|
||
## 11. Implementation sequence and unresolved decisions
|
||
|
||
1. **Reconstruction milestone:** offline identity processor, impulse/latency tests,
|
||
ring-buffer scheduling, and allocation checks. No suppression UI yet.
|
||
2. **Detector milestone:** relative prominence, explicit range/protection, knee,
|
||
separate Amount/Max cut, linked stereo, gain smoothing, and offline comparisons.
|
||
3. **Usable effect milestone:** CLAP audio wrapper, stable parameters and state,
|
||
analyzer/reduction display, delta, aligned bypass/mix, and FL Studio validation.
|
||
4. **Musical evaluation milestone:** transient guard, producer A/B sessions, tuned
|
||
defaults, CPU measurements, and a documented set of failures.
|
||
5. **Only then:** learn, harmonic evidence, sidechain ducking, additional quality
|
||
modes, minimum-phase filtering, or a separate low-latency engine.
|
||
|
||
Open decisions require prototypes: the local baseline statistic and bandwidth,
|
||
detector grid, detail mapping, smoothing order, frequency-scaled timing, and the
|
||
acceptable CPU/latency envelope. No claim of matching Quance or Soothe is justified
|
||
until repeatable comparisons establish which tasks the new plugin handles well.
|
||
|
||
The most valuable first outcome is a reliable, understandable suppressor that
|
||
helps the producer keep a sound's character while making a specific annoyance
|
||
easier to control.
|
||
|
||
|
||
## 12. First implementation
|
||
|
||
Version 0.1 uses a complex RustFFT engine with preallocated scratch storage.
|
||
The initial mixing configuration is 4096 samples with a 512-sample hop at
|
||
44.1/48 kHz, scaling both for higher supported rates. This supersedes the
|
||
smaller experimental configuration proposed above. It implements linked stereo,
|
||
relative prominence, soft-knee bounded reduction, explicit focus/protect controls,
|
||
transient guard, aligned monitoring, and the full visible engineer panel.
|
||
The original research scope and unverified listening hypotheses still apply.
|
||
|
||
The effect is independently implemented. The commercial references were not
|
||
reverse engineered. Tests establish reconstruction and scheduling behavior;
|
||
FL Studio acceptance and producer listening remain separate requirements.
|