Package a Volta patch as a CLAP instrument for use in another music application, within the supported export limits.
What you get#
volta export plugin examples/songs/aphelion.volta \
--patch fogPad --name "Fog Pad" -o ~/plugins
→ ~/plugins/Fog Pad.clap, an instrument plugin that:
- plays
fogPadfrom host MIDI, note-on/note-off with the patch's own release (held notes, not fixed-length one-shots); - exposes every
macrothe patch reads as a CLAP parameter under its own name and declared range/unit (macro "cut" 800Hz→cut, 0–20000 Hz), plus aLeveltrim; - has its own plugin name and CLAP id (
org.voltalang.patch.fog-pad), so ten exported patches are ten devices in the browser; - follows the host tempo for tempo-relative rates inside the patch;
- carries its own source:
Contents/Resources/patch.voltais the exact standalone module that was compiled in, so the plugin round-trips back into Volta (open it, edit it, re-export).
Install on macOS: cp -R "~/plugins/Fog Pad.clap" ~/Library/Audio/Plug-Ins/CLAP/.
The command#
volta export plugin <song.volta> [--patch <name>] -o <dir>
[--name "Display Name"] [--id <clap-id>]
[--no-build] [--debug] [--plugin-target <dir>]
| flag | meaning |
|---|---|
--patch <name> | which patch to export. Optional when the song declares exactly one; otherwise the error lists the candidates. |
-o / --out | output directory (default .). |
--name | the plugin's display name and bundle name. Default: the patch's binding name. |
--id | CLAP id. Default org.voltalang.patch.<slug of name>. |
--no-build | stop after slicing: writes <slug>.volta (the standalone module) and the export spec, builds nothing. The fast way to see what would be exported. |
--debug | build the player in the dev profile — much faster, not for delivery. |
--plugin-target <dir> | where plugin-target/ lives. Default: $VOLTA_PLUGIN_TARGET, then the checkout this CLI was built from, then a walk up from the working directory. |
Exporting builds a small crate (design doc §3 says why: nih-plug reads the plugin's identity from compile-time consts), so the machine doing the export needs a Volta checkout and cargo. Warm builds take ~10 s; the first one on a clean checkout compiles the engine. The artifact needs neither.
What gets sliced out#
The patch plus every local top-level binding it needs, transitively — the macro it reads, the helper that macro reads, the patch it extends with with — in buffer order. Tracks, songs and arrangements never come along. On top of that:
- the song's
tempo =declaration is carried even when the patch never names it (tempo-relative rates are a compile-time input); - an
importline survives only if the closure uses one of its names, and the imported module's text travels with the export (into the spec and intoContents/Resources/<path>.volta).
The sliced module is compiled on its own before anything is built, so a bad slice is a CLI error with the compiler's own message, never a broken plugin.
Parameters#
| Param | Meaning |
|---|---|
<macro name> | one float per declared macro, in declaration order, over its declared range with its unit as suffix, defaulting to its declared initial. A parameter drives its macro once it moves; untouched, the patch's own value and modulation graph stay authoritative. A value already off the default when the plugin loads (project recall) is written through at load. |
Level | output trim, 0–2, smoothed. |
Parameter ids are positional (m0, m1, …). Renaming a macro is safe; inserting a new macro before an existing one shifts the ids, so automation written against an older export moves. Export order = declaration order in the sliced module.
Limits (Stage 1)#
- No sample bank.
sample "…",bounceOf,convolve,wavetableOfandlatentOfsources render silence in the exported plugin — the CLI warns at export time. (Design doc Stage 3.) - No plugin GUI — the host's generic parameter panel, with real names.
- CLAP only (VST3 would make the artifact GPLv3 — Use Volta as a plugin §1).
- One patch per export; the song's mixer/inserts/arrangement stay behind.
- No pitch bend, poly pressure or CC — plain note-on/note-off, velocity.
Testing an export headlessly#
The player's own suite can be re-baked around a real export, which is the cheapest way to prove one before opening a DAW:
cd plugin-target
VOLTA_PATCH_SPEC=target/patch-export/<slug>/spec.json cargo test -p volta-patch-player
It asserts the embedded patch compiles, a note-on sounds, a note-off releases, and a macro write lands under its baked name. clap-validator validate "<Name>.clap" covers the CLAP layer (2026-08-12: 44 tests, 32 passed, 4 failed, 8 skipped — the state-reproducibility-* family, the same pre-existing param-layer issue Use Volta as a plugin records).
Where things live#
| piece | file |
|---|---|
| island slice (closure + code-position scan) | compiler/src/island.rs |
| the command | cli/src/pluginexport.rs |
| identity/source/param codegen | plugin-target/patch-player/build.rs |
| audio thread + CLAP entry | plugin-target/patch-player/src/lib.rs |
| control thread (compile, notes, macros) | plugin-target/patch-player/src/control.rs |
| headless render suite | plugin-target/patch-player/tests/render.rs |
export spec (schema volta.patch-plugin/1) | plugin-target/target/patch-export/<slug>/spec.json |