Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

VXN1 (“vixen 1”) is an 80s-style analogue polysynth by Vulpus Labs, built in Rust as a CLAP plugin. A VST3 build via clap-wrapper is on the roadmap (see Distribution).

This manual covers installation, the faceplate panel-by-panel, performance features (key modes, presets, MIDI), and engine internals. Read top-to-bottom for an end-to-end tour, or jump to the Parameter reference and Glossary for lookups.

What VXN1 is

A two-layer subtractive polysynth with 16 voices total (8 channels per layer). Each voice runs:

  • Two oscillators (Sine / Triangle / Saw / Pulse), plus a square sub-oscillator below osc 1 and a White/Pink noise source.
  • Cross-modulation between the oscillators: band-limited hard Sync, through-zero Phase Modulation (“FM” in UI labels), or diode-bridge Ring modulation.
  • OTA-C ladder filter (R3109/IR3109-flavoured) with LP / HP / BP / Notch modes and a 12 / 24 dB/oct slope switch. Separate pre-VCF high-pass filter.
  • Two ADSR envelopes (modulation + amplitude) with linear or exponential shapes.
  • Per-voice LFO 1 (retriggered or free-running) plus a global LFO 2 shared across both layers.
  • Fixed-route modulation panels: pitch, PWM, filter cutoff, cross-mod sweep, mod-wheel. No matrix — every route is labelled.

The instrument-level signal path adds a pre-chorus phaser, a vintage BBD chorus (Juno-60-flavoured), a stereo delay, and an FDN reverb, finished with a master volume, optional brick-wall limiter, and selectable oversampling at 1× / 2× / 4× / 8×.

What VXN1 is not

  • Not a matrix-modulation synth — modulation routes are fixed and named (ADR 0004).
  • Not a wavetable or sample-based instrument — strictly analogue-modelled subtractive.
  • Not a multitimbral workstation — two layers, one patch role each (Upper / Lower), no per-key splits beyond a single split point.
  • Not configurable for runtime polyphony — voice counts are static (8 per layer, 16 total).

Layout of this manual

SectionWhat it covers
Getting startedInstall the plugin, run it for the first time, build a mental model.
Faceplate referenceOne page per panel on the faceplate, in signal-flow order.
PerformanceKey modes, presets, MIDI, and the full parameter table.
InternalsEngine architecture, MVC layering, distribution. For developers and curious users.
AppendicesGlossary and changelog.

Parameter names and ranges in this manual come directly from vxn-engine/src/params.rs and the ADRs in vxn-1/adrs/. Where an ADR carries decision context that didn’t make the manual, cross-references are noted inline.

Installing VXN1

VXN1 currently ships as a CLAP plugin (canonical format, statically linked, no external runtime dependencies). VST3 distribution via clap-wrapper is planned (see ADR 0008 and Distribution) but not yet wired into the build.

Prerequisites

  • Rust 1.85+ (edition 2024).
  • macOS, Windows (x86_64), or Linux. Apple Silicon is the primary target; universal builds (arm64 + x86_64) are supported on macOS.

Build & install from source

From the workspace root (vxn-1/):

# CLAP, install to user directory
cargo xtask bundle --release --install

# CLAP, no install (bundle stays in target/)
cargo xtask bundle --release

# macOS universal (arm64 + x86_64), installed
cargo xtask bundle --release --universal --install

The available xtask flags are --release, --install, and --universal. Without --install, the .clap bundle is written under target/ and can be copied by hand.

Install locations

OSCLAP
macOS~/Library/Audio/Plug-Ins/CLAP/VXN1.clap
Windows%LOCALAPPDATA%\Programs\Common\CLAP\VXN1.clap
Linux~/.clap/VXN1.clap

Bundle identifier: labs.vulpus.vxn1.

Verifying the install

  1. Restart your DAW (or rescan plugins).
  2. Look for VXN1 under instruments / Vulpus Labs.
  3. Load it on a MIDI track. The faceplate should show the default patch (Saw / Saw, mid-cutoff, chorus on).

If the plugin doesn’t appear:

  • Check the install path matches the DAW’s CLAP search paths.
  • On macOS, see Unsigned binaries for Gatekeeper quarantine.
  • Confirm the host supports CLAP. Most modern hosts (Bitwig, Reaper, recent FL Studio / Ableton) do; some still need a CLAP plugin to be enabled in settings.

Unsigned binaries

Pre-release VXN1 builds are not yet code-signed or notarised. The OS will block them on first launch.

macOS

After copying VXN1.clap into the install location, clear the Gatekeeper quarantine attribute:

xattr -dr com.apple.quarantine ~/Library/Audio/Plug-Ins/CLAP/VXN1.clap

Restart the DAW after running these commands. If the plugin still doesn’t load, check the host’s plugin scan log — some hosts (Logic, Ableton Live) cache scan failures and need an explicit rescan after the quarantine flag is cleared.

Windows

SmartScreen may warn on first DAW launch after install. Click “More info” → “Run anyway”. This is a per-host-binary prompt, not per-plugin, so it shouldn’t recur.

Linux

No signing required. If the plugin doesn’t load, check dmesg for SELinux denials and the DAW’s log for symbol-resolution errors against libstdc++ / libc / libGL.

Building your own

Building VXN1 yourself from source (see Installing VXN1) sidesteps the unsigned-binary issue entirely — the local build is trusted by the OS as long as you trust the toolchain that produced it.

Quick start

This page walks you from a fresh VXN1 instance to a playable sound in about a minute.

Load the plugin

In your DAW, create a MIDI track and insert VXN1 as the instrument. The default patch is a saw-saw poly sound with chorus on — play a chord and you should hear a clear, slightly-detuned pad.

A tour of the faceplate

The default patch loads the Init state, which is a deliberately neutral starting point:

SectionDefault
Osc 1Saw, octave 0, level 0.8
Osc 2Saw, octave −1, level 0.6
Sub / NoiseOff
FilterLP, 24 dB/oct, cutoff 1 kHz, resonance 0.2, drive 1.0
Env 1 (mod)A 5 ms / D 300 ms / S 0 / R 300 ms, linear
Env 2 (amp)A 5 ms / D 200 ms / S 0.8 / R 300 ms, exponential
LFO 1Sine, 5 Hz, free-running off
LFO 2Sine, 5 Hz
ChorusOn, rate 0.6 Hz, mix 0.4
Oversample

Both oscillators are at full saw, and Env 2 (the amplitude envelope) is hardwired to the VCA — you’ll always hear sound at note-on.

Make a bass

  1. Filter: drop cutoff to ~400 Hz, push resonance to 0.5, drive to 1.5.
  2. Env 1: sustain 0, decay 200 ms.
  3. Filter Mod: set Cutoff Env Dep to about +48 (one octave open with each note).
  4. Voice: change Assign to Solo, set Glide Time to 30–50 ms.
  5. Chorus: turn off for a drier bass tone.

Make a pad

  1. Env 2: attack 800 ms, release 1.5 s, sustain 0.8.
  2. Filter: cutoff 2 kHz, slope 24 dB, resonance 0.1.
  3. Filter Mod: Cutoff LFO2 Dep ≈ +12, LFO 2 Rate ≈ 0.3 Hz for slow filter sweeps.
  4. Pitch Mod: LFO source = LFO 1, depth ≈ 0.1 st for subtle vibrato.
  5. Chorus: depth 0.7, mix 0.5.
  6. Reverb: on, size 0.6, decay 4 s, mix 0.35.

Make a lead

  1. Voice: Assign = Solo, Legato = on, Glide Time 80 ms.
  2. Cross-Mod Type = Sync, Cross-Mod Amount ~1.5, Osc 2 Coarse +7 — classic sync-lead character.
  3. Env 1: short attack, decay 150 ms, sustain 0; Cutoff Env Dep +24 for the snap.
  4. Mod Wheel: route Wheel→PWM 0.2, Wheel→Cutoff +18 — your wheel becomes a brightness/movement control.

Save your patch

The preset bar at the top of the faceplate (between the banner and the first row of controls) handles save/load: < name > walks through the combined factory + user list, Browse opens the folder/preset panel, Save overwrites the current user preset, Save As opens a name/folder dialog. New user presets land in:

  • macOS: ~/Library/Audio/Presets/Vulpus Labs/VXN1/
  • Windows: %APPDATA%\Vulpus Labs\VXN1\Presets
  • Linux: $XDG_DATA_HOME/VXN1/presets (fallback: ~/.local/share/VXN1/presets)

See Presets for the full preset model.

Next steps

Mental model

VXN1 is a classical subtractive synth with a few intentional deviations from the textbook layout. This page explains the signal path and modulation model in one pass.

Signal path

  • Per-voice path: oscillators → mixer → HPF → VCF → VCA. Runs at synthesis sample rate (optionally oversampled 1× / 2× / 4× / 8×).
  • Instrument-level path (post voice-mix): phaser → BBD chorus → stereo delay → FDN reverb → master volume → limiter (optional).
  • Voice rendering is per-sample (recurrences stay bit-faithful). Modulation runs at control rate — one update per 32-sample CONTROL_BLOCK.

Two layers, always

VXN1 always carries two complete patches in memory — the Upper and Lower layers. Each layer has 8 channels. What MIDI does to those layers is governed by the Key Mode:

  • Whole: 16-voice mono-timbral. Both layers play the same patch.
  • Dual: 8 + 8 stereo layering. Both layers play different patches simultaneously on every note.
  • Split: 8 + 8 split at a MIDI note. Below split → Lower; at-or-above split → Upper.

This is why some parameters are per-layer (everything in the oscillator / filter / envelope / LFO 1 / mod-route region) and some are global (master, LFO 2, all effects, oversampling).

Modulation: fixed routes, not a matrix

VXN1 does not have a modulation matrix. Instead, every musically common destination is a dedicated panel with explicit source selectors and depths. The full set:

DestinationSourcesNotes
Pitch (both osc, vibrato)LFO 1 / LFO 2 / Env 1 / Env 2 / Pitch Wheel±12 st on each route
PWM (Osc 1 + Osc 2 pulse width)LFO 1 / LFO 2 / Env 1 / Env 2 / Mod WheelFixed routes share both osc
Filter CutoffLFO 1 + LFO 2 + Env 1 + Velocity (four fixed depths) + Mod Wheel + Key TrackNo source selector — all four depth knobs are live simultaneously
ResonanceMod WheelSingle fixed route
Cross-Mod Sweep (wide pitch)Env 1 / Env 2 / Mod Wheel±48 st; only active when Cross-Mod Type ≠ Off
VCAEnv 2 (hardwired) + Amp LFO source selector + Tremolo depthEnv 2 always drives the VCA; Amp Gate bypasses to gate-only

If a modulation idea isn’t on a panel, VXN1 doesn’t do it. This was a deliberate trade (ADR 0004): give up the matrix to gain a faceplate where every patch element is visible without diving into menus.

Two envelopes, fixed roles

  • Env 1 is the modulation envelope. Default destinations: filter cutoff, pitch envelope, PWM envelope, cross-mod sweep. It is never wired to the VCA.
  • Env 2 is the amplitude envelope. Hardwired to the VCA. Can also drive pitch / PWM / cross-mod-sweep destinations, but its role at the VCA is non-negotiable (set Amp Gate to bypass Env 2 and have a gate-only amplitude).

Each envelope has a Shape parameter selecting linear or exponential segments. Env 1 defaults to linear; Env 2 defaults to exponential (smoother amplitude).

Two LFOs, different scopes

  • LFO 1 is per-voice. Each voice has its own phase. By default it retriggers at note-on (set Free-Run on to have one continuous phase across all notes). Has delay/fade controls so vibrato or tremolo can ease in.
  • LFO 2 is global. One phase, shared by every voice in both layers. Useful for whole-instrument cycles where you want everything moving in lockstep.

Both LFOs can host-sync to tempo via the Sync toggle.

Oversampling

The whole per-voice synthesis chain — oscillators, sub, noise, cross-mod, filter, drive saturation — runs at the oversampled rate. Effects (phaser, chorus, delay, reverb) run at the host sample rate. Higher oversampling reduces aliasing on sync, ring-mod, and resonant filter sweeps at CPU cost. Default is — adequate for most material; bump to 4× or 8× for aggressive sync leads or audible aliasing on bright patches.

Voice allocation modes

Per layer (the Voice & assign panel):

ModeBehaviour
PolyFirst-free voice, oldest-steal when full. Standard polyphonic behaviour.
UnisonAll 8 channels stack on every note. Per-channel detune (UnisonDetune) and phase decorrelation. Mono — one note at a time.
SoloOne channel, last-note priority. Legato controls whether the envelope retriggers on a new note.
TwinTwo channels per note, ±UnisonDetune apart. Halves effective polyphony but doubles each note.

Glide (PortamentoTime) is per-voice and applies in all modes.

With this in hand, the faceplate reference is mostly a walk through which knob does what within each box.

Panel overview

The VXN1 faceplate groups every parameter into labelled panels arranged roughly in signal-flow order: sources at the top, filter and amp in the middle, modulation routes alongside, effects at the bottom. The headers below match the panel labels on the faceplate.

PanelPage
Oscillator 1 / Oscillator 2Oscillators
Cross-Mod selector + amountCross-modulation
Mixer (Osc 1 / Osc 2 / Sub / Noise)Mixer
Filter (HPF + VCF)Filter
Env 1 / Env 2Envelopes
LFO 1 / LFO 2LFOs
Pitch Mod / PWM Mod / Filter Mod / Mod WheelModulation routes
Voice & assignVoice & assign
Phaser / Chorus / Delay / ReverbEffects
MasterMaster

Per-layer vs. global

Every panel above except LFO 2, the effects rack, and Master is per-layer — you get a separate set of values for Upper and Lower. The faceplate shows one layer at a time; the Layer switcher in the header chooses which.

In Whole mode, only the Upper layer is visible — Lower mirrors Upper. In Dual and Split modes, both layers carry independent state and you can switch between them freely.

Parameter conventions used in this manual

  • Range: the raw value range the parameter accepts.
  • Default: the value loaded by the Init patch.
  • Unit: the natural unit the value is displayed in on the faceplate.
  • Taper: how knob position maps to value. Linear is uniform; exponential is denser at the low end (good for time and frequency); enum is discrete steps.

Reading the parameter tables

Each panel page closes with a parameter table:

ParameterRangeDefaultUnitNotes
Example0.001–100.005sExponential taper

The full instrument-wide table is in the Parameter reference.

Oscillators

Each layer has two full-feature oscillators. They share the same parameter set; defaults differ (Osc 2 is one octave below Osc 1) so the Init patch sounds layered rather than unisoned.

Waveforms

Both oscillators offer four waveforms:

ValueWaveformCharacter
0SinePure sine, low harmonics. Useful as a PM modulator or sub-fundamental.
1TriangleSoft, mostly odd harmonics. Reedier than sine but smoother than saw.
2SawFull harmonic content, bright and dc-free. Default.
3PulseSquare at PW 0.5; narrows toward hollow / nasal at PW 0.05 or 0.95. Duty cycle set by PW parameter or PWM modulation.

The Saw and Pulse waveforms are band-limited via polyBLEP residuals — no zipper aliasing on sweeps, even at extreme oversampling settings.

Tuning

Three knobs stack:

  • Octave (−4 to +4 oct) — coarse octave transposition.
  • Coarse (−7 to +7 st) — semitone fine-tune, useful for 5ths / 7ths above Osc 1.
  • Fine (−50 to +50 ct) — cent detune, used most often on Osc 2 to thicken the pair.

Per-voice Drift (Master panel) adds a small random per-voice phase offset to both oscillators, modelling analogue tuning instability.

Pulse Width

Static PW (0.05–0.95) sets the square’s duty cycle. At 0.5 it’s a perfect square; values toward 0.05 / 0.95 give a thinner, more nasal tone.

For moving pulse width, see the PWM modulation route — LFO or envelope sources can sweep the width on top of the static setting.

Parameters — Osc 1

ParameterRangeDefaultUnitNotes
Osc 1 WaveSine / Tri / Saw / PulseSawenum
Osc 1 Octave−4 to +40oct
Osc 1 Coarse−7 to +70st
Osc 1 Fine−50 to +500ct
Osc 1 Level0–10.8linearMixer level
Osc 1 PW0.05–0.950.5dutyPulse waveform only

Parameters — Osc 2

ParameterRangeDefaultUnitNotes
Osc 2 WaveSine / Tri / Saw / PulseSawenum
Osc 2 Octave−4 to +4−1octDefaults one octave below Osc 1
Osc 2 Coarse−7 to +70st
Osc 2 Fine−50 to +500ct
Osc 2 Level0–10.6linear
Osc 2 PW0.05–0.950.5duty

In Sync mode, Osc 1 becomes the sync slave and Osc 2 the master — see Cross-modulation.

Cross-modulation

VXN1 collapses what most synths spread across three distinct features — hard sync, FM/PM, and ring modulation — into one Cross-Mod Type selector with a single Cross-Mod Amount depth. The selected mode determines what Osc 2 does to Osc 1.

Modes

TypeWhat it does
OffIndependent oscillators. Bit-identical to the no-cross-mod fast path.
SyncHard sync. Osc 1 is the slave; Osc 2 is the master. Sub-sample phase reset on every Osc 2 wrap, with polyBLEP residual for band-limiting. Sweep Osc 1’s pitch (typically via Pitch Env with Mod on) to get classic sync sweeps.
FM (PM)Through-zero phase modulation. Osc 2’s output offsets Osc 1’s read phase. Labelled “FM” on the panel for familiarity — internally it is true phase modulation, which means pitch stays stable as the modulator’s DC level moves.
RingDiode-bridge ring modulator (Parker DAFx-11 model). The ring output replaces Osc 1’s mixer slot — Osc 1’s normal waveform is gone, replaced by the ring product.

Amount

Cross-Mod Amount (0–4) maps to:

  • Sync: ignored — sync is binary (mode selects whether sync happens; amount has no effect).
  • PM/FM: phase deviation index. Higher values push Osc 1 into harsher inharmonic territory.
  • Ring: ring depth (mixer level for the ring product).

A common starting point: PM at amount ~1.5 with Osc 2 a few semitones above Osc 1 gives a bell-like timbre.

Cross-Mod Sweep

There is no dedicated cross-mod sweep route. The classic sound — a wide, envelope-driven pitch swing on the modulator only — is built from two parts on the modulation panel:

  • Pitch Env Mod switch (on the pitch route) — when on, the pitch envelope is routed only to the cross-mod modulator: Osc 1 under Sync, Osc 2 under PM / Ring / Off. Combine with the ±12 st Pitch Env Dep for the envelope swing.
  • Wheel→X-Mod (on the mod-wheel routes) — ±48 st wide-pitch route from MIDI CC1, applies to both oscillators in parallel. Use it for hands-on sweeping.

Pair Pitch Env Mod + a high Pitch Env Dep with Cross-Mod Type = Sync, and you have the canonical sync-sweep lead. Same trick under PM gives a swept-index FM bell.

The Wheel→X-Mod route is not gated by Cross-Mod Type. With Off or Ring it acts as a normal pitch joystick (both oscillators move together); with Sync or PM it audibly drives the cross-mod timbre.

Aliasing notes

Cross-mod can fold significant high-frequency energy back into the audible band:

  • Sync: polyBLEP-band-limited; safe at 1× oversample for most material.
  • PM with non-sine modulator: aliasing is by design — many vintage PM sounds rely on it. The 4× / 8× oversampling settings are the escape hatch if a specific patch needs cleaning up.
  • Ring: cleaner than PM, but ring of two saws will alias under 2×. Push oversampling for bright ring patches.

See Master for the global oversampling setting.

Parameters

ParameterRangeDefaultUnitNotes
Cross-Mod TypeOff / Sync / FM / RingOffenumMode selector
Cross-Mod Amount0–40depthIndex for PM, mixer level for Ring; ignored by Sync

Mixer

Five sources feed the filter:

  • Osc 1 at its Level knob (mixer column on the Osc 1 panel).
  • Osc 2 at its Level knob (mixer column on the Osc 2 panel).
  • Sub — a square wave one octave below Osc 1, useful for adding low-end weight without retuning the main oscillators.
  • Noise — white or pink, selected by the Noise Colour toggle.

All four sources sum into a single stereo mix before the high-pass and ladder filter.

Sub-oscillator

The sub is a square wave one octave below the carrier oscillator. By default that’s Osc 1; under Cross-Mod Type = Sync the sub follows Osc 2 instead (since Osc 1 is then a sync slave of Osc 2). Otherwise the sub tracks Osc 1’s tuning, including pitch modulation, and is band-limited.

A common move: pull Osc 1 Level to ~0.5, raise Sub Level to ~0.7 for a fat bass that retains its top-octave brightness from Osc 2.

Noise

Noise Colour:

  • White — uniform spectral density.
  • Pink — −3 dB/oct rolloff (1/f). Friendlier on the ladder filter at high resonance.

A small amount of noise (level ~0.1) blended with the oscillators gives a perceptual “lift” — the analog impression of subtle hiss in the signal path. Larger amounts move into wind and percussion territory; use the modulation envelope to gate noise bursts for snare-like attacks.

Parameters

ParameterRangeDefaultUnitNotes
Sub Level0–10linearSquare one octave below Osc 1
Noise Level0–10linear
Noise ColourWhite / PinkWhiteenum

Osc 1 Level and Osc 2 Level are documented on the Oscillators page.

Filter

VXN1’s filter section is a two-stage chain:

  1. High-pass filter (HPF) — 1-pole, pre-VCF. Used to trim low-end ahead of the main filter; great for keeping pads from muddying low registers.
  2. OTA-C ladder VCF — 4-pole transistor-ladder model (R3109 / IR3109-flavoured) with selectable LP / HP / BP / Notch outputs and a 12 / 24 dB/oct slope switch.

High-pass filter

Single knob: HPF Cutoff (20 Hz – 18 kHz, exp taper). Set it just above the fundamental of your lowest played note to remove rumble without thinning the body.

The HPF is a 1-pole (6 dB/oct) topology-preserving design with no resonance. It sits before the main filter, so the ladder receives an already-trimmed signal.

Main filter (VCF)

Cutoff (16.35 Hz – 16 kHz, exp taper centred at 800 Hz) controls the corner frequency. The taper sits a little above middle of the knob travel by default — this matches typical analog control voltages and gives even resolution around the most musically useful region.

Resonance (0–1, linear) increases feedback around the cutoff. The ladder will self-oscillate cleanly at the top of the range; settings around 0.5–0.7 give the characteristic emphasised cutoff peak without howl.

Drive (0.1–4, exp taper, default 1.0) saturates the input to the ladder. Below 1 the filter behaves cleaner / softer; above 1 the input clips into the per-stage tanh saturators (rational Padé(5,6) approximation, applied at each integrator input) and harmonic content blooms around the cutoff. Useful for adding bite without raising master volume.

Filter Mode selects which point on the ladder is tapped:

ModeBehaviour
LPClassic ladder low-pass (default).
HPHigh-pass output from the ladder (in addition to the pre-VCF HPF).
BPBandpass centred at cutoff, Q proportional to resonance.
NotchBand-reject (inverse of bandpass).

Filter Slope picks 12 dB/oct (2-pole) or 24 dB/oct (4-pole). 24 dB is the default — fatter, more “ladder-like.” 12 dB is brighter and lets more upper harmonics through, useful for leads where you want presence even at low cutoff.

Key Track

Key Track (0–1) is a continuous depth knob. At 1.0 the cutoff rises one octave per octave of key relative to C4 (the played note’s pitch tracks 1:1 into the filter). At 0 the cutoff stays fixed regardless of key. Intermediate values give proportional tracking — 0.5 is half-tracking, etc.

Set this to 1.0 for a filter that follows the keyboard fully; use lower values when you want bass notes to be darker than treble notes without going all the way to neutral.

Modulation

Filter modulation has four fixed depths with no source selector — every depth is live simultaneously. See Filter modulation:

  • Cutoff LFO1 Dep — LFO 1 (per-voice) into cutoff.
  • Cutoff LFO2 Dep — LFO 2 (global) into cutoff.
  • Cutoff Env Dep — Env 1 into cutoff. Can be negative (envelope closes the filter).
  • Vel→Cutoff — MIDI velocity into cutoff.

Plus from the Mod Wheel panel:

  • Wheel→Cutoff — MIDI CC1 into cutoff.
  • Wheel→Reso — CC1 into resonance.

Parameters

ParameterRangeDefaultUnitNotes
HPF Cutoff20–1800020HzPre-VCF high-pass
Cutoff16.35–160001000HzExp taper (mid 800 Hz)
Resonance0–10.2linear
Drive0.1–41.0linearExp taper (mid 1.0)
Filter ModeLP / HP / BP / NotchLPenumLadder output selector
Filter Slope12 dB / 24 dB24 dBenum2-pole or 4-pole
Key Track0–10linear1.0 = 1 octave cutoff / octave key
TunedOff / OnOffboolReserved for future cutoff-tuned-to-key offset behaviour; currently inactive

Envelopes

Each voice has two independent ADSR envelopes.

  • Env 1 — the modulation envelope. Default destinations: filter cutoff, pitch envelope, PWM envelope, cross-mod sweep. Never wired to the VCA.
  • Env 2 — the amplitude envelope. Hardwired to the VCA. Can additionally route to pitch / PWM / cross-mod sweep (via the source selectors on those panels), but its role at the VCA is non-negotiable.

Both envelopes have the same parameter shape: A / D / S / R + Shape.

Stages

  • Attack (A) — time from gate-on to peak. 0.001–10 s, exponential taper centred at 1 s.
  • Decay (D) — time from peak to sustain level. Same taper.
  • Sustain (S) — held level (0–1, linear) until gate-off.
  • Release (R) — time from gate-off to silence. Same exponential taper.

Shape

The Shape parameter picks between linear and exponential segments:

  • Linear — each stage interpolates straight from start value to end. Constant rate of change.
  • Exponential — each stage curves. Decay and Release approach their target asymptotically. Attack overshoots — it aims at a target of 1.2 and clamps at 1.0, giving the characteristic “snappy” front edge of an analog amp envelope. Smoother on the ear than linear, especially for amplitude.

Env 1 defaults to Linear (predictable for modulation). Env 2 defaults to Exponential (smoother for amplitude).

Amp Gate

The Amp Gate parameter (0/1) bypasses Env 2 at the VCA. With Amp Gate on, the amp envelope is replaced by a hard gate that follows note-on / note-off — useful for organ-like sounds or when you want to drive amplitude entirely from an LFO-to-amp modulation rather than the envelope.

Tremolo

Independent of Env 2, the Amp LFO route applies a tremolo to the VCA stage:

  • Amp LFO — source selector (Off / LFO 1 / LFO 2).
  • Amp LFO Dep — tremolo depth (0–1).

This route is additive on top of Env 2 (or the Amp Gate, if active).

Parameters — Env 1

ParameterRangeDefaultUnitNotes
Env 1 Attack0.001–100.005sExp taper
Env 1 Decay0.001–100.3sExp taper
Env 1 Sustain0–10linear
Env 1 Release0.001–100.3sExp taper
Env 1 ShapeLinear / ExpLinearenum

Parameters — Env 2

ParameterRangeDefaultUnitNotes
Env 2 Attack0.001–100.005s
Env 2 Decay0.001–100.2s
Env 2 Sustain0–10.8linear
Env 2 Release0.001–100.3s
Env 2 ShapeLinear / ExpExpenum
Amp GateOff / OnOffboolBypass Env 2 at VCA
Amp LFOOff / LFO 1 / LFO 2OffenumTremolo source
Amp LFO Dep0–10linearTremolo depth

LFOs

VXN1 carries two LFOs with deliberately different scopes:

  • LFO 1 is per-voice. Each voice owns a phase; default behaviour retriggers on note-on. Has delay / fade-in for ease-in vibrato.
  • LFO 2 is global. One phase shared across every voice in both layers. Useful when you want the whole instrument moving together — wobble basses, dual-osc detune drift, synchronised filter cycling.

Waveforms

Both LFOs share the same six shapes:

ValueShape
0Sine
1Triangle
2Saw+ (rising)
3Saw− (falling)
4Square
5Sample & Hold

Rate and sync

Rate (0.01–40 Hz, exponential taper centred at 5 Hz) is free-running by default.

Sync locks the LFO rate to host tempo. With sync on, the rate knob steps through beat subdivisions instead of Hertz (1/1, 1/2, 1/4, 1/8, 1/16, 1/32, 1/8T, 1/16T, …). The control-rate update cadence is unchanged.

LFO 1 specifics

Free-Run (off by default) controls phase retriggering:

  • Off — phase resets to 0 on every note-on. Predictable vibrato that always starts from zero.
  • On — phase runs continuously across notes. Better for chord-spanning LFO sweeps where you don’t want every voice’s phase coupled to its note-on time.

Delay Time (0–4 s) sets a hold before the LFO becomes audible after note-on. Useful for slow-onset vibrato that only kicks in once the note has been held.

Fade (0–4 s) is the ramp-up time once the delay expires — the LFO’s output amplitude crossfades from 0 to its target over this interval. Combined with Delay Time, this gives the classic “vibrato that grows” voice mannerism.

Delay and Fade are clocked from note-on. With Free-Run on they have no per-note anchor and are effectively bypassed for held notes — use Free-Run off if you want the delay/fade behaviour to be predictable.

LFO 2 specifics

LFO 2 has just Shape / Rate / Sync. There’s no delay or fade because the global LFO has no per-note event to trigger from. (It also has no Free-Run option because it’s always free-running — that’s what global means.)

Where the LFOs go

Both LFOs are sources on every modulation panel:

  • Pitch — vibrato (±12 st).
  • PWM — pulse-width wobble.
  • Filter Cutoff — filter sweeps. (LFO 1 and LFO 2 each have their own fixed depth knob.)
  • Amp — tremolo.

Parameters — LFO 1 (per-layer)

ParameterRangeDefaultUnitNotes
LFO 1 ShapeSine / Tri / Saw+ / Saw− / Sq / S&HSineenum
LFO 1 Rate0.01–405.0HzExp taper
LFO 1 SyncOff / OnOffboolHost-tempo sync
LFO 1 Delay0–40sPre-fade hold
LFO 1 Fade0–40sFade-in ramp
LFO 1 FreeOff / OnOffbool1 = free-running, 0 = retrigger on note-on

Parameters — LFO 2 (global)

ParameterRangeDefaultUnitNotes
LFO 2 ShapeSine / Tri / Saw+ / Saw− / Sq / S&HSineenum
LFO 2 Rate0.01–405.0HzExp taper
LFO 2 SyncOff / OnOffbool

Modulation routes

Modulation in VXN1 is organised by destination: each routable destination is its own panel with source selectors and depth knobs. There is no matrix. Every route on this page is always live — no routing menus, no slots to assign.

Pitch modulation

Routes to both oscillators by default (vibrato, pitch envelope, pitch wheel). The two Mod switches turn either route into a single-oscillator route to drive the cross-mod modulator without moving the carrier — which is how VXN1 implements wide cross-mod sweeps.

  • Pitch LFO — source: Off / LFO 1 / LFO 2.
  • Pitch LFO Dep (0–12 st, exp taper) — vibrato depth.
  • Pitch LFO Mod — when on, the LFO contribution is routed only to the cross-mod modulator oscillator. Under Sync, that’s Osc 1 (the slave). Under PM / Ring / Off, that’s Osc 2 (whose output modulates Osc 1). When off, the LFO route moves both oscillators together (normal vibrato).
  • Pitch Env — source: Off / Env 1 / Env 2.
  • Pitch Env Dep (−12 to +12 st) — envelope-driven pitch sweep. Negative inverts.
  • Pitch Env Mod — same single-oscillator routing as the LFO Mod switch. With Mod on and a wide envelope depth (use the ±12 st range), this is the standard way to drive a classic sync sweep or a swept PM index.
  • Pitch Wheel (0–12 st) — pitch-bend range from MIDI pitch wheel. Always applies to both oscillators.

The Mod switches and the Cross-Mod Sweep mod-wheel route below are the only routes that target one oscillator without the other. All other pitch routing moves both osc together.

PWM modulation

Routes to Osc 1 and Osc 2 pulse widths simultaneously (so PWM only affects oscillators currently set to Pulse).

  • PWM LFO — source: Off / LFO 1 / LFO 2.
  • PWM LFO Dep (0 to 0.5) — sweep depth (0.25 = full ±25% PW excursion).
  • PWM Env — source: Off / Env 1 / Env 2.
  • PWM Env Dep (−0.5 to +0.5) — envelope depth, can invert.

Static PW from each oscillator’s PW knob is the centre point; modulation displaces from there.

Filter modulation

Four fixed depths, no source selectors. Every depth knob is live simultaneously; set unused routes to zero. The envelope route is hardwired to Env 1.

  • Cutoff LFO1 Dep (0–48 st) — LFO 1 → cutoff.
  • Cutoff LFO2 Dep (0–48 st) — LFO 2 → cutoff.
  • Cutoff Env Dep (−96 to +96 st) — Env 1 → cutoff (source is fixed; not selectable). Negative closes the filter on note-on.
  • Vel→Cutoff (−96 to +96 st) — MIDI velocity → cutoff.

The 96-semitone range gives enough headroom to fully sweep from minimum to maximum cutoff via velocity alone — useful for dynamic playing where soft notes are dark and hard notes are bright.

Key tracking is a separate continuous depth on the Filter panel, not a modulation depth on this panel.

Cross-Mod Sweep (mod-wheel)

VXN1 has no dedicated cross-mod sweep envelope route. Instead:

  • Envelope-driven sweeps are built by enabling the Mod switch on the Pitch Env route (above). With Mod on, the pitch envelope drives only the cross-mod modulator (Osc 1 in Sync mode; Osc 2 in PM / Ring / Off). Use the full ±12 st depth for dramatic sweeps; combine with Osc 2 detune to shift the sweep range.
  • Wheel-driven sweeps are the Wheel→X-Mod knob in the Mod Wheel panel below, which gives ±48 st of wide pitch range. Unlike the Mod-switched routes, the wheel route is always both-osc — under Sync and Ring it shifts both oscillators in parallel; under PM the modulator’s pitch dominates the audible result.

The wheel route is not gated by Cross-Mod Type — turning Cross-Mod Type to Off doesn’t disable it. With Off or Ring it acts as a wide pitch joystick.

Mod Wheel routes

The Mod Wheel (MIDI CC1) has four fixed destinations. Set any to zero to disable that route.

  • Wheel→PWM (−0.5 to +0.5) — pulse-width depth.
  • Wheel→Cutoff (−96 to +96 st) — cutoff depth.
  • Wheel→Reso (0–1) — adds to resonance.
  • Wheel→X-Mod (−48 to +48 st) — wide pitch route; used as the cross-mod sweep when Cross-Mod Type is Sync or PM.

The Mod Wheel CC is smoothed with a 40 ms time constant at control rate, so jitter from cheap controllers won’t transmit into the audio.

Parameter summary

ParameterRangeDefaultUnit
Pitch LFOOff / LFO 1 / LFO 2LFO 1enum
Pitch LFO Dep0–120.05st
Pitch LFO ModOff / OnOffbool
Pitch EnvOff / Env 1 / Env 2Offenum
Pitch Env Dep−12 to +120st
Pitch Env ModOff / OnOffbool
Pitch Wheel0–122.0st
PWM LFOOff / LFO 1 / LFO 2Offenum
PWM LFO Dep0–0.50linear
PWM EnvOff / Env 1 / Env 2Offenum
PWM Env Dep−0.5 to +0.50linear
Cutoff LFO1 Dep0–480st
Cutoff LFO2 Dep0–480st
Cutoff Env Dep−96 to +960st
Vel→Cutoff−96 to +960st
Wheel→PWM−0.5 to +0.50linear
Wheel→Cutoff−96 to +960st
Wheel→Reso0–10linear
Wheel→X-Mod−48 to +480st

Voice & assign

This panel controls how MIDI notes map to voices within a single layer. Layer-level decisions about which layer receives MIDI live on the Key Modes page.

Each layer has 8 channels. Assign Mode picks how those channels are spent.

Assign Mode

ModeChannels per notePolyphonyNotes
Poly18 (per layer)First-free voice, oldest-steal when full. Standard polyphonic behaviour.
Unison81All 8 channels stack on every note. Per-channel detune (Unison Detune) and phase decorrelation. Mono — one note at a time. Level-compensated by 1/√8.
Solo11One channel, last-note priority. With Legato on, the envelope doesn’t retrigger when you slur.
Twin24Two channels per note at ±Unison Detune and a 90° phase offset; 1/√2 level compensation. Effectively halves polyphony to thicken each note.

In Whole key mode, Poly spreads notes across both layers (16-voice round-robin) and Twin doubles to 8-note. Unison and Solo are inherently mono and run on the Upper layer only — the second layer’s channels are idle. In Dual and Split, each layer is independent: you can have Upper in Solo for the melody and Lower in Poly for the chord, for instance.

Glide (Portamento)

Glide Time (0–0.5 s, exp taper) sets the per-voice pitch slide time. It applies in all assign modes — Solo, Twin, Unison, even Poly (where each new voice glides from where its channel last left off).

Legato only changes behaviour in Solo mode:

  • Legato Off — every new note retriggers the gate (and so retriggers Env 1 / Env 2 and LFO 1 unless Free-Run is on).
  • Legato On — overlapping new notes glide but don’t retrigger. Use this for expressive lead lines where you want continuous envelope decay through a phrase.

In Poly mode, Legato is silently ignored (every new note takes a fresh channel; there’s nothing to “slur into”).

Unison Detune

Unison Detune (0–50 ct) is the per-channel detune spread for Unison and Twin modes. In Unison, all 8 channels are spread across the ±Detune range. In Twin, the two channels sit at +Detune and −Detune exactly.

A small value (5–10 ct) gives subtle thickening; larger values (25–40 ct) move into chorus / ensemble territory.

Layer level and Spread

These two parameters live on the same panel but are independent of assign mode:

  • Layer Level (0–1, default 1.0) — per-layer gain applied after rendering, before effects. Used to balance Upper and Lower in Dual mode.
  • Spread (0–1, default 0) — pans voice slots across the stereo field. At 0, all voices are centred. At 1, voices fan out into a wide stereo image. The spread is per-voice-slot, not per-note, so a single note hits the same pan position across plays.

Parameters

ParameterRangeDefaultUnitNotes
AssignPoly / Unison / Solo / TwinPolyenum
LegatoOff / OnOffboolSolo mode only
Unison Detune0–5012ctPer-channel detune spread
Glide Time0–0.50sExp taper (mid 0.1)
Layer Level0–11.0linearPer-layer gain
Spread0–10linearStereo voice spread

Effects

Four global effects sit between the voice mix and the master stage, in this order:

voice mix ─► Phaser ─► Chorus ─► Delay ─► Reverb ─► master

All four are global (shared by both layers) and each can be turned on or off independently.

Phaser

Pre-chorus phaser. Four cascaded all-pass stages with an LFO sweeping the centre frequency.

ParameterRangeDefaultUnitNotes
PhaserOff / OnOffbool
Phaser Rate0.05–100.5HzLFO rate, exp taper (mid 1 Hz)
Phaser Depth0–10.7linearSweep range
Phaser FB−0.9 to +0.90linearFeedback (negative inverts)
Phaser Mix0–10.5linearDry/wet
Phaser Stereo0–180180°L/R sweep offset — 180° anti-phase (widest), 0° both channels in lockstep

Chorus

Vintage BBD (bucket-brigade) chorus, “Bright” voicing modelled on the Juno-60. Includes bucket-write saturation, reconstruction filter at 9 kHz, and post-BBD makeup gain.

The right channel reads the inverted LFO phase — authentic mono-compatible stereo, not two offset LFOs.

ParameterRangeDefaultUnitNotes
ChorusOff / OnOnboolDefault on
Chorus Rate0.05–80.6HzBBD LFO rate
Chorus Depth0–10.5linearDelay swing amount
Chorus Mix0–10.4linearDry/wet

Delay range: 1.66–5.35 ms, swept by a strict-triangle LFO.

Delay

Stereo delay with feedback.

ParameterRangeDefaultUnitNotes
DelayOff / OnOffbool
Delay Time0.01–20.35sLinear taper
Delay FB0–0.950.4linearFeedback amount
Delay Mix0–10.25linearDry/wet
Delay SyncOff / OnOffboolHost-tempo sync (planned; not yet routed)
Ping-PongOff / OnOnboolFeedback crossfeeds L↔R; off keeps each side’s feedback on its own channel

Reverb

Feedback delay network (FDN) reverb. Size controls the FDN’s perceived room dimensions; damp absorbs high frequencies on each feedback pass.

ParameterRangeDefaultUnitNotes
ReverbOff / OnOffbool
Reverb Size0–10.5linearRoom size
Reverb Decay0.2–102.5sDecay time, exp taper (mid 2.0)
Reverb Damp0–10.4linearHigh-freq damping
Reverb Mix0–10.3linearDry/wet

Bypass behaviour

Each effect’s on/off toggle hard-bypasses that stage. Bypassed stages have no CPU cost — the audio buffer passes through unaltered. There is no smoothing on the bypass toggle, so automating it mid-note will click — use the Mix knob for click-free wet/dry changes.

Master

Global, instrument-wide controls. All parameters here are shared across both layers.

Tuning and level

  • Master Tune (−12 to +12 st) — instrument-wide pitch transposition. Applies after per-layer tuning, including pitch modulation.
  • Master Volume (0–1, default 0.7) — final output gain, per-sample smoothed to avoid zipper noise.

Drift

Master Drift (0–1, default 0) drives a slow random walk on every active oscillator’s tuning, modelling analogue tuning instability. At 0 every voice tracks its nominal pitch exactly; at 1 voices wander noticeably, broadening the stereo image and softening transients.

The drift is continuous, not a per-note snapshot — pitch keeps wandering for the whole lifetime of a held note. Two repeats of the same note will sound slightly different from each other and may not stay in tune with each other for long sustains.

Limiter

Limiter (Off / On, default Off) inserts a brick-wall limiter at the master output. Useful as a final safety net for heavy patches; in normal use, leave it off and manage your master level with the Master Volume knob.

The limiter is post-volume, so cranking Master Volume into the limiter is a valid way to push hot levels with a hard ceiling.

Oversampling

Oversample (Off / 2× / 4× / 8×, default 2×) sets the synthesis oversampling factor. The per-voice path (oscillators, sub, noise, cross-mod, filter, drive saturation) runs at the oversampled rate; effects run at host rate.

ModeWhen to use
Off (1×)CPU constrained; willing to live with aliasing on sync / ring / aggressive filter sweeps.
Default. Adequate for most patches.
Bright sync leads, ring modulation with non-sine sources, heavy resonance.
Worst-case anti-aliasing; clean PM with non-sine modulators. CPU-heavy.

Changing the oversample setting at runtime is real-time safe (no allocations, no reload), but the decimator FIR state is reset on the switch — expect a brief dropout on any sounding note. Make the change between notes if you can.

Parameters

ParameterRangeDefaultUnitNotes
Master Tune−12 to +120st
Master Volume0–10.7linearPer-sample smoothed
Master Drift0–10linearPer-voice phase offset amount
LimiterOff / OnOffboolBrick-wall limiter on master bus
OversampleOff / 2× / 4× / 8×enumSynthesis oversampling factor

Key modes

The Key Mode decides what MIDI notes do at the instrument level — before they reach any individual voice. VXN1 always carries two layers (Upper and Lower); the key mode is the routing rule that maps incoming notes to those layers.

There are three modes. Key Mode is not automatable — it’s stored as plugin state but cannot be moved with DAW automation lanes (ADR 0003).

Whole

16-voice mono-timbral. Both layers play the same patch.

  • Every note round-robins through both layers’ 8 channels — effective polyphony is 16.
  • Layer A holds the patch values. Layer B silently follows Layer A.
  • The Layer switcher is fixed to Upper; Lower is not editable.

Use Whole for normal full-polyphony performance with a single patch.

Dual

8 + 8 layered stereo. Both layers play the same notes simultaneously but with different patches.

  • Every note triggers a voice on each layer.
  • Layer A reads Upper params; Layer B reads Lower params.
  • Both layers are editable via the Layer switcher.
  • Use Layer Level on each layer’s Voice & assign panel to balance.

Classic uses: detuned doubles (slight tuning offset between layers), pad + bell layering, dark filter sound under a bright transient.

Split

8 + 8 split at a MIDI note. Each layer plays only certain notes.

  • Notes below the split point trigger Lower; notes at-or-above trigger Upper.
  • Both layers are independently editable.
  • The split point is a separate plugin-state field (not a parameter), set via the Split Point control in the Key Mode panel. Default: MIDI note 60 (C4).
  • Split point is non-automatable (ADR 0003).

Use Split for bass-and-lead splits, layered keyboard performances, or any time the left hand needs a different patch from the right.

Seed-on-entry

When you switch from Whole to Dual or Split, Layer B (Lower) is empty — it’s been silently following Upper. The engine copies Upper → Lower on the transition so both layers start with the same patch, then they diverge as you edit Lower.

Switching from Dual or Split to Whole drops Lower silently — no merge, no destructive prompt. Switching back to Dual/Split restores the seed-on-entry copy from Upper, not the previous Lower state. If you have a Lower patch you want to keep, save it as a Patch preset before switching to Whole.

What’s per-layer vs. global

The complete list of what each layer holds independently:

  • Both oscillators (waves, tuning, levels, PW).
  • Sub level, noise level + colour.
  • Cross-Mod Type and Amount.
  • Filter (HPF, VCF, mode, slope, key track).
  • Both envelopes.
  • LFO 1 (per-voice, layer-scoped).
  • All modulation routes (pitch, PWM, filter mod, cross-mod sweep, mod-wheel routes).
  • Amp Gate, Amp LFO route.
  • Voice & assign panel (Assign Mode, Legato, Unison Detune, Glide Time, Layer Level, Spread).

What’s global (shared by both layers, single value):

  • Master Tune, Master Volume, Master Drift, Limiter, Oversample.
  • LFO 2 (Shape, Rate, Sync).
  • All effects: Phaser, Chorus, Delay, Reverb.
  • Key Mode itself and the Split Point.
  • Performance control values (mod wheel position, pitch wheel position) — the routings of those controllers are per-layer.

Performance controls and key modes

MIDI controllers (mod wheel, pitch wheel, sustain, velocity) reach both layers in all modes. Each layer interprets them according to its own routing — so in Dual mode, Upper might use the mod wheel for filter cutoff while Lower uses it for PWM, both at the same time.

Presets

VXN1 has two kinds of preset:

  • Patch — one layer’s state. Loads into either the Upper or Lower layer; doesn’t touch the other layer, global, or key mode.
  • Performance — the full instrument state: both layers, global parameters, key mode, and split point. Equivalent to capturing the whole plugin.

Both kinds share a TOML file format. Factory presets ship inside the plugin binary; user presets live on disk and are fully editable.

File format

Plain TOML. Keys are parameter names (matching the labels in this manual), not numeric IDs — so files survive parameter reordering. Values are in display units (Hz, seconds, semitones, etc.). Enums are stored by label, case-insensitive.

The format is sparse: only parameters that differ from the descriptor default are written. Forward-compatible: unknown keys are silently skipped, missing keys fall back to defaults.

schema = 1
kind = "patch"

[meta]
name = "Brass Ensemble"
author = "Vulpus Labs"
category = "Brass"

[patch]
osc1_wave = "Saw"
osc2_octave = -1
cutoff = 4200.0
resonance = 0.35
env1_attack = 0.05
env2_sustain = 0.9
chorus_mix = 0.6

The [meta] table currently carries name, author, and category. Free-text tags were specced in early drafts but dropped from the format — there is no tags field on disk.

For Performances, the structure splits across multiple tables:

schema = 1
kind = "performance"

[meta]
name = "Split Bass + Lead"
author = "Vulpus Labs"

[performance.upper]
# Upper layer params

[performance.lower]
# Lower layer params

[performance.global]
master_volume = 0.65
chorus = true
delay_mix = 0.3
key_mode = "Split"
split_point = 60

Storage

OSLocation
macOS~/Library/Audio/Presets/Vulpus Labs/VXN1/
Windows%APPDATA%\Vulpus Labs\VXN1\Presets
Linux$XDG_DATA_HOME/VXN1/presets (fallback: ~/.local/share/VXN1/presets)

Inside the preset root, folder structure is flat one level deep — categories are subdirectories, presets are files in those directories. No nested categories.

Factory presets are embedded in the binary at compile time via include_dir! and are read-only at runtime.

Factory bank

Seven categories ship in the factory bank:

  • Bass
  • Brass
  • Keys
  • Lead
  • Pad
  • Performance
  • Strings

The Performance category specifically holds full-instrument states (Dual layered patches, Split keyboards) — patches with kind = "performance".

Browser

The preset browser opens from the Browse button on the preset bar at the top of the faceplate. It shows:

  • Folder tree on the left (categories + your subfolders).
  • Preset list on the right.
  • Search box above the list — substring match on preset name.
  • Context menu on each preset: Rename, Delete, Move to ▸.
  • Drag-drop — drag a user-preset row onto a user folder in the left pane to move it into that folder. Factory folders are not drop targets; the source folder is rejected too. The context menu’s “Move to ▸” is the fallback.

Factory presets have read-only flags — you can’t rename or delete them, but you can save a modified copy via Save As.

Load semantics

  • Loading a Patch writes to one layer (the Layer switcher chooses Upper or Lower). Global, key mode, split point, and the other layer are untouched.
  • Loading a Performance replaces everything. The plugin announces the change to the host so DAW automation lanes refresh.

There’s no “load undo” — load is destructive of the previously edited state. Save your in-progress patch before loading a new one if you want to come back to it.

Save form

The Save As dialog asks for:

  • Name — required. Used as the filename (with extension .toml).
  • Author — optional, persists to [meta] author.
  • Category — picks the folder. Pre-filled with the current category or empty for a new one.

Saves to user storage only; factory presets are read-only.

MIDI & automation

VXN1 takes standard MIDI and CLAP / VST3 parameter automation. This page covers what each input type drives.

MIDI notes

  • Note On / Note Off — drive the voice allocator according to the layer’s Assign Mode and the instrument’s Key Mode.
  • Velocity — routed by the Vel→Cutoff depth on the filter modulation panel. Velocity is not hardwired to amplitude; if you want velocity-to-volume, route an envelope through it.

MIDI controllers

MIDI inputDefault routingDestination panel
Pitch Wheel±2 st on both oscPitch modulationPitch Wheel knob (0–12 st)
Mod Wheel (CC1)All four routes default 0Mod Wheel routes — Wheel→PWM, Wheel→Cutoff, Wheel→Reso, Wheel→X-Mod Sweep
Channel Aftertouch / Poly AftertouchNot routedReserved for future revisions
Sustain (CC64)Standard sustain (holds gate after note-off)All assign modes

The Mod Wheel is smoothed with a 40 ms time constant at control rate to filter controller jitter.

Tempo sync

Three parameters honour host tempo when their Sync toggle is on:

  • LFO 1 Sync
  • LFO 2 Sync
  • Delay Sync

With sync on, the rate knob steps through beat subdivisions (1/1 down to 1/32, including triplet and dotted variants). With sync off, the rate is free-running in Hz.

Parameter automation

All 165 parameters are exposed to CLAP automation (and to VST3 once the wrapper build lands; see Distribution). The full list lives in the Parameter reference. A few notes on automation behaviour:

  • Per-layer parameters are exposed twice — once for Upper, once for Lower. Their CLAP IDs are derived from a per-layer offset.
  • Key Mode and Split Point are not automatable. They live in plugin state and can be saved / loaded with the project but can’t be moved by an automation lane (ADR 0003).
  • Performance parameters (mod wheel routings, pitch wheel range) are automatable but tend to be set-and-forget — they’re per-layer state that you’d normally save in a preset.
  • Smoothing: every continuous parameter is smoothed at control rate (typically 32-sample blocks). Step-change automation won’t produce zipper artefacts.

VST3 IDs

VST3 parameter IDs are derived from CLAP IDs by hashing. This means renaming a CLAP parameter ID will break VST3 automation in saved projects. Identifier stability is a soft policy from ADR 0008 — VXN1 won’t rename IDs post-ship except in release notes. If you write DAW automation against VST3, the IDs are stable across patch versions.

Patch reload after preset change

When a preset loads via the browser, the plugin posts the new parameter values to the host so automation lanes refresh. This works in CLAP. In VST3, host behaviour varies — some hosts re-read parameter values on a plugin request, others only on user interaction. Test in your specific host.

Parameter reference

The complete VXN1 parameter table, grouped by panel. 165 parameters total = 2 × 69 per-layer + 27 global.

Per-layer parameters are exposed twice to the host — once for Upper, once for Lower — and described once below.

Oscillator 1

ParameterRangeDefaultUnit
Osc 1 WaveSine / Tri / Saw / PulseSawenum
Osc 1 Octave−4 to +40oct
Osc 1 Coarse−7 to +70st
Osc 1 Fine−50 to +500ct
Osc 1 Level0–10.8linear
Osc 1 PW0.05–0.950.5duty

Oscillator 2 & Cross-Mod

ParameterRangeDefaultUnit
Osc 2 WaveSine / Tri / Saw / PulseSawenum
Osc 2 Octave−4 to +4−1oct
Osc 2 Coarse−7 to +70st
Osc 2 Fine−50 to +500ct
Osc 2 Level0–10.6linear
Osc 2 PW0.05–0.950.5duty
Cross-Mod TypeOff / Sync / FM / RingOffenum
Cross-Mod Amount0–40linear

Mixer

ParameterRangeDefaultUnit
Sub Level0–10linear
Noise Level0–10linear
Noise ColourWhite / PinkWhiteenum

Filter

ParameterRangeDefaultUnit
HPF Cutoff20–1800020Hz
Cutoff16.35–160001000Hz
Resonance0–10.2linear
Drive0.1–41.0linear
Filter ModeLP / HP / BP / NotchLPenum
Filter Slope12 dB / 24 dB24 dBenum
Key Track0–10linear
TunedOff / OnOffbool

Envelope 1 (Modulation)

ParameterRangeDefaultUnit
Env 1 Attack0.001–100.005s
Env 1 Decay0.001–100.3s
Env 1 Sustain0–10linear
Env 1 Release0.001–100.3s
Env 1 ShapeLinear / ExpLinearenum

Envelope 2 (Amplitude) + VCA

ParameterRangeDefaultUnit
Env 2 Attack0.001–100.005s
Env 2 Decay0.001–100.2s
Env 2 Sustain0–10.8linear
Env 2 Release0.001–100.3s
Env 2 ShapeLinear / ExpExpenum
Amp GateOff / OnOffbool
Amp LFOOff / LFO 1 / LFO 2Offenum
Amp LFO Dep0–10linear

LFO 1 (per-voice, per-layer)

ParameterRangeDefaultUnit
LFO 1 ShapeSine / Tri / Saw+ / Saw− / Sq / S&HSineenum
LFO 1 Rate0.01–405.0Hz
LFO 1 SyncOff / OnOffbool
LFO 1 Delay0–40s
LFO 1 Fade0–40s
LFO 1 FreeOff / OnOffbool

Pitch modulation

ParameterRangeDefaultUnit
Pitch LFOOff / LFO 1 / LFO 2LFO 1enum
Pitch LFO Dep0–120.05st
Pitch LFO ModOff / OnOffbool
Pitch EnvOff / Env 1 / Env 2Offenum
Pitch Env Dep−12 to +120st
Pitch Env ModOff / OnOffbool
Pitch Wheel0–122.0st

PWM modulation

ParameterRangeDefaultUnit
PWM LFOOff / LFO 1 / LFO 2Offenum
PWM LFO Dep0–0.50linear
PWM EnvOff / Env 1 / Env 2Offenum
PWM Env Dep−0.5 to +0.50linear

Filter modulation

ParameterRangeDefaultUnit
Cutoff LFO1 Dep0–480st
Cutoff LFO2 Dep0–480st
Cutoff Env Dep−96 to +960st
Vel→Cutoff−96 to +960st

Mod Wheel routes

ParameterRangeDefaultUnit
Wheel→PWM−0.5 to +0.50linear
Wheel→Cutoff−96 to +960st
Wheel→Reso0–10linear
Wheel→X-Mod−48 to +480st

Wide cross-mod sweeps are built from the Pitch Env Mod switch plus Pitch Env Dep — there is no dedicated sweep envelope route in the parameter table.

Voice & assign

ParameterRangeDefaultUnit
AssignPoly / Unison / Solo / TwinPolyenum
LegatoOff / OnOffbool
Unison Detune0–5012ct
Glide Time0–0.50s
Layer Level0–11.0linear
Spread0–10linear

Global — Master

ParameterRangeDefaultUnit
Master Tune−12 to +120st
Master Volume0–10.7linear
Master Drift0–10linear
LimiterOff / OnOffbool
OversampleOff / 2× / 4× / 8×enum

Global — LFO 2

ParameterRangeDefaultUnit
LFO 2 ShapeSine / Tri / Saw+ / Saw− / Sq / S&HSineenum
LFO 2 Rate0.01–405.0Hz
LFO 2 SyncOff / OnOffbool

Global — Phaser

ParameterRangeDefaultUnit
PhaserOff / OnOffbool
Phaser Rate0.05–100.5Hz
Phaser Depth0–10.7linear
Phaser FB−0.9 to +0.90linear
Phaser Mix0–10.5linear
Phaser Stereo0–180180°

Global — Chorus

ParameterRangeDefaultUnit
ChorusOff / OnOnbool
Chorus Rate0.05–80.6Hz
Chorus Depth0–10.5linear
Chorus Mix0–10.4linear

Global — Delay

ParameterRangeDefaultUnit
DelayOff / OnOffbool
Delay Time0.01–20.35s
Delay FB0–0.950.4linear
Delay Mix0–10.25linear
Delay SyncOff / OnOffbool
Ping-PongOff / OnOnbool

Global — Reverb

ParameterRangeDefaultUnit
ReverbOff / OnOffbool
Reverb Size0–10.5linear
Reverb Decay0.2–102.5s
Reverb Damp0–10.4linear
Reverb Mix0–10.3linear

Non-parameter state

These live in plugin state (PluginState) but are not host-automatable:

  • Key Mode — Whole / Dual / Split
  • Split Point — MIDI note (default 60)
  • Layer Switcher — UI selection (Upper / Lower); persists across project reopens

Architecture

VXN1 lives inside a Cargo workspace shared with VXN2. The product-specific crates are vxn-dsp, vxn-engine, vxn-app, vxn-clap, and vxn-ui-web; cross-product reusable types (Controller event enums, preset-IO, host-event plumbing) live in vxn-core-app at the workspace root. Layering is strictly downward — vxn-clapvxn-appvxn-enginevxn-dsp. UI is a separate axis (vxn-ui-web).

              ┌───────────────┐
              │   vxn-clap    │  CLAP cdylib entry point (clack)
              └──────┬────────┘
                     │
   ┌─────────────┐   │   ┌────────────────┐
   │ vxn-ui-web  │◀──┴──▶│    vxn-app     │  Controller (MVC arbiter)
   └─────────────┘       └──────┬─────────┘
                                │
                       ┌────────▼─────────┐
                       │    vxn-engine    │  Param model, voice alloc, render loop
                       └────────┬─────────┘
                                │
                       ┌────────▼─────────┐
                       │     vxn-dsp      │  Framework-free DSP kernels
                       └──────────────────┘

Crate roles

CrateRole
vxn-dspFramework-free, allocation-free DSP kernels: oscillators, filters, ADSR, LFO shapes, ring-mod / PM / sync, BBD chorus, FDN reverb, phaser, delay. No std::sync, no globals. Unit-tested with golden buffers.
vxn-engineParameter table (SharedParams), voice allocator, key-mode router, modulation calculation, and the block-rate render loop. Owns one rendered voice per channel and the global FX rack. Calls into vxn-dsp per sample.
vxn-appThe VXN1 Controller. Holds non-automatable state (key mode, split point), defines VXN1-specific custom events (Vxn1UiCustom, Vxn1ViewCustom), and re-exports the shared UiEvent / HostEvent / ViewEvent enums from vxn-core-app.
vxn-core-appWorkspace-shared controller substrate: generic UiEvent / HostEvent / ViewEvent types, preset-IO scaffolding, MIDI/automation plumbing. Reused by VXN2.
vxn-ui-webThe View. wry-WebView host for the HTML/CSS/JS faceplate (E010/E011). Pluggable — the controller doesn’t depend on a specific view implementation.
vxn-clapThe CLAP shell. clack cdylib that wires the audio thread to vxn-engine, the main thread to vxn-app, and the GUI extension to vxn-ui-web.
xtaskBuild/bundle helper. Drives cargo build, packages the CLAP bundle, optionally invokes clap-wrapper CMake for VST3.

Threading model

Three contexts, with strict rules about who writes what:

ThreadReadsWrites
Audio (RT)SharedParams atomicsVoice state (private); audio buffer
MainAnythingSharedParams (via Controller events); plugin state
GUIPosted ViewEventsUiEvents → Controller

The audio thread never blocks on the main thread. The main thread never directly mutates voice state — it goes through SharedParams atomics and lets the audio thread pick up the change on the next control block boundary.

See MVC layering for the event-channel topology.

Build configuration

VXN1 builds with stable Rust 1.85+ (edition 2024). Key build-time settings:

  • Optimisation: release profile with lto = "thin" and codegen-units = 1 for the cdylib crates.
  • Target features: +neon on aarch64-apple-darwin; +avx2 on x86_64 targets.
  • No-std subset: vxn-dsp is #![no_std]-compatible but enables std by default for f32 math helpers.

Workspace layout

vxn-1/vxn-1/
├── crates/
│   ├── vxn-app/        Controller
│   ├── vxn-clap/       CLAP shell
│   ├── vxn-dsp/        DSP kernels
│   ├── vxn-engine/     Param model + render loop
│   └── vxn-ui-web/     WebView GUI
├── xtask/              Bundler / build tooling
├── adrs/               Architecture decision records
├── epics/              Multi-ticket project epics
├── tickets/            Open and closed tickets
├── tests/              Integration tests
└── docs/               This manual

Future-proofing

The architecture is designed to allow a second instrument (VXN2) to reuse the Controller, shell integration, WebView embedding, and preset format. VXN2 defines its own Synth + parameter blocks (DX7-style 6-op FM); everything around the synth core is shared infrastructure (see ADR 0007 and the VXN2 ADRs).

Engine & DSP

The engine is the bridge between the parameter table and the audio buffer. Its job, per host buffer:

  1. Receive parameter snapshots from SharedParams.
  2. Receive note events from the host (translated via the key-mode router; 165 params total in SharedParams).
  3. Allocate / steal voice channels per assign mode.
  4. For each control block (32 samples), recompute modulation, envelope, LFO, filter coefficients.
  5. For each sample in the block, run per-voice DSP and mix into the output.
  6. Apply global FX, master volume, optional limiter.

Constants

ConstantValueNotes
CONTROL_BLOCK32 samplesControl-rate update cadence. At 48 kHz ≈ 0.67 ms.
Voices per layer8Static. Not reconfigurable.
Layers2 (Upper, Lower)Always allocated.
Total voices16All 16 channels active in Whole mode (round-robin).
MAX_OVERSAMPLE8Per-voice buffer sized to 8× host buffer.
Default oversampleReal-time switchable.
Mod-wheel smoothing40 msControl-rate one-pole on CC1.

Block structure

Each call to the host’s process callback:

loop {
    let frames_this_pass = min(host_frames_left, CONTROL_BLOCK);

    update_control_rate(snapshot, frames_this_pass);   // mod, env, LFO, filter coeffs
    for sample in 0..frames_this_pass {
        for voice in active_voices { voice.render_sample(); }
        mix_voices_into_block(buffer, sample);
    }
    apply_fx_block(buffer, frames_this_pass);

    host_frames_left -= frames_this_pass;
    if host_frames_left == 0 { break }
}

Modulation, envelope segments, LFO output, and filter cutoff are recomputed once per block. Per-sample work is the oscillator phase increment, sub/noise output, mix, filter recurrence, and VCA — all of which need per-sample precision to preserve the DSP recurrences’ transient response.

SoA voice layout

Each layer holds its 8 channels as a structure-of-arrays layout. Oscillator phase, mix levels, filter state, and envelope state live in 8-wide f32 arrays. The inner loop iterates over channels, which lets the compiler auto-vectorise to NEON on Apple Silicon (and AVX2 on Intel).

A few hot-path subtleties:

  • Runtime enum matches inside the SoA loop defeat vectorisation. Waveform selection and ladder-mix selection are hoisted out via type markers (WaveKind, LadderMix) — the loop sees a monomorphised constant, not a runtime match (see memory entry “VXN1 SoA match defeats SIMD” for the gotcha).
  • Silent voices skip the per-sample loop entirely at block granularity. The cost of an idle voice is one branch per block — measured at ~1100× real-time on M1.
  • Filter state is frozen during silent-skip. Coefficient ramps freeze too. This shows on attack of a high-resonance patch coming out of silence; the amp envelope masks staleness in practice.

DSP kernels

vxn-dsp is the kernel library. All kernels are framework-free (no global state, no std::sync, no allocator after construction) and take an &mut self + per-sample inputs.

KernelNotes
OscillatorspolyBLEP-band-limited saw / pulse. Sine and triangle are unaliased. PM / Sync / Ring routed through dedicated kernel variants for the SoA-friendly fast path.
SubSquare wave at Osc 1’s frequency / 2. Band-limited.
NoiseWhite: Xorshift32. Pink: 4-octave Voss-McCartney summing.
Ladder filterOTA-C transistor-ladder. tanh saturator at each integrator input (rational Padé(5,6) approximation from vxn-dsp::math), not in the feedback path. Per-block coefficient recompute, per-sample state advance. Mode (LP/HP/BP/Notch) is a const-selected tap.
HPF1-pole (6 dB/oct) topology-preserving high-pass.
EnvelopeADSR with linear or exponential segments. Branch-free per-sample step; segment transitions are one branch per gate event.
LFOSix shapes (sine, tri, saw+, saw−, sq, S&H). Phase accumulator with optional host-tempo sync.
Phaser4-stage all-pass with LFO-modulated centre.
ChorusBBD model with bucket saturation, reconstruction filter, inverted-LFO stereo.
DelayStereo delay line, one-pole high-frequency damping on the feedback path.
ReverbFDN, 8-channel, with damping on each loop.

Sample-rate handling

Sample rate is set once at plugin activation (CLAP clap_plugin.activate). On activation, the engine:

  1. Recomputes envelope time-to-coefficient constants.
  2. Recomputes LFO phase increments for all rates.
  3. Recomputes filter pre-warp constants.
  4. Reallocates the oversampled buffer for the per-voice render path.

Mid-stream sample-rate change is not supported (per CLAP spec, the host must deactivate / reactivate the plugin).

Performance ballpark

Recent measured numbers on Apple M1 (release LTO build):

  • Idle (no notes): ~1100× real-time.
  • Single-voice dry sound (no FX): ~51× real-time.
  • Single-voice sync sound (cross-mod active): ~41× real-time.
  • 16-voice full poly + chorus + delay + reverb at 4× oversample: comfortably real-time on M1; check your own CPU budget for heavy ensembles.

See vxn-1/crates/vxn-engine/benches/ for the busy_profile benchmark harness.

MVC layering

VXN1 uses an explicit Model / View / Controller split (ADR 0007). The Controller crate is vxn-app; it sits between the audio engine, the host, and the GUI, and mediates every non-audio mutation through structured event channels.

The goals:

  • The audio thread never blocks on the main thread.
  • The GUI is pluggable — swap Vizia for WebView (we did) without touching the engine.
  • The same Controller serves any instrument — VXN2 reuses it.

Roles

Model

The audio-readable state.

  • SharedParams — flat, index-addressed atomic table of every automatable parameter (165 entries for VXN1: 2 × 69 per-layer + 27 global). The audio thread reads atomics directly; the main thread mediates writes.
  • Non-automatable state — Key Mode, Split Point, Layer Switcher selection. Stored in plugin state but not exposed as parameters.

The audio thread has read-only access to non-automatable state via a copy passed at activation time (or via an atomic for fields that can change mid-run, like Key Mode).

Controller (vxn-app)

The mediator. Owns:

  • Event channels — bounded mpsc rings between (host, GUI) and the Controller; another from the Controller to the GUI.
  • Intent application — translates UiEvent / HostEvent into SharedParams writes and plugin-state mutations.
  • IO — preset load/save, file watcher for user preset directory.
  • View updates — emits ViewEvents when state changes that the GUI needs to know about (preset change, parameter change from host automation, key mode switch).

View (vxn-ui-web)

Stateless presentation layer (except widget tree).

  • Receives ViewEvents.
  • Emits UiEvents.
  • Pluggable — the Controller doesn’t depend on vxn-ui-web specifically. The current build uses wry-WebView with an HTML/CSS/JS faceplate.

Event types

GUI ──UiEvent──► Controller ◄──HostEvent── CLAP host
                     │
                     ▼
                ViewEvent ──► GUI
                     │
                     ▼
                SharedParams ──► Audio thread (reads atomically)

The base enums live in vxn-core-app::events; VXN1-specific payloads sit inside UiEvent::Custom(Box<Vxn1UiCustom>) and ViewEvent::Custom(Box<Vxn1ViewCustom>).

UiEvent

What the user did in the GUI:

  • SetParam { id, plain } / SetParamNorm { id, norm } — knob turned, value entered.
  • BeginGesture { id } / EndGesture { id } — host-visible automation gesture bounds.
  • LoadPreset { source } / StepPreset { delta } — preset navigation.
  • SavePreset { name, folder } / RenamePreset / DeletePreset / MovePreset.
  • NewFolder / RenameFolder / DeleteFolder — preset directory mutations.
  • EditorReady — GUI handshake.
  • RequestTextInput / TextInputResult — text-entry round-trip for save/rename dialogs.
  • Custom(Box<dyn Any + Send>) — VXN1 uses this to carry Vxn1UiCustom::SetKeyMode { mode }, SetSplitPoint { note }, SetEditLayer { layer }, ResetLayer { layer }.

HostEvent

What the host did:

  • ParamAutomation { id, plain }.
  • StateLoaded { blob } — project reload.
  • Tempo { bpm } — host tempo change.
  • Custom(...) — extensions; MIDI CCs are handled inside the engine, not surfaced here.

ViewEvent

What the GUI needs to redraw:

  • ParamChanged { id, plain, norm, display } — knob position + display string refresh.
  • PresetLoaded { ... } — repopulate display, refresh all knobs.
  • PresetCorpusChanged { follow } — file watcher detected user preset directory change.
  • Status { line } — status-bar / error toast text.
  • OpenTextInput / TextInputResult — text-entry dialog lifecycle.
  • Custom(...) — VXN1 receives Vxn1ViewCustom::KeyModeChanged { mode }, SplitPointChanged { note }, EditLayerChanged { layer }.

Bounded channels

Both directions use bounded mpsc rings:

  • Sender → Controller: bounded to ~128 events. Overflow drops the oldest non-critical event.
  • Controller → GUI: bounded to ~256 events; the GUI thread is expected to drain at vsync rate.

The audio thread is not on either of these channels — it reads SharedParams atomically and is decoupled from the Controller.

File IO

Preset load/save runs on the main thread under the Controller, never on the audio thread. The file watcher runs in a background thread that posts PresetListChanged events to the Controller.

When a preset loads:

  1. Controller parses the TOML.
  2. Controller writes each parameter through SharedParams::set.
  3. Controller posts ParamUpdated events to the GUI for each changed knob.
  4. Controller signals the CLAP host that parameter values have changed (so DAW automation lanes refresh).

Why this split

Two driving constraints made the matrix:

  1. Realtime safety — the audio thread can’t allocate, lock, or do file IO. Everything that does happens on the main thread, communicating through atomics and bounded channels.
  2. Reusability — the next instrument (VXN2) is a DX7-style 6-op FM synth. Different DSP, same Controller / shell / GUI substrate. Splitting the Controller into a generic crate (vxn-app) lets VXN2 reuse it verbatim.

ADR 0007 covers the design choices in more depth — particularly why the Controller is a separate crate rather than baked into vxn-engine.

CLAP & VST3 distribution

VXN1 currently ships CLAP only. VST3 via clap-wrapper is the committed distribution path (ADR 0008), but the wrapper integration has not yet landed in xtask and there is no vendor/clap-wrapper submodule in the workspace. Treat the VST3 section below as the target state, not the current build.

CLAP build

vxn-clap is a cdylib linking vxn-engine, vxn-app, and vxn-ui-web. Built via cargo build --release, the resulting libvxn_clap.{dylib,so,dll} is packaged into a VXN1.clap bundle by xtask.

Bundle structure (macOS):

VXN1.clap/
└── Contents/
    ├── Info.plist
    ├── MacOS/
    │   └── VXN1
    └── PkgInfo

Windows and Linux ship the cdylib as a flat VXN1.clap file with no bundle wrapper.

VST3 build (planned, not yet shipping)

The committed plan (ADR 0008) is for xtask to invoke a vendored clap-wrapper build via CMake, with vendor/clap-wrapper and vendor/vst3sdk (pinned to VST3 3.8 — MIT-licensed since October 2025) as workspace submodules. The build will produce a single-binary bundled VST3 with the CLAP cdylib statically linked.

Planned requirements:

  • CMake ≥ 3.21.
  • A C++17 compiler (clang, MSVC, GCC).

Planned xtask invocations (none of these flags are accepted today):

# VST3 only (planned)
cargo xtask bundle --release --format vst3

# Both formats in one shot (planned)
cargo xtask bundle --release --format clap,vst3

# macOS universal binary (planned)
cargo xtask bundle --release --universal --format vst3

For the actual shipping CLAP build, see Installing VXN1.

Install locations

OSCLAP (shipping)VST3 (planned)
macOS~/Library/Audio/Plug-Ins/CLAP/VXN1.clap~/Library/Audio/Plug-Ins/VST3/VXN1.vst3
Windows%LOCALAPPDATA%\Programs\Common\CLAP\VXN1.clap%LOCALAPPDATA%\Programs\Common\VST3\VXN1.vst3
Linux~/.clap/VXN1.clapnot planned for the first VST3 cut

Bundle identifier: labs.vulpus.vxn1.

Parameter identity

VST3 parameter IDs are derived from CLAP parameter IDs by hashing. The implication:

Renaming a CLAP parameter ID breaks VST3 automation in saved projects.

ADR 0008 commits to a soft stability policy: CLAP IDs won’t be renamed post-ship, except when called out in release notes. Pre-release, IDs are free to change.

If you’re writing host-side integrations against the VST3 binary, you can rely on stable hashed IDs from release to release.

Code signing

Pre-release builds are not signed. See Unsigned binaries for the macOS Gatekeeper workaround and Windows SmartScreen handling.

The release pipeline plan is:

  • macOS: codesign with a Developer ID Application certificate + notarisation via xcrun notarytool.
  • Windows: Authenticode signing of the .clap and .vst3.
  • Linux: no signing; SHA256SUMS in release artefacts.

Until release, expect to clear Gatekeeper quarantine by hand or build from source.

Plugin discovery

CLAP and VST3 both rely on filesystem scanning by the host. If VXN1 doesn’t appear after install:

  1. Verify the install path matches the OS table above.
  2. Confirm the host’s plugin search paths include the standard CLAP / VST3 directories (most hosts do this by default, but some Linux distros and locked-down corporate machines need explicit configuration).
  3. On macOS, check the quarantine flag is cleared (see Unsigned binaries).
  4. Force a plugin rescan in the host. Some hosts cache failure metadata and won’t retry until told to.

Glossary

ADSR — Attack / Decay / Sustain / Release. The four envelope stages.

Assign Mode — Per-layer policy for how voice channels are spent: Poly, Unison, Solo, Twin.

BBD (Bucket-Brigade Device) — Analogue delay line implemented as a charge-coupled shift register. VXN1’s chorus models a Juno-60-style BBD with bucket saturation and a reconstruction filter.

CLAPCLever Audio Plug-in API. The native plugin format VXN1 uses. MIT-licensed, modern, no vendor lock-in.

Channel — One slot in a layer’s 8-slot voice pool. Each layer has 8 channels; a voice (one “note’s worth of synthesis state”) occupies one or more channels depending on Assign Mode.

Control block / Control rate — 32-sample chunk inside which modulation, envelope, LFO, and filter coefficients are constant. ~1.5 kHz update rate at 48 kHz sample rate.

Cross-Mod — Umbrella term for the four oscillator-interaction modes: Off, Sync, FM (PM), Ring.

Detune — Tuning offset, typically in cents (ct).

Dual mode — Key mode in which both layers receive every note and play different patches simultaneously (8 + 8 layered stereo).

Env 1 / Env 2 — The modulation and amplitude envelopes, respectively. Env 2 is hardwired to the VCA; Env 1 is freely routable.

FDN (Feedback Delay Network) — Reverb topology built from a matrix of delay lines with feedback. VXN1’s reverb is an 8-channel FDN.

FM (in VXN1 UI) — Labelled “FM” on the cross-mod selector, but internally implemented as PM (phase modulation). Same family of timbres, but pitch stays stable when the modulator’s DC level moves.

Free-Run — LFO 1 setting that keeps the LFO phase continuous across note-ons (vs. retriggering on each note).

Glide / Portamento — Per-voice pitch slide between notes.

HPF (High-pass filter) — 1-pole pre-VCF filter on each voice’s mix.

Key Mode — Instrument-level routing: Whole / Dual / Split. Decides what notes go to which layer. Not automatable.

Key Track — Filter feature that ties cutoff to the played note (in VXN1: binary on/off, 100% per octave when on).

Layer — One of two complete patches (Upper / Lower). Each layer has 8 channels. Always-allocated regardless of Key Mode.

LFO 1 / LFO 2 — Low-frequency oscillators. LFO 1 is per-voice; LFO 2 is global.

Mod-wheel — MIDI CC1. VXN1 has four fixed Mod-wheel destinations: PWM, Cutoff, Reso, Cross-Mod Sweep.

OTA-C — Operational Transconductance Amplifier with Capacitor. The analog topology behind VXN1’s ladder filter (R3109 / IR3109 family).

Oversample — Synthesis runs at 1× / 2× / 4× / 8× the host sample rate to reduce aliasing. Per-voice path is oversampled; effects are not.

Patch — A preset that holds one layer’s state. Loads into Upper or Lower.

Performance — A preset that holds the full instrument state (both layers + global + Key Mode + Split Point).

PM (Phase Modulation) — One of the cross-mod modes; labelled “FM” on the UI.

polyBLEP — Band-limited step residual. Algorithm that anti-aliases saw/pulse/sync edges by subtracting a windowed step from the naive waveform.

Poly mode — Standard polyphonic Assign Mode: first-free voice, oldest-steal.

PW (Pulse Width) — Static duty cycle of the pulse waveform. Modulated by the PWM route.

Ring Modulation — Multiplication of two signals. VXN1’s ring uses a Parker diode-bridge model.

SharedParams — The atomic parameter table. The audio thread reads it directly; the main thread mediates writes via the Controller.

Solo mode — Monophonic Assign Mode with last-note priority. Legato controls envelope retrigger on slurs.

SoA (Structure of Arrays) — Memory layout where each field of an “object” is a separate array. Enables vectorisation across objects.

Split mode — Key mode in which the layers split at a MIDI note: notes below → Lower, at-or-above → Upper.

Split Point — MIDI note number at which Split mode partitions the keyboard. Default 60 (C4). Not automatable.

Sub-oscillator — Square wave one octave below Osc 1. Has its own mixer level.

Sync — Hard sync. Osc 1’s phase is reset by Osc 2’s phase wrap (Osc 2 is the master). Band-limited via polyBLEP.

Twin mode — Assign Mode with two channels per note, ±Detune apart. Halves polyphony to thicken each note.

Unison mode — Assign Mode with all 8 channels stacked per note. Mono. Per-channel detune.

VCA (Voltage-Controlled Amplifier) — The amp stage. Always driven by Env 2 (unless Amp Gate bypasses).

VCF (Voltage-Controlled Filter) — The main filter. VXN1’s is a 4-pole OTA-C ladder.

Velocity — MIDI note-on velocity. Routes via the Vel→Cutoff knob on the filter modulation panel.

VST3 — Steinberg’s plugin format. VXN1’s VST3 binary wraps the CLAP cdylib via clap-wrapper.

Whole mode — Key mode in which both layers play the same patch (16-voice mono-timbral).

xtask — Cargo workspace helper crate that drives non-cargo build steps (CLAP bundling, VST3 wrapper CMake invocation, code signing).