[{"data":1,"prerenderedAt":33},["ShallowReactive",2],{"note-could-the-host-take-vst3-en":3},{"slug":4,"order":5,"title":6,"summary":7,"measured":8,"topic":9,"toc":10,"html":32},"could-the-host-take-vst3",110,"\"Could the host take VST3? The adapter seam and its cost\"","A VST3 adapter is possible in plain C against Steinberg's C API, under licences a GPL host can use, and a spike hosted a real VST3 effect end to end. It would cost about 6200 lines and three weeks, for a thin demand, so it is not being built; what would change that is written down.","omx-clap-core's LV2 adapter, the VST 3 SDK 3.8.1 and its C API on Fedora 44, and a survey of plugin vendors' pages, October 2026.","core",[11,14,17,20,23,26,29],{"id":12,"title":13},"the-answer-in-numbers","The answer in numbers",{"id":15,"title":16},"the-seam","The seam",{"id":18,"title":19},"the-sdk","The SDK",{"id":21,"title":22},"the-spike","The spike",{"id":24,"title":25},"the-test-plan-if-it-is-built","The test plan, if it is built",{"id":27,"title":28},"could-it-be-hosted","Could it be hosted",{"id":30,"title":31},"why-not-now-and-what-would-change-it","Why not now, and what would change it","\u003Ch2 id=\"the-answer-in-numbers\">The answer in numbers\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>The LV2 adapter and its plumbing hold 5754 lines of code in 336 functions: 1594 lines (139 functions) format-neutral,\n1699 (73) mixed, which is neutral logic around a few format calls, and 2461 (124) specific to LV2.\u003C\u002Fli>\n\u003Cli>The plumbing is already shared: all six library headers the adapter includes are neutral, and four are already used\noutside it.\u003C\u002Fli>\n\u003Cli>The seam still to cut is the shim that presents an LV2 plugin as a CLAP plugin: 1314 of its 1319 lines are neutral or\nmixed, and 1161 of them would serve a VST3 shim. The LV2 core is two-thirds specific; its neutral code (the worker loop,\npath containment, atom helpers, a real-time memory pool) serves LV2 ideas VST3 does not have.\u003C\u002Fli>\n\u003Cli>On the audio thread the shim adds one indirect call per cycle and one per MIDI event out; every other indirect call there\nis the CLAP host's or the plugin's own. A shared layer must add none.\u003C\u002Fli>\n\u003Cli>A VST3 adapter needs no C++: Steinberg's generated C API works, and a spike of 477 lines of C11 hosted a VST3 effect end\nto end. The SDK is MIT-licensed and the C API BSD-3-Clause, both usable in a GPL-3.0-or-later host.\u003C\u002Fli>\n\u003Cli>The cost: about 2200 lines on the format side, about 6200 with tests and fixtures; about three weeks of work with the seam\ncut first, two and a half without.\u003C\u002Fli>\n\u003Cli>The demand is thin: 64 open-source plugin packages in the distribution repositories are VST3-only on Linux, none of them\none OpenMixer depends on, and 6 closed-source vendors ship Linux VST3 without CLAP or LV2.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>So: not now. The results are kept, so that a later adapter starts from them.\u003C\u002Fp>\n\u003Ch2 id=\"the-seam\">The seam\u003C\u002Fh2>\n\u003Cp>Each function of the adapter was read and given a class: \u003Cstrong>neutral\u003C\u002Fstrong> (a VST3 adapter would use it unchanged: it depends only\non CLAP, the contract, the ring, the limits or plain C), \u003Cstrong>mixed\u003C\u002Fstrong> (neutral logic around a few format calls, each a hook the\nformat side would supply) or \u003Cstrong>specific\u003C\u002Fstrong> (LV2 only, including code where only the idea is shared). A tool in omx-clap-core\njoins the classification with lizard's list, fails on a function the table does not class, and prints the sums.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>part\u003C\u002Fth>\n\u003Cth style=\"text-align:right\">neutral\u003C\u002Fth>\n\u003Cth style=\"text-align:right\">mixed\u003C\u002Fth>\n\u003Cth style=\"text-align:right\">specific\u003C\u002Fth>\n\u003Cth style=\"text-align:right\">total\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>the shim (LV2 as CLAP)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">347 (46)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">967 (44)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">5 (1)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">1319 (91)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>LV2 features\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">30 (3)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">30 (1)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">449 (31)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">509 (35)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>LV2 instance\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">275 (29)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">501 (22)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">923 (44)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">1699 (95)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>real-time memory pool\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">169 (12)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">0 (0)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">12 (3)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">181 (15)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>LV2 world (descriptions through lilv)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">48 (6)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">201 (6)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">1072 (45)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">1321 (57)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>the shared library headers\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">725 (43)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">0 (0)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">0 (0)\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">725 (43)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>all\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">\u003Cstrong>1594 (139)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">\u003Cstrong>1699 (73)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">\u003Cstrong>2461 (124)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">\u003Cstrong>5754 (336)\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>Each cell is lines of code (functions).\u003C\u002Fp>\n\u003Cp>When a second adapter is approved, the shim splits in two: the 1161 reusable lines go to a shared shim inside omx-clap-core\n(its audio-thread half \u003Ccode>static inline\u003C\u002Fcode> in a header, its main-thread half in an internal static library, no public ABI), and\nthe LV2 remainder stays behind about 25 hooks:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>group\u003C\u002Fth>\n\u003Cth>hooks\u003C\u002Fth>\n\u003Cth>VST3 counterpart\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>lifecycle\u003C\u002Ftd>\n\u003Ctd>load, free, activate or keep at a rate and block range, deactivate, reset, latency\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>createInstance\u003C\u002Fcode> and \u003Ccode>initialize\u003C\u002Fcode>, \u003Ccode>terminate\u003C\u002Fcode>, \u003Ccode>setupProcessing\u003C\u002Fcode> and \u003Ccode>setActive\u003C\u002Fcode>, \u003Ccode>setProcessing\u003C\u002Fcode>, \u003Ccode>getLatencySamples\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>parameters\u003C\u002Ftd>\n\u003Ctd>count, info (range, flags, steps, unit), get and set, value to text and back, symbol\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>getParameterCount\u002FInfo\u003C\u002Fcode>, normalised and plain values, \u003Ccode>getParamStringByValue\u002FValueByString\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>ports\u003C\u002Ftd>\n\u003Ctd>a port table (kind, width, CLAP channel map, main or aux), buffer in and out per leg and channel\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>getBusInfo\u003C\u002Fcode>, \u003Ccode>SpeakerArrangement\u003C\u002Fcode>, \u003Ccode>AudioBusBuffers\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>process\u003C\u002Ftd>\n\u003Ctd>run, MIDI in, MIDI out, transport, counters\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>process(ProcessData)\u003C\u002Fcode>, \u003Ccode>IEventList\u003C\u002Fcode>, \u003Ccode>ProcessContext\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>state and presets\u003C\u002Ftd>\n\u003Ctd>save to a blob, load from one, list presets, load one\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>getState\u002FsetState\u003C\u002Fcode> (component and controller), \u003Ccode>.vstpreset\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>discovery\u003C\u002Ftd>\n\u003Ctd>open and close a bundle, list its plugins, refuse before opening\u003C\u002Ftd>\n\u003Ctd>the bundle scan, \u003Ccode>IPluginFactory\u003C\u002Fcode>, \u003Ccode>moduleinfo.json\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>VST3 needs four things LV2 never asked for: the controller and the processor are split, with messages between them;\nparameter changes flow back from the processor to the controller; \u003Ccode>restartComponent\u003C\u002Fcode> flags must become CLAP requests; and\nstate is two blobs, not one. That is format-side work and does not widen the interface.\u003C\u002Fp>\n\u003Cp>The rule for the shared layer: its audio-thread half is compiled into each format's shim, and the format hooks are direct\ncalls bound when it is compiled, with no vtable. The test is an objdump count: no indirect call on the audio path beyond the\nCLAP host's and the plugin's own.\u003C\u002Fp>\n\u003Cp>No other OpenMixer library holds code that would replace the shared layer: the DSP library has a crossfade the host already\nuses and no ring, worker or parameter table; plugin-hostd dispatches formats through a prefix table; the plugins' library\nmirrors the parameter-contract vocabulary.\u003C\u002Fp>\n\u003Ch2 id=\"the-sdk\">The SDK\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Licence.\u003C\u002Fstrong> The VST 3 SDK 3.8.1 is MIT-licensed; its README says licensing under GPLv3 and the Steinberg proprietary licence\nis no longer offered, and that &quot;VST&quot; may not be used in company or product names. MIT code combines into a\nGPL-3.0-or-later work; the one duty is to keep the notice with any SDK file shipped.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Language.\u003C\u002Fstrong> The SDK is C++ with COM-style interfaces, and its hosting helpers link libstdc++. Steinberg also publishes a C\nAPI generated from those interfaces, under the BSD 3-clause licence: every interface a struct with a vtable, every id a\nconstant. A VST3 adapter can therefore be C like the rest of the library. The price is writing the host's COM objects by\nhand: 257 lines in the spike.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>What a host implements\u003C\u002Fstrong>, estimated from the spike and from the LV2 code doing the same job:\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>task\u003C\u002Fth>\n\u003Cth>what VST3 needs from the host\u003C\u002Fth>\n\u003Cth style=\"text-align:right\">lines (estimate)\u003C\u002Fth>\n\u003Cth>reused from the seam\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>instantiate and connect a stereo effect\u003C\u002Ftd>\n\u003Ctd>scan \u003Ccode>.vst3\u003C\u002Fcode> bundles; the module entry and plugin factory; initialize the component with a host application object that also allocates messages and attribute lists; the controller; a connection point both ways; buses; the setup and activation sequence; \u003Ccode>process\u003C\u002Fcode> with bus buffers\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">650\u003C\u002Ftd>\n\u003Ctd>the CLAP face, the port table, the leg copies, the refusal record\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>a MIDI instrument\u003C\u002Ftd>\n\u003Ctd>no raw MIDI input: notes arrive as events in a list the host implements and preallocates; controllers, pitch bend and aftertouch become parameter changes through a MIDI mapping\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">300\u003C\u002Ftd>\n\u003Ctd>the note ports and MIDI helpers\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>threading\u003C\u002Ftd>\n\u003Ctd>no worker: each call has its thread; edits from the controller arrive on the main thread and must reach the next \u003Ccode>process\u003C\u002Fcode> as parameter queues; output changes travel back\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">400\u003C\u002Ftd>\n\u003Ctd>the message ring, the parameter flush, the latency watch\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>state\u003C\u002Ftd>\n\u003Ctd>a byte stream object; one CLAP state holds two blobs, component and controller; restore only after \u003Ccode>setupProcessing\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">200\u003C\u002Ftd>\n\u003Ctd>the state reader and the held restore\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>a rate change\u003C\u002Ftd>\n\u003Ctd>deactivate, set up with the new rate and block, activate: no re-instantiation\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">60\u003C\u002Ftd>\n\u003Ctd>the activate and keep rule\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>aux and sidechain buses\u003C\u002Ftd>\n\u003Ctd>aux buses after the main ones; speaker arrangements map onto CLAP port types and channel maps\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">200\u003C\u002Ftd>\n\u003Ctd>the port table, the surround and ambisonic extensions\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>parameters\u003C\u002Ftd>\n\u003Ctd>count and info with bypass and read-only flags, normalised doubles, text conversion, sample-accurate queues\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">250\u003C\u002Ftd>\n\u003Ctd>the parameter face and the contract\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>discovery and refusals\u003C\u002Ftd>\n\u003Ctd>a code per refusal, and a scan that need not open the binary when \u003Ccode>moduleinfo.json\u003C\u002Fcode> is there\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">150\u003C\u002Ftd>\n\u003Ctd>the refusal vocabulary, the factory bookkeeping\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>format side, total\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003C\u002Ftd>\n\u003Ctd style=\"text-align:right\">\u003Cstrong>about 2200\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"the-spike\">The spike\u003C\u002Fh2>\n\u003Cp>A probe in C11 opened a Linux VST3 build of an LSP compressor from the distribution's package, read the factory, created the\nplugin, listed its buses and parameters, round-tripped its state, and processed one block of silence and eight of noise.\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>step\u003C\u002Fth>\n\u003Cth>result\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>module\u003C\u002Ftd>\n\u003Ctd>entry ok; 388 classes (194 audio modules, 194 controllers)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>component and controller\u003C\u002Ftd>\n\u003Ctd>a separate controller; both initialised; connected both ways\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>buses\u003C\u002Ftd>\n\u003Ctd>a stereo main input and output; the sidechain variant adds an aux input and processes with only the main bus handed over\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>parameters\u003C\u002Ftd>\n\u003Ctd>48, bypass flagged as such; names, units and default text through the text conversion\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>state\u003C\u002Ftd>\n\u003Ctd>517 bytes; set then get byte-identical\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>process\u003C\u002Ftd>\n\u003Ctd>silence in, silence out; noise in at -6.04 dBFS peak, out at -6.57\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>C++ needed\u003C\u002Ftd>\n\u003Ctd>none: C11 and the C API\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>size\u003C\u002Ftd>\n\u003Ctd>477 lines of code, 257 of them the host's COM objects\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>Three rules came out of it, each one the adapter would carry:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>The host must allocate the message objects.\u003C\u002Fstrong> The plugin asked the host application for a message during \u003Ccode>setState\u003C\u002Fcode> and\nfailed when refused; the SDK's C++ hosting library hides this, and in C it is 147 lines.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Restore state only after \u003Ccode>setupProcessing\u003C\u002Fcode>.\u003C\u002Fstrong> The specification allows \u003Ccode>setState\u003C\u002Fcode> earlier, but the plugin used its sample\nrate there, which was still zero.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>A crashed instance can poison the next.\u003C\u002Fstrong> The plugin keeps a catalogue in shared memory; a crash left that file empty, and\nevery later instance died reading it, until the file was removed. Running each plugin in a worker process of its own, as\nplugin-hostd does, contains such a crash.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"the-test-plan-if-it-is-built\">The test plan, if it is built\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>One small VST3 fixture per behaviour, written in C against the C API, as the LV2 tests do, so CI stays hermetic and every\nrefusal is exercised on purpose.\u003C\u002Fli>\n\u003Cli>The VST3 builds in the distribution repositories as real plugins.\u003C\u002Fli>\n\u003Cli>Parity: the same plugin as VST3, LV2 and CLAP through the host, fed the same seeded noise and sweep, compared sample by\nsample; a pass is a residual at least 100 dB down, or a named difference. The parameter map is compared the same way.\u003C\u002Fli>\n\u003Cli>The allocation witness around the VST3 \u003Ccode>process\u003C\u002Fcode> path.\u003C\u002Fli>\n\u003Cli>The poisoned catalogue as a regression test: a refusal with a code from the worker, never a dead engine.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"could-it-be-hosted\">Could it be hosted\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>area\u003C\u002Fth>\n\u003Cth>can it?\u003C\u002Fth>\n\u003Cth>why\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>licence\u003C\u002Ftd>\n\u003Ctd>yes\u003C\u002Ftd>\n\u003Ctd>MIT and BSD-3-Clause combine into GPL-3.0-or-later; &quot;VST&quot; may not appear in a product name\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>C++\u003C\u002Ftd>\n\u003Ctd>yes, in C\u003C\u002Ftd>\n\u003Ctd>the C API; the spike is C11 with no libstdc++\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>real-time rules\u003C\u002Ftd>\n\u003Ctd>yes\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>process\u003C\u002Fcode> runs on the processing thread; the host preallocates its parameter and event lists\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>threading\u003C\u002Ftd>\n\u003Ctd>yes\u003C\u002Ftd>\n\u003Ctd>the &quot;UI thread&quot; calls map onto the host's main thread; there is no worker\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>headless\u003C\u002Ftd>\n\u003Ctd>yes for the plugin tested, not for every plugin\u003C\u002Ftd>\n\u003Ctd>the spike ran headless; Steinberg's own validator failed in the same headless container\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>closed-source activation\u003C\u002Ftd>\n\u003Ctd>depends on the vendor\u003C\u002Ftd>\n\u003Ctd>serial or key-file activation works headless; activation inside the plugin's editor needs a desktop session once; iLok has no Linux client\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>crash containment\u003C\u002Ftd>\n\u003Ctd>yes\u003C\u002Ftd>\n\u003Ctd>plugin-hostd runs each plugin in a worker process of its own\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>Of 14 closed-source vendors that ship Linux VST3 builds (a survey of the vendors' own pages, made for this note), 8 also ship CLAP\nor LV2, which the host already takes. The 6 that are VST3-only on Linux are ToneBoosters, Loomer, Renoise (Redux), Tracktion\n(BioTek 3, F'em), Bertom Audio and DDMF.\u003C\u002Fp>\n\u003Ch2 id=\"why-not-now-and-what-would-change-it\">Why not now, and what would change it\u003C\u002Fh2>\n\u003Cp>The demand is thin, the cost is about 6200 lines, and writing the adapter would confirm the seam drawn above rather than\ndiscover a new one: every VST3 task maps onto an existing group of shim functions. Meanwhile new code in the shim keeps its\nneutral functions free of LV2 calls, so the seam stays cheap to cut.\u003C\u002Fp>\n\u003Cp>Any one of these reopens the decision:\u003C\u002Fp>\n\u003Col>\n\u003Cli>A named VST3-only plugin worth hosting: a Linux x86-64 build, no LV2 or CLAP build, activation that works without its\neditor, and terms that allow a third-party host. Three such requests make the case; one is enough if it fills a role\nnothing hosted fills.\u003C\u002Fli>\n\u003Cli>A second non-CLAP adapter approved for another reason: then the seam is cut first, whatever the format.\u003C\u002Fli>\n\u003Cli>A vendor that ships both formats today dropping CLAP and LV2 on Linux for a plugin that is hosted.\u003C\u002Fli>\n\u003C\u002Fol>\n",1793954995885]