321 lines
18 KiB
Markdown
321 lines
18 KiB
Markdown
# External Plugin Hosting Evaluation
|
|
|
|
The original decision deferred external hosting. The native source API is useful
|
|
for proving processor contracts without adding an SDK or scanning/loading code.
|
|
|
|
| Candidate | Benefits | Work required before adoption |
|
|
| --- | --- | --- |
|
|
| LV2 | Open specifications, established Linux hosts/plugins | RDF discovery and feature negotiation, URIDs, state, UI lifetime, worker requests and event mapping; likely additional discovery dependency |
|
|
| CLAP | C ABI, explicit lifecycle and event model | Extension negotiation, parameter/state persistence, host callbacks, main/audio-thread scheduling and UI integration |
|
|
| VST3 | Broad plugin availability | SDK/license review for the selected SDK release, C++ adapter outside the C core, component/controller and editor lifecycle, state and event translation |
|
|
|
|
No candidate was linked, scanned or loaded. API/SDK and plugin licenses must be
|
|
checked at the revision actually adopted; they are separate from xtc's MIT
|
|
license. Supporting a format does not grant redistribution rights to individual
|
|
plugins. Do not search user plugin directories from the callback.
|
|
|
|
Before implementation, require versioned module identity and state, a missing-
|
|
plugin recovery policy, preparation failure/partial cleanup tests, bounded
|
|
events and parameter publication, latency/tail handling, callback-safe unload
|
|
after retirement, a GUI/main-thread boundary and an explicit crash-containment
|
|
decision. Native ABI tests cover some ownership mechanics but do not prove
|
|
third-party code safe. Event graph ports remain a gap; track instrument
|
|
selection is described below. Choose one format with concrete user needs after
|
|
those contracts are specified; do not introduce a general multi-format framework
|
|
now. See decision 0008.
|
|
|
|
---
|
|
|
|
## LADSPA Implementation (Supersedes the Earlier Deferral)
|
|
|
|
Decision [0009](decisions/0009-parameter-processors-and-ladspa.md) selects a
|
|
bounded LADSPA 1.1 host. `xtc_plugin.h` is the C API; no C++ engine or FLTK type
|
|
is needed.
|
|
|
|
1. Choose **File → Load LADSPA plugin** and a trusted absolute `.so` path.
|
|
Loading executes native code immediately, including constructors during
|
|
discovery.
|
|
2. The descriptor browser lists names, labels, makers and source/effect roles.
|
|
Select a row and Choose. Counts report compatible and skipped descriptors.
|
|
3. In the Modular Graph, connect the new source/effect output to an output or
|
|
other effect; effects have an optional mono input. Unconnected effect inputs
|
|
receive external input, as other graph processors do.
|
|
4. Select its node and use **Edit → Selected plugin control**. The editor shows
|
|
name, maker, copyright, module path, descriptor index and unique ID, then
|
|
actual port IDs, editable values, rate-adjusted bounds, defaults and
|
|
discrete/ logarithmic hints. Apply validates all values and candidate
|
|
initialization; Cancel and window close preserve the document. Toggled
|
|
controls require 0/1 and integer controls require integral values.
|
|
5. Save normally, reopen, reconnect external JACK playback if needed, and Play.
|
|
Plugin changes stop/rebuild playback and participate in ordinary undo/redo.
|
|
|
|
The GUI uses only public graph/model/plugin APIs. Discovery probes explicit
|
|
module files by descriptor index, bounded to 1024 descriptors. It never
|
|
recursively scans system directories. The C API exposes probe, parameter
|
|
metadata, instance create/process/destroy, and add/get/set graph configuration.
|
|
Missing header means hosting returns UNSUPPORTED; metadata persistence continues
|
|
to work.
|
|
|
|
Supported: 0/1 audio input, 1 output, <=64 total ports and <=16 input controls.
|
|
Stereo graph channels are two independent mono instances. Stereo/multichannel
|
|
LADSPA descriptors are rejected, rather than downmixed silently. Control output
|
|
ports have private storage; no output-control GUI is implemented. No plugin UI,
|
|
MIDI/events, sample automation, worker interface, latency compensation or opaque
|
|
state is supplied by this first format. Parameter configuration is persisted;
|
|
oscillator phase, held voices, delay history and tails are not. No smoothing is
|
|
added around plan replacement. Integer LADSPA sample rates are obtained from the
|
|
engine rate; practical backend rates are integral.
|
|
|
|
A new plan opens and owns its own modules and instances. Failed loading,
|
|
incompatible identity/range/layout, or instantiate returning NULL unwinds the
|
|
candidate and leaves active/pending plans intact. Only run and bounded host
|
|
buffer copies execute in audio. Destruction and unloading happen after callback
|
|
quiescence or retirement acknowledgement on the control thread. Transport reset
|
|
retains LADSPA history; compile/reopen starts fresh. A LADSPA run callback has
|
|
no error return. Nonfinite/subnormal samples become zero, but native crashes,
|
|
corruption, runaway CPU, allocations or I/O inside a plugin cannot be contained
|
|
or prevented. Use trusted plugins; hard-realtime properties are declarations,
|
|
not proof. This is an in-process host, not a crash-isolation service.
|
|
|
|
Project v5 stores absolute module path, index, label, LADSPA unique ID, mono
|
|
input count and ordered input-control port/value pairs. Restore verifies
|
|
identity, layout, parameter ports and ranges against the loaded module. No
|
|
pointers are serialized. Relocating a module requires restoring it at its saved
|
|
absolute path or deliberately modifying its metadata through a C client. GUI
|
|
Open reports the missing/incompatible identity/path and retains the current
|
|
document. Metadata load/save and `xtc-check --validate` do not execute plugin
|
|
code and do not certify module availability. LADSPA IDs are not cryptographic
|
|
binary/version identities.
|
|
|
|
---
|
|
|
|
## Repeatable Acceptance
|
|
|
|
```sh
|
|
make all test gui test-plugins test-gui-plugins analyze
|
|
make test-plugins-real
|
|
```
|
|
|
|
The first plugin target builds an original MIT shared-library fixture with gain,
|
|
source, unsupported stereo and failed-initialization descriptors. Tests exercise
|
|
identity/range errors, exact in-place gain, save/reopen/exact rendering, missing
|
|
module metadata preservation and 100 plan replacements with callback guards
|
|
against host allocation/free/dlopen/dlclose. GUI commands cover add, control
|
|
edit, invalid-value rollback, undo/redo and save/New/reopen. ASan/UBSan/leak
|
|
checks cover host/fixture lifetimes; plugin code from system packages is not
|
|
instrumented.
|
|
|
|
`test-plugins-real` requires these separately installed modules (nothing is
|
|
bundled or installed by the target):
|
|
|
|
| Module / descriptor | Tested behavior |
|
|
| --- | --- |
|
|
| `/usr/lib/ladspa/amp.so`, 0 `amp_mono` | One input, gain control, finite nonzero render, exact save/reopen render |
|
|
| `/usr/lib/ladspa/inv_1429.so`, 0 `inv` | One input, no controls, exact save/reopen render |
|
|
| `/usr/lib/ladspa/sine.so`, 3 `sine_fcac` | No input, frequency/amplitude controls, exact save/reopen render |
|
|
|
|
Tested packages: ladspa 1.17-7 and swh-plugins 0.4.17-7. These zero-latency
|
|
processors define the initial compatible set; other plugins remain unqualified.
|
|
Physical listening and plugin CPU/endurance qualification remain pending.
|
|
|
|
---
|
|
|
|
## Discovery, Editing and Graph Lifecycle
|
|
|
|
Discovery remains explicit-file only, bounded to 1024 descriptor indices; no
|
|
recursive scan, plugin database or new dependency is introduced. Unsupported
|
|
layouts are skipped with counts; source/effect roles describe audio ports, not
|
|
MIDI instrument capabilities. Modern track instruments are described below.
|
|
LADSPA sources can be identified, edited, replaced and removed in the graph but
|
|
cannot receive MIDI events.
|
|
|
|
**Edit → Insert LADSPA on selected wire** replaces one existing connection with
|
|
`source -> effect -> destination`, as one undo step. Only one-input effects are
|
|
accepted; stale wires, capacity failures and initialization failures retain the
|
|
original graph. **Edit → Replace selected plugin** accepts the same audio input
|
|
layout, keeps the node ID and all wires, and installs the new descriptor's
|
|
default controls with no inherited automation. It can replace from another
|
|
module. A source/effect layout mismatch is rejected. Undo restores the old
|
|
identity, values and automation. Every candidate is compiled before adoption
|
|
even when the backend is disconnected; no intermediate graph is published.
|
|
|
|
Ordering is graph signal flow, not descriptor order, creation order or a flat
|
|
rack. Use the graph's connection/disconnection tools to place effects in series
|
|
or parallel. Insertion on a wire preserves its endpoints and changes only that
|
|
stage. Select a plugin and Delete to remove it and incident wires, then
|
|
reconnect as desired; deletion does not automatically bridge or sum routes. A
|
|
now-unwired effect receives external audio, following existing graph semantics.
|
|
Undo/redo restores removal and routing. Independent bus inserts are still
|
|
outside the shared-source graph model.
|
|
|
|
Reset defaults in the editor previews every control's declared/default-derived
|
|
value. Apply also clears that plugin's automation; Cancel preserves it. The
|
|
**Edit → Reset selected plugin** command does the same in one undo step. The
|
|
public control-thread `xtc_plugin_reset` can reset one port (removing only its
|
|
lane) or UINT32_MAX for all controls. Automation otherwise overrides static
|
|
values, so editing a static control does not remove an existing lane.
|
|
|
|
`xtc_plugin_describe` returns copied name/maker/copyright (bounded/truncated),
|
|
parameter ranges/defaults and integer/toggle/logarithmic/sample-rate/boundedness
|
|
flags. Missing maker/copyright receive explicit fallback labels. LADSPA has no
|
|
standard unit metadata: unit stays empty and the editor says unspecified rather
|
|
than guessing Hz/dB from names. Numeric inputs use linear parameter values;
|
|
logarithmic is a descriptor hint, not an extra host conversion. Unbounded ends
|
|
are labeled unbounded; finite float extrema remain the validation bounds.
|
|
Sample-rate hints scale bounds and defaults once at the engine's actual rate;
|
|
nonrepresentable scaled bounds are rejected. Discrete defaults are rounded and
|
|
validated; impossible defaults fail discovery rather than reaching the engine.
|
|
|
|
The generic modal editor owns copied metadata and temporary widget values only.
|
|
It never owns or retains module handles, descriptors or audio instance pointers.
|
|
Closing it cannot unload active DSP; Apply builds a new immutable plan before
|
|
installation and existing retirement/quiescence rules release old instances. No
|
|
embedded third-party editor UI is supported.
|
|
|
|
### Compatibility and recovery
|
|
|
|
LADSPA 1.1 descriptor identity is module path + index + label + unique ID, with
|
|
exact supported port/control layout verified on restore. A unique ID is not a
|
|
binary version/hash guarantee. Parameter values and bounded automation persist
|
|
in existing project v9 records; this work adds no file format change. Transient
|
|
history/tails and opaque state remain unsupported. Native processor ABI 2 and
|
|
its exact `struct_size` contract are unchanged; it remains parameter-only state.
|
|
Consumers rebuild against public headers; the new copied details API adds no
|
|
callbacks or GUI types to the C core.
|
|
|
|
Missing/incompatible modules reject GUI Open with the offending label/path and
|
|
leave the current project intact. Restore a compatible module at that path and
|
|
retry; use metadata-only validation/save to preserve an unavailable module's
|
|
records. No dummy replacement silently removes its controls. Failed discovery,
|
|
replacement or instantiate reports an error and retains the current document. A
|
|
LADSPA `run` callback has no error return; finite-sample sanitation does not
|
|
contain crashes, hangs, memory corruption or prohibited work inside a plugin.
|
|
Only trusted in-process plugins are supported. See RECOVERY.md.
|
|
|
|
### Verification
|
|
|
|
Fixture tests cover copied identity/hints, integer and toggle defaults and range
|
|
rejection, logarithmic/sample-rate hints, scaled-range overflow rejection, reset
|
|
and unchanged-on-error behavior, parameter/automation save/reopen and exact
|
|
rendering. Noncommutative gain/offset effects prove that graph order changes
|
|
output as expected. GUI tests drive the actual chooser/editor, reset/Apply and
|
|
Cancel, replacement layout/init/missing-module rollback, insertion/stale-wire
|
|
failure, deletion and undo. Existing fixture tests exercise 100 live plan
|
|
replacements with host allocation/free/dlopen/dlclose guards. Native ABI tests
|
|
cover process-error silence/reset. The separately installed amp/inverter/sine
|
|
modules remain the qualified LADSPA set. Human listening, arbitrary third-party
|
|
editors/plugins and extended realtime endurance remain unverified.
|
|
|
|
---
|
|
|
|
## Parameter Targets
|
|
|
|
`xtc_control.h` supplies common stable owner/parameter IDs and control-thread
|
|
metadata/edit APIs for track/bus gain/pan and LADSPA input ports. The GUI's
|
|
plugin automation command uses this contract. Linear/hold LADSPA encoding and
|
|
its integer/toggle restrictions remain unchanged; v10 adds mixer interpolation
|
|
and live-sine CC maps without changing plugin state records. Direct static edits
|
|
do not override existing lanes. Live CCs do not map to plugins; missing targets
|
|
or modules fail without installing partial state. See CONTROL.md for timing,
|
|
precedence, reset and backward-jump behavior.
|
|
|
|
---
|
|
|
|
## CLAP, VST2 and VST3 Effect Loaders
|
|
|
|
**File → Load plugin** now detects LADSPA, CLAP, VST2 and VST3 entry points in
|
|
an explicit trusted Linux module. **Insert plugin on selected wire** and
|
|
**Replace selected plugin** use the same browser and transactional graph
|
|
preparation. The identity/editor dialog names the format and stable ID. Existing
|
|
LADSPA controls and automation continue to work. Modern effects currently use
|
|
their default configuration: the editor shows identity but no editable
|
|
parameters.
|
|
|
|
Choose the actual binary: `.clap` for CLAP, a VST2 `.so` exporting
|
|
`VSTPluginMain`, or a VST3 bundle's `Contents/<architecture>-linux/<module>.so`
|
|
exporting `ModuleEntry`, `ModuleExit` and `GetPluginFactory`. The C API also
|
|
accepts conventional `.vst3` bundle paths and resolves the matching architecture
|
|
and bundle-stem binary. The file chooser may require selecting the inner binary;
|
|
no recursive plugin-folder scan is performed.
|
|
Auto-detection prefers CLAP, then VST2, then VST3, otherwise LADSPA; the C API
|
|
also provides `xtc_plugin_probe_format` for an explicit format.
|
|
|
|
The modern subset is **one mono audio input and one mono output, float32,
|
|
default configuration, zero reported latency**. Stereo graph playback uses
|
|
independent instances. For the modern effect subset, true
|
|
stereo/multichannel/sidechain buses, instruments, MIDI/events, custom editors,
|
|
parameter changes/automation, programs/presets, opaque state, transport/time
|
|
information, workers and latency compensation are not implemented for these
|
|
three formats. Modern metadata with controls/automation is rejected, not
|
|
silently ignored. VST3 event buses and CLAP note ports are rejected. Plugins
|
|
requiring unsupported host extensions may refuse initialization. This is not a
|
|
claim that all CLAP/VST2/VST3 plugins will load or sound identically after
|
|
updates.
|
|
|
|
Only original effect fixtures have been qualified for the new formats. Trusted
|
|
plugin code is in-process and can crash/hang or violate realtime rules. No
|
|
plugin binary is included in packages. A VST2 ABI adapter does not provide
|
|
rights to the discontinued SDK or plugin redistribution. See decision 0010 and
|
|
LICENSES.md.
|
|
|
|
The control/main thread owns discovery, activation, module reference accounting,
|
|
retirement and unloading. CLAP's start-processing call runs on the audio owner;
|
|
main-thread callbacks are coalesced and serviced via `xtc_plugin_idle` (GUI
|
|
timer, currently 100 ms). C hosts using CLAP must pump that API on their
|
|
serialized main thread while instances live, and stop/join audio before
|
|
destruction. Restart and VST2 I/O-change requests make subsequent blocks
|
|
fail/silence; stop and rebuild or reload the plugin rather than reconfiguring
|
|
inside the callback. CLAP process errors and VST3 failure results silence a
|
|
block. VST2 has no process error return.
|
|
|
|
```sh
|
|
make all test gui test-modern-plugins test-gui-modern-plugins
|
|
make test-sanitize analyze
|
|
```
|
|
|
|
Original CLAP/VST2/VST3 shared-library fixtures exercise exact in-place and
|
|
stereo processing, entry/instance lifetimes, rejected layouts, partial
|
|
initialization, CLAP start/process failures, graph replacement rollback, 50 plan
|
|
retirements per format, missing modules, v11 save/reopen and GUI
|
|
add/browser/undo/reopen. Host allocation/loading/unloading guards remain active
|
|
while rendering. This evidence qualifies the subset above, not arbitrary
|
|
external plugins or physical audio.
|
|
|
|
---
|
|
|
|
## Instrument Support
|
|
|
|
MIDI tracks can bind CLAP, VST2 and VST3 instruments. In the Track menu, select
|
|
**Load instrument plugin** for the selected sequencer track; **Use built-in
|
|
instrument** removes the binding. Assignment also selects the track for live
|
|
MIDI. **Live MIDI to selected instrument** changes that target; **Live MIDI to
|
|
built-in sine** restores the legacy live source. MIDI clip notes always go to
|
|
their own track's instrument. Independent instances feed track gain/pan,
|
|
mute/solo, bus routing and automation. Bindings/live target save in v12.
|
|
|
|
Supply the actual trusted Linux binary (including the inner .so of a VST3
|
|
bundle). Supported instruments have zero audio inputs, one mono output, float32
|
|
processing, zero latency and one note/event input. Default configuration only:
|
|
no custom editor, opaque state/preset restoration, modern parameter
|
|
editing/automation, stereo/multichannel instruments or MIDI output. No arbitrary
|
|
third-party instrument is qualified by the original ABI fixture tests.
|
|
|
|
CLAP accepts MIDI or native notes; VST2 uses MIDI events; VST3 uses IEventList
|
|
note events. Live CC reaches CLAP MIDI/VST2; native CLAP/VST3 have no controller
|
|
mapping (including sustain). Live MIDI goes to exactly one selected target.
|
|
Seeks/loops retrigger notes crossing the destination with fresh envelopes;
|
|
plugin oscillator/opaque DSP state cannot be reconstructed. Panic delivers
|
|
all-off (VST3 held-key note-offs); plugin release tails may remain. Processing
|
|
uses preallocated 256-frame chunks and 256 incoming events per instance/chunk;
|
|
overflow or processing failure silences the block and schedules reset events.
|
|
The existing 32 simultaneous scheduled-note and 4096 stored-note bounds remain.
|
|
Do not unload/replace modules during playback; candidate plans are prepared off
|
|
the callback and old instances are reclaimed on the control thread. Missing
|
|
references remain inspectable without loading; failed GUI preparation retains
|
|
the current project. Save a backup for applications limited to v11 or earlier.
|
|
|
|
|
|
Phase 106 adds [official SDK examples and qualification](STEINBERG.md), including
|
|
VST3 process context (sample position, rate, tempo and musical position), an
|
|
SDK-built gain/synth pair and a WAV render example. Native Windows ASIO remains
|
|
in progress; the optional ASIO SDK example currently provides device diagnostics.
|