Skip to content

Latest commit

 

History

History
118 lines (91 loc) · 4.51 KB

File metadata and controls

118 lines (91 loc) · 4.51 KB

VST3 / CLAP build

cargo xtask bundle fa76-plugin --release

produces both bundles under target/bundled/:

target/bundled/FA76.vst3/Contents/x86_64-win/FA76.vst3
target/bundled/FA76.clap

Copy FA76.vst3 to C:\Program Files\Common Files\VST3\ (or ~/.vst3 on Linux, ~/Library/Audio/Plug-Ins/VST3 on macOS) and rescan in the host. The CLAP bundle is built from the same code at no extra cost, so it is shipped too.

Workspace layout

The plugin exists because the editor is shared, not duplicated:

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
xtask the bundler nih_plug_xtask

The core still has zero dependencies and is still tested on its own (cargo test -p fa76). Nothing in fa76-ui knows whether it is running inside eframe or inside a plugin window.

How one editor serves both

The two hosts store parameters completely differently - the standalone in atomics, the plugin in nih-plug's FloatParam/EnumParam with begin/set/end gestures so a DAW can record automation. The editor never touches either; it talks to a trait:

pub trait EditorHost {
    fn value(&self, control: Control) -> f32;
    fn set(&self, control: Control, value: f32);
    fn begin_gesture(&self, _control: Control) {}
    fn end_gesture(&self, _control: Control) {}
    fn gain_reduction_db(&self) -> f32;
    fn take_input_peak(&self) -> f32;
    fn take_output_peak(&self) -> f32;
}

Control values are in the panel's own units - dial 0..10 for the attenuators, 1..7 for the pots, an index into the enum's ALL for the switches - so nothing has to agree on a normalisation convention. The standalone implementation ignores the gesture calls; the plugin turns them into begin_set_parameter/end_set_parameter, so dragging a knob on the faceplate writes one clean automation gesture rather than a burst of unrelated writes.

egui version

nih_plug_egui pins egui 0.31, so the whole workspace is pinned there too. That is the reason the standalone uses eframe 0.31 rather than something newer: sharing the editor is worth more than being on the latest egui, and the alternative - two copies of the panel in two egui dialects - would drift apart within a week.

Parameters

Nine automatable parameters, named and scaled exactly as the panel reads them, so a host's generic UI shows the same numbers as the faceplate:

id type range
input float 0..10 (dial)
output float 0..10 (dial)
attack float 1..7 (dial, 7 = fastest)
release float 1..7 (dial, 7 = fastest)
ratio enum 4:1, 8:1, 12:1, 20:1, All Buttons
revision enum Rev A, Rev D, Rev E, Rev F
oversampling enum 1x, 2x, 4x, 8x
stereo enum Linked, Dual Mono
bypass bool

SAMPLE_ACCURATE_AUTOMATION is off deliberately. The model is a closed feedback loop with its own timing network; re-solving it at arbitrary sub-block boundaries would not make the automation more faithful, it would just cost CPU. Parameters are applied per block, and only when one has actually moved.

Latency

The core reports its oversampling group delay through FetLimiterCore::latency_samples(). It is zero at 1x, and a few samples otherwise; see docs/dsp/1176-circuit-model.md. There is no lookahead anywhere.

It is reported to the host from initialize, and again whenever the oversampling parameter changes - switching 1x -> 4x mid-session moves the delay, and a host that is not told will not recompensate it.

Class ID

VST3_CLASS_ID is b"FutureboardFA76." - exactly 16 bytes. Changing it makes hosts treat the plugin as a different one and drop saved state, so it must stay put once anything has been saved with it.

Testing

The DSP suite runs against the core, independent of any host:

cargo test -p fa76 --release      # 69 tests + doctest
cargo test -p fa76-ui             # preset round-trips
cargo clippy --workspace --all-targets

There is no automated test of the plugin wrapper itself. Validating it against a real host - pluginval, or simply loading it in a DAW - has not been done and is the obvious next step.