[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"note-from-a-plugin-file-to-a-page-en":3},{"slug":4,"order":5,"title":6,"summary":7,"measured":8,"topic":9,"toc":10,"html":26},"from-a-plugin-file-to-a-page",70,"From a plugin file to a page on the screen","Five stages turn a CLAP or LV2 file into a menu row with its figures, controller pages and a MOD GUI on a Zynthian. Each stage has one owner, writes one artefact in a fixed place, and the stages after it read that artefact and nothing else.","plugin-hostd, plugin-qualify and omx-panel on a Raspberry Pi 5 with Zynthian, October 2026.","zynthian",[11,14,17,20,23],{"id":12,"title":13},"1-scan-and-describe","1. Scan and describe",{"id":15,"title":16},"2-qualify","2. Qualify",{"id":18,"title":19},"3-pins","3. Pins",{"id":21,"title":22},"4-layout","4. Layout",{"id":24,"title":25},"5-read-on-the-device","5. Read on the device","\u003Cp>An instrument on a Zynthian is a file on the disk: a CLAP or LV2 binary. What the player sees is a row in a menu with its\nfigures, a chain with a MIDI input, controller pages with knobs, a MOD GUI, and a footer saying what the chain will cost.\nFive stages turn one into the other. Each stage has one owner, writes one artefact in a fixed place, and what comes after\nreads that artefact and nothing else.\u003C\u002Fp>\n\u003Cp>The running example is an instrument: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FDISTRHO\u002FKars\">Kars\u003C\u002Fa>, an LV2 plucked-string synth, in the\ncaptures from the device; Surge XT (CLAP) where its figures matter; and an effect once, to show where the layout differs.\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"\u002Ftechnical-notes\u002Ffrom-a-plugin-file-to-a-page\u002Ffrom-file-to-page.svg\" alt=\"The five stages, the artefact each writes and the surfaces that read them\">\u003C\u002Fp>\n\u003Ch2 id=\"1-scan-and-describe\">1. Scan and describe\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Owner:\u003C\u002Fstrong> plugin-hostd. \u003Cstrong>Writes:\u003C\u002Fstrong> the descriptor, one JSON document per plugin binary under\n\u003Ccode>\u002Fvar\u002Flib\u002Fomx\u002Fdescriptors\u003C\u002Fcode>, named \u003Ccode>&lt;id&gt;@&lt;version&gt;-&lt;hash&gt;.json\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>The daemon loads the plugin once in the matching host (omx-clap-host for CLAP and for the LV2 plugins its adapter takes,\nmod-host for the rest) and reads everything the plugin declares: id and version, audio and MIDI ports, every parameter with\nsymbol, name, unit, range, default, steps or enumeration, flags, declared latency, and the extensions it supports.\u003C\u002Fp>\n\u003Cp>For an instrument the descriptor says what an effect's does not: a MIDI input port, and so the notes it takes, its\nparameters and its presets. Kars's descriptor has the three parameters the engine later shows on its one page: Sustain,\nRelease and Volume.\u003C\u002Fp>\n\u003Cp>The descriptor must fit the contract: omx-contract defines the vocabulary (units, port kinds, scopes, outcome codes), and a\nscan that finds an unknown unit or field is refused, not invented. OpenMixer's own plugins skip the scan: their parameters\nare written once in a declaration, and a generator writes the descriptor, the LV2 bundle, the CLAP parameters and the\nparameter reference from it.\u003C\u002Fp>\n\u003Ch2 id=\"2-qualify\">2. Qualify\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Owner:\u003C\u002Fstrong> plugin-qualify. \u003Cstrong>Writes:\u003C\u002Fstrong> the qualification record, \u003Ccode>&lt;plugin&gt;@&lt;version&gt;-&lt;hash&gt;.&lt;offer&gt;.qualification.json\u003C\u002Fcode>,\nwith the probe output it judged beside it, under \u003Ccode>\u002Fvar\u002Flib\u002Fomx\u002Fqualification\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>The probe runs the plugin at every cell of a grid (44.1, 48, 96 and 192 kHz times blocks of 128, 256, 512 and 1024\nframes) and writes the record: the checksums of the binary's files, the cost per block, declared and measured latency,\nreal-time safety under the interposer, or a reason code for each cell that did not run. For an instrument the probe holds\na set of notes while it measures, so the cost is the cost with those voices sounding.\u003C\u002Fp>\n\u003Cp>A verdict for each host follows: suitable, isolated (it runs in a worker of its own and is never grouped), or unsuitable.\nKars is rated suitable for omx-clap-host, real-time safe under a parameter sweep and under notes, hosted isolated. The Pi's\nfigures are in \u003Ca href=\"\u002Fnotes\u002Fplugin-hostd-on-a-raspberry-pi-5\">plugin-hostd on a Raspberry Pi 5\u003C\u002Fa>, and Surge XT's in\n\u003Ca href=\"\u002Fnotes\u002Finstruments-on-a-pi-5\">Instruments on a Pi 5\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"3-pins\">3. Pins\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Owner:\u003C\u002Fstrong> plugin-qualify. \u003Cstrong>Writes:\u003C\u002Fstrong> \u003Ccode>\u002Fvar\u002Flib\u002Fomx\u002Fqualified.pins\u003C\u002Fcode>, one line per plugin.\u003C\u002Fp>\n\u003Cp>From the records, one line per plugin says what plugin-hostd may host and how (see\n\u003Ca href=\"\u002Fnotes\u002Fpins-on-first-use\">Pins on first use\u003C\u002Fa>). The daemon loads the pins as its only source of permission, and its\nplanner reads them to group workers and to place them on cores. An instrument is never put into a chain's shared worker,\nso a fault or a late buffer in a voice cannot reach the effects after it.\u003C\u002Fp>\n\u003Cp>That is also what the cores need. Surge XT 1.4.0 ignores CLAP's thread-pool extension, so one instrument is one audio\nthread. Several instruments use several cores when each is in its own worker and the workers are spread over the audio\ncores, with the daemon kept off them.\u003C\u002Fp>\n\u003Ch2 id=\"4-layout\">4. Layout\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Owner:\u003C\u002Fstrong> omx-panel. \u003Cstrong>Writes:\u003C\u002Fstrong> the pages, computed from the descriptor by \u003Ccode>omx-panel layout --descriptor\u003C\u002Fcode>; the record\nkeeps the layout scheme and hash it was judged against.\u003C\u002Fp>\n\u003Cp>The layout turns a descriptor's parameters into pages: sections become groups, and every control gets a stable key. The\nsame layout drives the engine's controller pages in Zynthian, the MOD GUI generated for MOD-UI, and the bundle that adds\ngroups to third-party LV2 plugins (see \u003Ca href=\"\u002Fnotes\u002Fwhy-lv2-plugins-on-zynthian-need-inferred-port-groups\">Why most LV2 plugins on a Zynthian need inferred port\ngroups\u003C\u002Fa>).\u003C\u002Fp>\n\u003Cp>Kars has one group of three knobs, and so one page, &quot;Controls&quot;. An effect lays out differently: OpenMixer's equaliser\nputs each band on a page of its own.\u003C\u002Fp>\n\u003Ch2 id=\"5-read-on-the-device\">5. Read on the device\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Owner:\u003C\u002Fstrong> Zynthian's user interface, with the daemon. \u003Cstrong>Writes:\u003C\u002Fstrong> nothing; it reads.\u003C\u002Fp>\n\u003Cp>The interface reads the pins for its menus, the records for latency, real-time safety and cost on each row, and the layout\nfor the pages. Before an insert it asks the daemon's \u003Ccode>estimate\u003C\u002Fcode> verb what the chain will cost. Nothing is computed twice:\nthe descriptor for parameters, the record for figures, the pin for permission, the layout for pages.\u003C\u002Fp>\n\u003Cp>On the device, for Kars, the plugin menu row with its tier, latency (measured and declared frames) and real-time safety:\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"\u002Ftechnical-notes\u002Ffrom-a-plugin-file-to-a-page\u002Flist-by-tier.png\" alt=\"The plugin menu, sorted by tier\">\u003C\u002Fp>\n\u003Cp>the engine's controller page:\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"\u002Ftechnical-notes\u002Ffrom-a-plugin-file-to-a-page\u002Fcontroller-page-1-controls.png\" alt=\"The controller page of Kars\">\u003C\u002Fp>\n\u003Cp>the MOD GUI from the same layout:\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"\u002Ftechnical-notes\u002Ffrom-a-plugin-file-to-a-page\u002Fmodgui-pedalboard.png\" alt=\"The MOD GUI of Kars on a pedalboard\">\u003C\u002Fp>\n\u003Cp>and the chain with the instrument:\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"\u002Ftechnical-notes\u002Ffrom-a-plugin-file-to-a-page\u002Fchain-view-kars-zamcomp.png\" alt=\"A chain with Kars and a compressor\">\u003C\u002Fp>\n\u003Cp>A fresh device does not repeat the long stage. A package ships the records and pins of a known plugin set, keyed by\nchecksums, so a fresh device applies them and probes only what differs.\u003C\u002Fp>\n",1793954995849]