omx-panel

Panel layout from a descriptor

The project's own text, in English. The source of this page

Panel layout from a descriptor

tools/panel-description.mjs lays out a plugin's panel from its descriptor: the words omx-contract uses to describe one plugin, ours or anyone's (omx-contract docs/descriptors.md). A plugin from any origin (authored, built, scanned) gets a layout; a plugin that declares its panel (omx-plugins' do, in <stem>.decl.json) adds it on top, and what its faces serve as org.openmixer.panel-description/1 is this layout.

The two inputs

Input Who has it What it gives
The descriptor every plugin each control's kind, travel (range, unit, scale, default), choice ids and labels, label, group, meaning, hidden, readonly; the ports; the origin
The declared panel a plugin that declares one (omx-plugins: <stem>.decl.json, panel) the family, the section keys and labels, the order of the controls in each section, the roles, the widgets the panel names, the graphs, the per-placement come-up values

This package reads descriptors, never a declaration. omx-plugins renders its declarations as their authored descriptors (its tools/descriptor.mjs): one control per parameter, its id the parameter's index, its name the symbol, its label the parameter's name; a toggle is a toggle, an integer travel with labelled values a choice, every other parameter a travel with the declared unit and scale; its group the label of the panel section that lists it; its meaning the one the declared role names (below). The declared panel then goes to the layout unchanged.

Units

A control's unit is the descriptor's travel.unit. A role checks it against the units the param-contract extension serves (tools/vocabulary.mjs, servedToken): a unit's token is the LV2 unit a face writes for it, plugin-hostd's installed LV2 table read backwards, or the name of a unit in the extension's vendor block (dBu, ratio); it is served when the extension's unit table, OMX_PARAM_CONTRACT_UNITS (omx-contract's sheet, 3/omx-contract/param-contract.json in the contract directory, omx-contract-data from 3.6.0), gives the token a value. So a unit that table or that block knows (LUFS, dBu among them) is checked like any other. A unit with no token there, unknown included, takes no role and is drawn as a plain number. Nothing here types a unit: the reader reads the installed table ($PLUGIN_HOSTD_TABLES stands in for it) and the installed unit sheet (param-contract.json in omx-contract's directory), and refuses by name when either is missing.

The level roles (threshold, ceiling) take dB, dBFS, dBu and LUFS.

Meanings and roles

A skin binds by meaning, never by symbol. A family's roles map to the descriptor's meanings (tools/vocabulary.mjs, meaningOfRole, over omx-contract's closed list of meanings as its installed schema declares it); a role whose name is a meaning maps to it, and these are renamed:

Role Meaning
mix wetDryRatio
time delay
cutoff cutoffFrequency
band_freq, band_gain, band_q frequency, gain, q
hpf_freq, low_cut lowCut
lpf_freq, high_cut highCut
hpf_slope, lpf_slope slope
key_listen listen

A role with no meaning word (band_type, model, algorithm, knee_start, ...) is bound only by a declared panel.

With no declared panel the roles come from the meanings: in the family the layout takes, each meaning that names exactly one of the family's roles plays it; an indexed role (band_*) takes every control of its meaning, in order; any other role is played by its first control, and a second is left unpublished, named.

A modulation depth is a plain amount (none, pc, coef) or a sweep in octaves (oct), as a phaser's is.

The family

The family follows from the meanings, never from a guess about the plugin:

Family A declared family needs one of Inferred, with no declared panel, from all of
limiter ceiling ceiling
deesser frequency, threshold frequency, threshold, listen
gate threshold threshold, range
dynamics threshold, ratio threshold, ratio
eq gain, frequency frequency, gain, q
filter cutoffFrequency cutoffFrequency
drive drive drive
delay delay delay, feedback
reverb size, decay size, or decay
pitch shift shift
modulation (any: a rotor's speeds and an LFO's shape have no meaning word yet) rate, depth

A declaration whose family none of its meanings backs is refused, naming the family and the meanings it would need: a transient designer (attack and sustain amounts, no threshold, no ratio) is not a compressor, so its family is none. Inference tries the families in the order of the table and takes the first whose meanings are all there; none fits, none.

The widget

The widget is a step of its own, widgetOf(control, declared), before any skin. It picks one of the standard widgets, and a representation (a skin) draws it; the skins are not chosen here.

Standard widget Taken by
knob a travel
fader a travel the declared panel draws as a fader
switch a toggle; a trigger (momentary)
selector a choice
meter a read-only travel in a level unit (dB, dBFS, dBu, LUFS)
display any other read-only control
curve a section's graph a host computes (eq-curve, dynamics-curve, transfer-curve)
xy no descriptor word names a pair of controls yet; nothing takes it

A meter carries its measurement (peak, rms, vu, ppm, lufs): lufs for a LUFS reading. The descriptor has no word for the others, so any other meter's measurement is null, not a guess.

What a face serves keeps the extension's widget enum: knob, fader and switch as they are, a selector as auto (a CLAP selector needs CLAP_PARAM_IS_ENUM, which the faces do not set).

Sections with no declared panel

Each distinct group path is a section, in the order its first control comes; its label is the path joined with /, its key the path's segments in the key grammar, joined with dots. Controls with no group go to a section controls. A hidden control is left out. The layout also carries the ports: the audio inputs and outputs with their channels, whether one is a side-chain, and the note inputs.

Pages

omx-panel layout --pages cuts each section's controls, in the section's order, into pages of eight (the CLAP remote-controls surface); a section of several pages numbers them (Filter 1, Filter 2). A surface with another number of encoders asks for it: --pages --per-page N (perPage in schema/panel.schema.json, default 8). Then a group that fits a page is not split across two (the page ends early), a group wider than N is cut, and a section of several pages names each page by the controls it holds (Band: Freq - Q). Every page carries N slots, a short page's last ones null. Left out, the output is the plain cut of eight, unchanged.

The Easy face

tools/easy.mjs picks the handful of controls a plugin opens on; every control is one click away, on the same processor. easyOf returns their symbols, and the layout carries them as easy. The first that answers wins:

  1. Declared: the declared panel's easy, else the descriptor's. At most 8. A list that names no control of the panel, a control no face can set, or one control twice is refused by name, never trimmed.
  2. Roles: the family's row below, each role that is mapped, at most 6. An indexed role with more members than places takes them evenly spaced, both ends included, so a 31-band graphic shows its whole span.
  3. Order: when the roles gave fewer than 4, the first controls in layout order fill up to 4.
Family Roles, in order
gate threshold, range, attack, release
dynamics threshold, ratio, makeup, mix, attack, release
limiter ceiling, threshold, release, output
deesser frequency, threshold, range, mix
eq band_gain, output
filter cutoff, resonance, filter_type, mix
drive drive, tone, model, mix
modulation rate, depth, feedback, mix
delay time, feedback, tone, mix
reverb size, decay, damping, mix
pitch shift, fine, mix

A face can set any control that is not a reading, a trigger or a bypass. The list is a view of the panel, so it stays out of the canonical serialisation and the fingerprint.

omx-panel layout --pages --easy-first puts the Easy controls first, on pages named Easy, Easy 2, with no control on two pages; a scanned descriptor takes the easy of the descriptor the plugin's package installed under the same plugin id, as a scan has nothing to read one from. Without the flag the pages are the page rule's. omx-panel modgui --easy-first draws the Easy controls as a MOD GUI's first section and lists them as modgui:port.

Verbatim

For omx-plugins' plugins the descriptor carries what the declaration did, so every generated header, every built face's description, every MOD GUI and its rasters stay byte-identical when omx-plugins lays them out through this package; its own --check runs hold that.

test/panel-layout.test.mjs (make test) holds each rule above by value, with an arm per rule that breaks its input and reads the result change; it lays out omx-contract's example descriptors (test/fixtures/descriptors/, scanned CLAP and LV2, a built instrument). The rule over omx-plugins' own plugins, which loads their declarations, stays in omx-plugins.

openmixer

A software mixing console for Linux. The desk is the software; the browser is the surface.

Pages

Licence

OpenMixer is free software under the GPL-3.0-or-later. Every package in the workspace carries the same licence.

Roland, Midas, Behringer, RME and the product names used here belong to their respective owners. OpenMixer is an independent project and is not affiliated with any of them.