0010 — An auto-generated, metadata-driven plugin editor
Status: Accepted (built) Date: 2026-06-29
Context
openmixer hosts arbitrary LV2 plugins (LSP, Calf, Zam, …), each with its own set of control ports — knobs, toggles, enumerations, log-scaled gains, file paths. The EasyEffects approach is to hand-code one UI and one preset class per effect. That does not scale to "any installed plugin" and welds the UI to a fixed plugin set.
LV2 already carries, in each plugin's Turtle metadata, everything needed to render the right control: range, default, integer/toggle/enumeration/logarithmic/trigger flags, scale points, and the unit symbol. Carla, MOD-UI, Non-Mixer and Zynthian all independently confirm that a single metadata→widget decision table is the right abstraction.
Decision
One decision table, no per-plugin UI code. A lilv scanner (scan.py) introspects
installed plugins into PluginDescriptors; a single widgetFor(param) function maps each
parameter's metadata to a widget kind (knob, logKnob, toggle, button, stepper,
dropdown, filepath, text, readout); the web UI renders that widget generically. A
curated palette intersected with what is actually installed gives the operator a quality
shortlist, while the full scan stays searchable.
This is built end to end: scan.py → @freemixer/catalog (widgetFor, Catalog,
curated palette) → the web UI's ParamControl and its widget components, with no
plugin-specific code anywhere.
Consequences
- Any installed LV2 plugin gets a usable editor for free; adding a plugin to the palette is a one-line catalog entry, not a new component.
- The same descriptor metadata drives the editor, the mod-host
param_setcalls, and (designed) the MCU encoder labels and scribble strips — one introspection pipeline, many surfaces. - Bypass and preset selection can be modelled as synthetic
:bypass/:presetspseudo control ports so the same widget pipeline renders them with no special-case UI (designed; the server already hassetBypass/loadPreset). - It depends on plugins providing honest LV2 metadata; where a port lacks a range or unit, the editor falls back to a plain readout rather than guessing.
- The explicit anti-pattern: do not hand-code per-plugin UIs. Per-plugin overrides are permitted only as optional data (labels, grouping), never as code.