Skip to content

Repository files navigation

fa76

FA76

A circuit-derived model of a 1176-style FET limiting amplifier, in Rust. VST3, CLAP, and a standalone harness.

The dynamics are not produced by an envelope follower and a gain computer. They come out of solving a closed feedback loop once per internal sample: a JFET shunt attenuator, an amplifier, a resistively switched detector network, a rectifier and a two-capacitor timing network. Gain reduction and distortion fall out of the same device equations at the same operating point, so they cannot be tuned apart — which is the whole point of building it this way.

There is no threshold parameter, no ratio constant, no knee control and no makeup gain anywhere in the core, because the hardware has none of those things. The compression curve is whatever the loop settles to.


Neural engine

The unit can also run a distilled neural model instead of the circuit. Same faceplate, same controls; the ENGINE chip in the header switches between them, and a right-click on it loads the next installed model.

Models are .fbmx files — a small container holding weights, the control surface they were trained with, and where the training data came from. They live in

~/Documents/Futureboard Studio/Utilities/Neural Models/

(FBMX_MODEL_DIR overrides it.) To see what the plugin can see:

cargo run -p fa76-neural --bin fa76-models -- --probe

The models are distilled from this circuit model, so they inherit its approximations plus whatever the fit missed. They are a research engine, not a replacement, and the differences are measured rather than guessed: as of the current models the release runs roughly twice as fast as the circuit's and the second harmonic about 10 dB high. crates/fa76-neural-lab in the parent workspace prints the full comparison.

Three limits are structural rather than incidental:

  • Mono. The models were trained on one channel, so stereo runs two independent copies. There is no detector linking in neural mode whatever the Stereo switch says.
  • Rev D only. The conditioning is Input, Attack, Release, Ratio. The revision switch does nothing to a neural model; the model carries the revision it was trained on.
  • OUTPUT is still real DSP. The output attenuator sits after the sidechain tap and cannot change gain reduction, so it was held fixed during training and is applied as a plain gain around the model.

The gain-reduction meter in neural mode is an estimate — the runtime does not execute the model's auxiliary gain head, so the engine infers it from the input/output envelopes against a small-signal gain it measures at load.

The engine adds no latency (the model is causal and has no oversampling), so switching engines changes the plugin's reported latency; hosts are told.

Building without any of this:

cargo bundle --no-default-features    # circuit only, no fbmx-runtime dependency

Build

# VST3 + CLAP into target/bundled/
cargo bundle

# the standalone harness: drop an audio file in and drive it
cargo playground

# offline DSP measurement (writes target/dsp-analysis/*.csv)
cargo lab linear
cargo lab all

Those are aliases in .cargo/config.toml. Spelled out:

cargo xtask bundle fa76-plugin --release
cargo run --release -p fa76-playground
cargo run --release -p fa76-dsp-lab -- linear

cargo xtask bundle fa76 will not work. fa76 is the DSP core: an rlib with no cdylib, so there is nothing to bundle. The plugin package is fa76-plugin. Use cargo bundle and the question does not arise.

Install by copying target/bundled/FA76.vst3 to your VST3 folder (C:\Program Files\Common Files\VST3\, ~/.vst3, or ~/Library/Audio/Plug-Ins/VST3) and rescanning.

Requires a recent stable Rust (built against 1.97). The plugin pulls nih-plug from git, pinned by revision.


Workspace

crate what it is dependencies
fa76 the DSP core none
fa76-ui the faceplate: geometry, widgets, presets, fonts fa76, egui
fa76-playground standalone: file loading, transport, waveform fa76-ui, eframe, cpal, symphonia
fa76-plugin VST3/CLAP wiring fa76-ui, nih_plug, nih_plug_egui
fa76-dsp-lab offline measurement and calibration fa76
xtask the bundler nih_plug_xtask

The core has zero dependencies and is tested on its own. The editor is written once against bare egui and shared verbatim between the standalone and the plugin, which talk to it through an EditorHost trait — the standalone backs it with atomics, the plugin with nih-plug parameters and automation gestures.


What it models

in ─► input transformer ─► INPUT attenuator
         │
         ▼
     FET shunt cell ◄──────────────────────────┐
         │                                     │
         ▼                                     │
      preamp ──┬──► ratio network ──► sidechain amp
         │     │         │                     │
         │     │    full-wave rectifier        │
         │     │         │                     │
         │     │    attack/release network     │
         │     │         │                     │
         │     └─────────┴──► control voltage ─┘
         ▼
   OUTPUT attenuator ─► output amplifier ─► output transformer ─► out

The detector is fed from the preamp output — signal that has already been through the gain-reduction cell — so the unit is a feedback compressor. That one wiring decision is why the knee is soft without a knee parameter existing, and why the OUTPUT control cannot change the amount of gain reduction.

The ratio buttons are not numbers. Each closes contacts that add resistors to a detector summing node; the node is solved as a one-node nodal problem, and the ratio, the threshold and the sidechain bandwidth all fall out of it. All Buttons is simply all four decks closed at once — nothing in the code branches on it — and the network lands on an operating point no single button can reach.

Measured behaviour

Slopes over the 1–15 dB gain-reduction window, and the level at which each deck first produces 1 dB of gain reduction:

mode threshold slope max GR
4:1 −10.36 dB 4.01:1 17.4 dB
8:1 −12.71 dB 8.41:1 21.9 dB
12:1 −15.59 dB 12.16:1 25.0 dB
20:1 −18.64 dB 19.58:1 28.7 dB
All Buttons −17.71 dB 17.29:1 27.4 dB

Linear baseline, Rev D at 48 kHz / 2×, relative to 1 kHz:

20 Hz   -0.163 dB      5 kHz   -0.000 dB
50 Hz   -0.029 dB     10 kHz   -0.001 dB
100 Hz  -0.007 dB     15 kHz   -0.004 dB
1 kHz    0.000 dB     20 kHz   -0.025 dB

Distortion at matched drive rises from 0.09 % at 10 kHz to 1.10 % at 50 Hz, with the third harmonic climbing 35 dB — that is the control voltage moving inside the cycle, not a bass-dependent effect anyone wrote.


Testing

cargo test --release -p fa76        # DSP core: 100 tests
cargo test --release --workspace    # everything
cargo clippy --workspace --all-targets

The suite covers static transfer curves, per-ratio thresholds, attack/release measurement, THD versus gain reduction, all-buttons comparison, sample-rate and oversampling regression, linear-response acceptance bounds, and robustness (NaN, DC, full-scale, rapid automation, block-size invariance).

Stage-by-stage isolation of the linear path is a development build:

cargo run --release -p fa76-dsp-lab --features diagnostics -- stages

It compiles to nothing without the feature and is never exposed as a user control.


Documentation

docs/dsp/1176-circuit-model.md the model, its assumptions, and the measurements behind them
docs/dsp/fa76-linear-calibration.md how the linear transfer function was diagnosed and corrected
docs/plugin.md VST3/CLAP build, parameters, latency
docs/playground.md the standalone harness and the panel

Each labels its claims: circuit-derived, fitted, approximated, or empirical.


Status and honesty

No hardware was measured while building this. The targets are published specifications and the widely reported behaviour of the design; component values are fitted to hit those, and the documentation says which are which. No claim of equivalence to any particular unit is made or implied.

Known limitations, in full in the docs:

  • THD is roughly flat against gain reduction at a fixed ratio. That is a real consequence of the topology, not an oversight, but it is less dramatic than the hardware's reputation and needs measurements to apportion.
  • The linear response is flatter than a real unit; the pole positions come from plausible component values rather than a measured sweep.
  • Transformer hysteresis is a rate-independent play operator: the loop width is right, the loop shape is not.
  • Rev A and Rev F are structural stubs — the FET and output stage differ correctly, but their detector networks have not been re-fitted, so their ratio markings will not measure right. Rev E is currently identical to Rev D.
  • The plugin has not been validated in a host. It builds and bundles; it has not been through pluginval or a DAW.

License

MIT. See LICENSE.

The panel artwork, the knob image and the bundled Mona Sans font in ui/assets/ are covered by their own licences, not by the MIT licence above.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages