[{"data":1,"prerenderedAt":39},["ShallowReactive",2],{"project-plugin-hostd-presets":3},{"page":4,"others":23},{"slug":5,"src":6,"title":7,"source":8,"toc":9,"html":22},"presets","docs\u002Fpresets.md","Presets in the host","https:\u002F\u002Fgithub.com\u002FFreeMixer\u002Fplugin-hostd\u002Fblob\u002Fb4ea6a9\u002Fdocs\u002Fpresets.md",[10,13,16,19],{"id":11,"title":12},"who-owns-what","Who owns what",{"id":14,"title":15},"verbs","Verbs",{"id":17,"title":18},"the-file-org-openmixer-preset","The file: org.openmixer.preset",{"id":20,"title":21},"loading-a-file","Loading a file","\u003Ch1 id=\"presets-in-the-host\">Presets in the host\u003C\u002Fh1>\n\u003Cp>Presets in the host, as this tree builds it. The schema is\n\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FFreeMixer\u002Fplugin-hostd\u002Fblob\u002Fb4ea6a9\u002Fprotocol\u002Fpreset.schema.json\">protocol\u002Fpreset.schema.json\u003C\u002Fa>; \u003Ccode>tests\u002Fpreset_format.py\u003C\u002Fcode> holds it to the examples below.\u003C\u002Fp>\n\u003Ch2 id=\"who-owns-what\">Who owns what\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>The \u003Cstrong>worker\u003C\u002Fstrong> (omx-clap-host for CLAP, its LV2 adapter for LV2) holds the instance, so it owns the primitives: list\nthe plugin's own presets, load one, write the instance to a file, load a file.\u003C\u002Fli>\n\u003Cli>The \u003Cstrong>daemon\u003C\u002Fstrong> relays the verbs to the worker that holds the instance and passes its answer back; it answers -902\nonly while the worker does. Before a file reaches a worker it reads it and refuses one that is not a preset file of\na version it knows (\u003Ccode>PRESET_INVALID\u003C\u002Fcode>), so no worker guesses at a format.\u003C\u002Fli>\n\u003Cli>The \u003Cstrong>consumer\u003C\u002Fstrong> (the console, the Zynthian engine) owns the library: where files live, the user's presets, rename,\ndelete, banks. The host never decides a path or a name: both arrive as arguments.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>MIDI learn is policy of the surfaces: the host answers \u003Ccode>midi_learn\u003C\u002Fcode>, \u003Ccode>midi_map\u003C\u002Fcode>, \u003Ccode>midi_unmap\u003C\u002Fcode>, \u003Ccode>cv_map\u003C\u002Fcode> and \u003Ccode>cv_unmap\u003C\u002Fcode>\nonly as the mechanism, and the host verb table keeps them optional for every profile.\u003C\u002Fp>\n\u003Ch2 id=\"verbs\">Verbs\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>verb\u003C\u002Fth>\n\u003Cth>arguments\u003C\u002Fth>\n\u003Cth>reply\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preset_list\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>&lt;instance&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>resp &lt;count&gt;\u003C\u002Fcode>, then \u003Ccode>preset_get &lt;instance&gt; &lt;index&gt;\u003C\u002Fcode> one row each\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preset_load\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>&lt;instance&gt; &lt;stable name | file path&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>resp 0\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preset_save\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>&lt;instance&gt; &lt;file path&gt; &lt;name&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>resp 0\u003C\u002Fcode>: the worker wrote the file\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preset_show\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>&lt;instance&gt; &lt;stable name | file path&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>resp 0 &lt;json&gt;\u003C\u002Fcode>: what a load would apply, on one line of JSON (below)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>The reply of \u003Ccode>preset_show\u003C\u002Fcode> is \u003Ccode>resp 0 &lt;json&gt;\u003C\u002Fcode>, one shape on every worker and through the daemon, which passes the worker's\nline on unchanged: the rest of the line after \u003Ccode>resp 0 \u003C\u002Fcode> is one JSON object on one line. For a file it is the document with\neach state body's \u003Ccode>bytes\u003C\u002Fcode> replaced by its decoded \u003Ccode>size\u003C\u002Fcode>; for a listed preset (a stable name) it holds \u003Ccode>name\u003C\u002Fcode>, \u003Ccode>loadKey\u003C\u002Fcode>\nand \u003Ccode>location\u003C\u002Fcode>. It is not \u003Ccode>resp 0 &lt;carries&gt; &lt;name&gt;\u003C\u002Fcode>: \u003Ccode>carries\u003C\u002Fcode> and \u003Ccode>name\u003C\u002Fcode> are fields of that object.\u003C\u002Fp>\n\u003Cp>A file path is absolute (it starts with \u003Ccode>\u002F\u003C\u002Fcode>); any other word is a stable name of the plugin's own presets, the\n\u003Ccode>load_key\u003C\u002Fcode> of \u003Ccode>preset_get\u003C\u002Fcode>. The name of \u003Ccode>preset_save\u003C\u002Fcode> is the rest of the line, quoted when it holds a space.\n\u003Ccode>preset_show\u003C\u002Fcode> has two forms, told apart by their number of words: \u003Ccode>preset_show &lt;instance&gt; &lt;key&gt;\u003C\u002Fcode> goes to the worker\nthat holds the instance alone, and \u003Ccode>preset_show &lt;uri&gt;\u003C\u002Fcode>, mod-host's form, goes to every worker and is answered as\nmod-host answers it, by the LV2 worker: the preset's state, with no \u003Ccode>resp\u003C\u002Fcode> before it (the first answer of the workers\nwhen no LV2 worker of mod-host's is up).\n\u003Ccode>bundle_add\u003C\u002Fcode> and \u003Ccode>bundle_remove\u003C\u002Fcode> stay as declared.\u003C\u002Fp>\n\u003Ch3 id=\"refusals\">Refusals\u003C\u002Fh3>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>name\u003C\u002Fth>\n\u003Cth>who\u003C\u002Fth>\n\u003Cth>when\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>-523\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PRESET_NOT_OFFERED\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>worker\u003C\u002Ftd>\n\u003Ctd>a stable name the plugin does not offer, or a plugin with no preset system and no state\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>-524\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PRESET_INVALID\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>daemon, worker\u003C\u002Ftd>\n\u003Ctd>a path that is not absolute, a \u003Ccode>preset_save\u003C\u002Fcode> with no name, a file that cannot be read, is no JSON, is of an unknown format version, or breaks the schema\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>-525\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PRESET_NEEDS_STATE\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>worker\u003C\u002Ftd>\n\u003Ctd>a file that carries state for a worker that cannot take state yet; it disappears when every worker takes state\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>-526\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PRESET_PLUGIN_CHANGED\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>worker\u003C\u002Ftd>\n\u003Ctd>the file's plugin id, version or fingerprint is not the instance's\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>-527\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>LOAD_REFUSED\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>worker\u003C\u002Ftd>\n\u003Ctd>the plugin refused the state or the preset (its load callback failed)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"the-file-org-openmixer-preset\">The file: org.openmixer.preset\u003C\u002Fh2>\n\u003Cp>The console's format, extended additively. \u003Ccode>\u002F1\u003C\u002Fcode> stays valid and is params-only: our own plugins and their factory\npresets never change. A file with a state body is \u003Ccode>\u002F2\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>field\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>meaning\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>format\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>org.openmixer.preset\u002F1\u003C\u002Fcode> or \u003Ccode>org.openmixer.preset\u002F2\u003C\u002Fcode>; any other is refused, never guessed\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>plugin.id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required\u003C\u002Ftd>\n\u003Ctd>the plugin's id: the CLAP id, or the LV2 uri\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>plugin.version\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required in \u002F2\u003C\u002Ftd>\n\u003Ctd>the plugin's version as its descriptor says\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>plugin.fingerprint\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required in \u002F2\u003C\u002Ftd>\n\u003Ctd>the descriptor fingerprint: the plugin's layout pin, \u003Ccode>omx-layout\u002F1:&lt;sha256&gt;\u003C\u002Fcode>; a fingerprint of a scheme this version does not know is refused\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required\u003C\u002Ftd>\n\u003Ctd>stable, minted by the consumer; a worker writing a file names it after the file (its name without \u003Ccode>.json\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>name\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required\u003C\u002Ftd>\n\u003Ctd>display, free text, editable on user presets only\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>alias\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>optional\u003C\u002Ftd>\n\u003Ctd>a short label, at most 12 characters, for small displays (a scribble strip); absent means derive from \u003Ccode>name\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>bank\u003C\u002Fcode>, \u003Ccode>tags\u003C\u002Fcode>, \u003Ccode>description\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>optional\u003C\u002Ftd>\n\u003Ctd>the consumer's\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>createdAt\u003C\u002Fcode>, \u003Ccode>modifiedAt\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>optional\u003C\u002Ftd>\n\u003Ctd>ISO 8601 UTC, \u003Ccode>YYYY-MM-DDTHH:MM:SSZ\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>carries\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>required in \u002F2\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>params\u003C\u002Fcode>, \u003Ccode>params+state\u003C\u002Fcode> or \u003Ccode>state\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>params\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>[{symbol, unit, value}]\u003C\u002Fcode>: plain values, written as the plugin gave them, never a float widened\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>state\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{encoding: &quot;base64&quot;, context, bytes, files?}\u003C\u002Fcode>; \u003Ccode>context\u003C\u002Fcode> is \u003Ccode>preset\u003C\u002Fcode> or \u003Ccode>project\u003C\u002Fcode> for CLAP (the clap.state context), \u003Ccode>lv2\u003C\u002Fcode> for LV2; \u003Ccode>files\u003C\u002Fcode> \u003Ccode>[{name, encoding, bytes}]\u003C\u002Fcode> are LV2 state paths, a file name each\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>The writer picks \u003Ccode>carries\u003C\u002Fcode>: \u003Ccode>params\u003C\u002Fcode> when the plugin has only declared parameters, otherwise \u003Ccode>state\u003C\u002Fcode> (\u003Ccode>params+state\u003C\u002Fcode>\nwhen both are read). \u003Ccode>carries\u003C\u002Fcode> and the body agree: \u003Ccode>params\u003C\u002Fcode> has \u003Ccode>params\u003C\u002Fcode> and no \u003Ccode>state\u003C\u002Fcode>, \u003Ccode>params+state\u003C\u002Fcode> both, \u003Ccode>state\u003C\u002Fcode>\na \u003Ccode>state\u003C\u002Fcode>. Unknown fields are kept on rewrite.\u003C\u002Fp>\n\u003Cp>A params-only file of \u002F2:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{&quot;format&quot;: &quot;org.openmixer.preset\u002F2&quot;,\n &quot;plugin&quot;: {&quot;id&quot;: &quot;org.freemixer.comp&quot;, &quot;version&quot;: &quot;1.2.0&quot;,\n            &quot;fingerprint&quot;: &quot;omx-layout\u002F1:0000000000000000000000000000000000000000000000000000000000000000&quot;},\n &quot;id&quot;: &quot;warm&quot;, &quot;name&quot;: &quot;Warm Pad&quot;, &quot;createdAt&quot;: &quot;2026-10-22T21:00:00Z&quot;, &quot;modifiedAt&quot;: &quot;2026-10-22T21:00:00Z&quot;,\n &quot;carries&quot;: &quot;params&quot;, &quot;params&quot;: [{&quot;symbol&quot;: &quot;gain&quot;, &quot;unit&quot;: &quot;dB&quot;, &quot;value&quot;: -3.25}]}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>A state file adds \u003Ccode>&quot;carries&quot;: &quot;params+state&quot;\u003C\u002Fcode> and\n\u003Ccode>&quot;state&quot;: {&quot;encoding&quot;: &quot;base64&quot;, &quot;context&quot;: &quot;preset&quot;, &quot;bytes&quot;: &quot;AAEC&quot;}\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch2 id=\"loading-a-file\">Loading a file\u003C\u002Fh2>\n\u003Col>\n\u003Cli>The daemon reads the file and checks it against the format above (\u003Ccode>PRESET_INVALID\u003C\u002Fcode> when it does not hold); no\nworker is asked.\u003C\u002Fli>\n\u003Cli>The worker compares \u003Ccode>plugin\u003C\u002Fcode> with the instance: another id, version or fingerprint answers \u003Ccode>PRESET_PLUGIN_CHANGED\u003C\u002Fcode>\nand nothing is applied.\u003C\u002Fli>\n\u003Cli>The worker applies the state body when the file carries one, then the parameter values, each clamped to its\ndeclared range, an unknown symbol ignored, as clap.state's load does.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>Parameter feedback (the \u003Ccode>param_set\u003C\u002Fcode> event a worker writes for a value the plugin moved by itself, a preset load\nincluded) is the declared event of the protocol's JSON (plugin-hostd-protocol's \u003Ccode>plugin-hostd.json\u003C\u002Fcode>); a controller's own writes produce none.\u003C\u002Fp>\n",[24,27,30,33,36],{"slug":25,"title":26},"","README.md",{"slug":28,"title":29},"formats","docs\u002Fformats.md",{"slug":31,"title":32},"own-host","the highest SCHED_FIFO priority, which the probe asks for, and a lock as large as the probe's memory limit",{"slug":34,"title":35},"panel","docs\u002Fpanel.md",{"slug":37,"title":38},"placement","docs\u002Fplacement.md",1793924849809]