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_set calls, and (designed) the MCU encoder labels and scribble strips — one introspection pipeline, many surfaces.
  • Bypass and preset selection can be modelled as synthetic :bypass / :presets pseudo control ports so the same widget pipeline renders them with no special-case UI (designed; the server already has setBypass / 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.