CLAP: The New Audio Plug-in Standard (by U-he, Bitwig and others)
- KVRAF
- 24411 posts since 7 Jan, 2009 from Croatia
There are some points mentioned in the . There's only one single event stream for everything rather than multiple different ones (as in VST3), there's native support for raw MIDI (not available in VST3), the API is extremely straightforward and simple (VST3 is heavily overengineered and way too tied to COM design and C++, so forces you to use C++, with CLAP you can bind it to pretty much any programming language) etc etc.
- KVRist
- 211 posts since 3 Jan, 2021
What's that?
- KVRian
- 1313 posts since 31 Dec, 2008
Component Object Model. If you care about the details: https://docs.microsoft.com/en-us/window ... ion-to-com
www.solostuff.net
The 3rd law of thermo-dynamics states that: the 2nd law has two meanings, one of them is strictly wrong, the other is massively misunderstood.
The 3rd law of thermo-dynamics states that: the 2nd law has two meanings, one of them is strictly wrong, the other is massively misunderstood.
- u-he
- 30192 posts since 8 Aug, 2002 from Berlin
Here are just two:soundmodel wrote: Thu Jul 07, 2022 8:59 am Any chance someone could produce a list of central points where VST2/VST3 design falls short, and how CLAP improves on these?
Threading
If you look at CLAP headers, it is clearly documented which call can be done on which thread. A CLAP plug-in can ask the host which thread its on at any time. A plug-in con be compiled with an option for automatic thread checking of every single call, from host or plug-in. This avoids crash source No. 1 that I experienced in "plug-in API interpretation".
Events
In CLAP every event arrives in one unified queue. So if a parameter change and a note on happen at the same timestamp, their order is clear. If they do not come in through a single queue, their order would be ambiguous. CLAP avoids that ambiguity.
I wholeheartedly agree. It's intuitively obvious that a single entity can not create the best standard that serves an ecosystem of thousands of companies. It's obvious that a collaboration between dozens of companies and projects would have quite a few advantages there.
- KVRAF
- 8476 posts since 12 Feb, 2006 from Helsinki, Finland
COM = Component Object Model, but since the technical docs on the subject might be quite confusing, let me try to give a high-level overview.
Basically you have two pieces of software, say the host and the plugin. The host wants to create objects or "components" (eg. plugin instances) implemented by the plugin and the two need to know how to call various interfaces in the other software and also query whether certain interfaces are available in the first place.
There are various ways to solve this "interface problem" where VST2 for example essentially enumerates all the different operations and calls a "dispatcher" function (handed over as a function pointer) where you then look at the "opcode" (or whatever it's called) parameter to decide which operation is requested. This is very simple, but has the downside that the list of operations is centralized (especially when allocated in order: 0, 1, 2, 3 ..) and not all that flexible.
A step up from this is basically how CLAP solves the issue: for each interface, you make a list of function pointers (=struct in C) and let the host and the plugin query for a different struct for every extension (= different interface). This way you can easily add additional interfaces as there is no longer a centralized point through which all calls must go through and we get to call the different methods on the interfaces directly.
COM is essentially a similar thing, except it leverages the C++ compiler machinery. Object oriented programming languages generally have a concept of interfaces already and in case of C++ this would be a class with nothing but pure virtual methods; such an interface specifies that any object implementing that interface must implement certain methods, but does not actually provide any implementation itself.
Now, the interesting part is that the way C++ compilers typically implement "virtual" inheritance (ie. methods that can be overriden by a derived class) is by generating what is known as "vtable" that is essentially a list of function pointers. This "vtable" is essentially very similar to the structs that specify function pointers for some extensions in a CLAP-like design, except for the fact that it's compiler generated and not directly accessible to C++ code.
So COM essentially adds a bunch of "magic" around this system so that you have a standard interface (IQuery) which can be used to query for other interfaces using GUIDs to pass around the vtables and allows COM objects to behave more or less like regular C++ objects as long as you're using C++. There's a bunch of arcane stuff involved, but when you're using a good COM API like say DirectX it pretty much feels like you were dealing with regular C++ objects, even though under the hood it's going through the COM machinery to make sure that the binary interface is stable.
I personally feel like COM gets a lot of undeserved hate (especially given that C++ is the most commonly used language here) and the real problems of VST3 are elsewhere, but arguably using the "magic machinery" from a language like C is kinda annoying, because there is a bunch of boilerplate involved (more so than just writing something similar to vtable directly, like what you end up doing with CLAP). At the end of the day, the technical solution is not that far from how CLAP does it, just more integrated into C++ specifically and a lot more general (and generality adds some complexity) so it scales to more situations.
- KVRist
- 211 posts since 3 Jan, 2021
@S0lo thnx for the link
@mystran thnx for the context
A simple search for "COM design" resulted in nothing. this helps.
On the face of it this type of design doesn't necessarily seems bad, but tying the architecture of an API so heavily with a specific programming language and/or framework of course makes it very tedious to use.
@mystran thnx for the context
A simple search for "COM design" resulted in nothing. this helps.
On the face of it this type of design doesn't necessarily seems bad, but tying the architecture of an API so heavily with a specific programming language and/or framework of course makes it very tedious to use.
-
- KVRian
- 1213 posts since 25 Dec, 2018
What an excellent summary @mystran
COM is also heavily associated with the Microsoft tech of the early 2000s where this object narrowing pattern and a distributed object narrowing pattern (DCOM) were how Microsoft desktop programs implemented many features. Object linking and embedding (OLE) relied heavily on com.
Other languages had similar things. Enterprise Java beans (EJB) was sort of similar but relied on the jvm. CORBA was language independent in that you wrote your interface in a new specification only language (the IDL) and then use their specific toolchain to generate language specific bindings to the object runtime.
In the end I think most folks think these efforts didn’t succeed because they tie you to big and specific object hierarchies and associated toolings. The usage of funknown and the out-of-language dynamic cast in vst3 is one such consequence. Is it the end of the world? No.
But also these are big solutions to relatively specific problems. The way clap solved it is to define an extension as a bundle of functions and a name. The shared entry point is “do you have the bundle of functions for this name”. That strips generality - but not any generality we have found a need for - and enables a collection of languages other than c++, as evidenced by plugins already being developed in rust and hosts appearing in rust, Delphi and other languages already.
COM is also heavily associated with the Microsoft tech of the early 2000s where this object narrowing pattern and a distributed object narrowing pattern (DCOM) were how Microsoft desktop programs implemented many features. Object linking and embedding (OLE) relied heavily on com.
Other languages had similar things. Enterprise Java beans (EJB) was sort of similar but relied on the jvm. CORBA was language independent in that you wrote your interface in a new specification only language (the IDL) and then use their specific toolchain to generate language specific bindings to the object runtime.
In the end I think most folks think these efforts didn’t succeed because they tie you to big and specific object hierarchies and associated toolings. The usage of funknown and the out-of-language dynamic cast in vst3 is one such consequence. Is it the end of the world? No.
But also these are big solutions to relatively specific problems. The way clap solved it is to define an extension as a bundle of functions and a name. The shared entry point is “do you have the bundle of functions for this name”. That strips generality - but not any generality we have found a need for - and enables a collection of languages other than c++, as evidenced by plugins already being developed in rust and hosts appearing in rust, Delphi and other languages already.
- KVRAF
- 8476 posts since 12 Feb, 2006 from Helsinki, Finland
Well, distributed RPC systems are a whole other can of worms, because when your "method call" goes over the network you need to both serialize the data (preferably a bit better and faster than CORBA; I still have nightmares from having had to use it years ago) and then be prepared for all kinds of weird partial failures that are not particularly great fit for the "function call" paradigm. In fact, even RPC from one process to another on a single system opens a number of questions that just aren't relevant when you're doing a regular local function call.baconpaul wrote: Thu Jul 07, 2022 3:16 pm Other languages had similar things. Enterprise Java beans (EJB) was sort of similar but relied on the jvm. CORBA was language independent in that you wrote your interface in a new specification only language (the IDL) and then use their specific toolchain to generate language specific bindings to the object runtime.
In a sense, once you accept that you're not doing a simple local function call, but need to prepare for things like timeouts and retries everywhere, it typically ends up being easier to just build an explicit message passing architecture, which is why these days you usually do HTTP queries, preferably using REST principles.
This isn't very relevant for basic COM in a single process though, 'cos in this case any function calls are really just about as fancy as a regular virtual function call.
-
- KVRian
- 1213 posts since 25 Dec, 2018
Yeah those were ugly days. Indeed! Anyway enough history - the point was more that some apis are very dynamic type object oriented and some are less so and that was really evils comment
-
Jeff McClintock Jeff McClintock https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=56398
- KVRist
- 432 posts since 30 Jan, 2005 from New Zealand
Agree, A simplistic explanation of COM is:mystran wrote: Thu Jul 07, 2022 10:55 am
A step up from this is basically how CLAP solves the issue: for each interface, you make a list of function pointers (=struct in C) and let the host and the plugin query for a different struct for every extension (= different interface).
COM is essentially a similar thing, except it leverages the C++ compiler machinery.
* your plugin is represented by a list of 'C' function pointers.
* you can 'query' the plugin for any specific 'interface' (aka 'extension')
CLAP is basically a variation on COM. The difference is:
* with CLAP you query an extension by calling get_extension() with a string describing what you want.
* with COM you query an extension by calling QueryInterface() with a number describing what you want.
Both return a list of C function pointers that do the stuff you asked for.
The other difference is: In COM, that list of function pointers can be cast directly to a C++ compatible object, in CLAP you have to write an extra layer, aka 'helper' to perform the translation to C++.
The C++ 'object' presented by COM has the lowest runtime overhead when calling into it. CLAP has an extra layer or two of overhead. Which is a missed opportunity I think considering what a tiny alteration it would have taken to fix CLAP.
One might argue that CLAP has reinvented COM, but without the benefit of easy C++ compatibility.
-
- KVRist
- 36 posts since 24 Jun, 2022
I don't think I agree here. Some important parts that are left out here is that with the COM model every object has its own query interface, and every object also has its own lifetime management through reference counting. So if you get a pointer as an argument for one of your methods, you can increment its reference count and store it for later (making sure to release that pointer later, or of course use a RAII helper to do all of this for you). And if you get a pointer as the result of a method call, you adopt it without increasing its reference count. As you can probably imagine, there are many things here that can go wrong. And finally, the memory layout is not coupled to C++ structs. This makes consuming these interfaces from languages that aren't C++ much simpler.Jeff McClintock wrote: Fri Jul 08, 2022 12:58 am Agree, A simplistic explanation of COM is:
* your plugin is represented by a list of 'C' function pointers.
* you can 'query' the plugin for any specific 'interface' (aka 'extension')
CLAP is basically a variation on COM. The difference is:
* with CLAP you query an extension by calling get_extension() with a string describing what you want.
* with COM you query an extension by calling QueryInterface() with a number describing what you want.
Both return a list of C function pointers that do the stuff you asked for.
The other difference is: In COM, that list of function pointers can be cast directly to a C++ compatible object, in CLAP you have to write an extra layer, aka 'helper' to perform the translation to C++.
One might argue that CLAP has reinvented COM, but without the benefit of easy C++ compatibility.
In contrast, with CLAP only the main clap_plugin and clap_host objects have query interfaces. And there is no lifetime management beyond creating a plugin instance and destroying it. In fact, on the plugin side there is zero lifetime management for host resources at all. And because CLAP uses simple, flat C structs it's easy to consume in any language with a C FFI.
-
Jeff McClintock Jeff McClintock https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=56398
- KVRist
- 432 posts since 30 Jan, 2005 from New Zealand
Yeah, that could be an advantage, not having to manage the reference count of an interface.robbert-vdh wrote: Fri Jul 08, 2022 1:07 am In contrast, with CLAP only the main clap_plugin and clap_host objects have query interfaces. And there is no lifetime management beyond creating a plugin instance and destroying it. In fact, on the plugin side there is zero lifetime management for host resources at all.
However, coders manage reference counts all the time, via smart pointers (e.g. std::shared_ptr). And smart pointers exist for COM too. So perhaps it's not such a big deal.
Yeah, this is a common misconception about COM.robbert-vdh wrote: Fri Jul 08, 2022 1:07 am And finally (with CLAP), the memory layout is not coupled to C++ structs. This makes consuming these interfaces from languages that aren't C++ much simpler...And because CLAP uses simple, flat C structs it's easy to consume in any language with a C FFI.
COM is a C API. COM uses simple, flat C structs. This C FFI is why COM can be consumed from pretty much any language.
The confusion around COM stems from the fact that those simple flat structs happen to mimic the layout of a C++ objects vtable. This is where people get all confused about 'magic' and 'complication'. a vtable is a struct containing the object's virtual function pointers. That's all.
As an aside: You don't have to use the 'c++ magic' if you don't want to. It is quite common for software to deal with COM via those simple C structs, e.g. by writing a 'helper' that translates it to the target language. exactly like CLAP does. CLAP is not adding anything here.
I think people's misunderstanding about what COM really is (a plain C API), has resulted in the developers of CLAP reinventing almost the same thing. CLAP's extension mechanism is not simpler (or harder, to be fair). It's practically the same thing, reinvented.
-
- KVRian
- 1213 posts since 25 Dec, 2018
Hmm Jeff of course you are not wrong about com, but this conversation began when evil short handed the vst3 object model (funknown, query interface, etc) as “com” (which it isn’t) as opposed to “com-like” (which in the object not function bundle sense it is).
And pragmatically, COM in its heyday was very closely tied to the ms c++ toolchain.
And of course you are correct many plugins use the clap helpers for c++ or the rust bindings rather than the raw c api in clap.
But brininging it back to the question at hand as opposed to this 20-30 year old history of object modeling in computer science, the point here is really again that vst3 *is* very c++ object oriented with specific patterns for things like ref counting and narrowing which are different than the modern standard (understandably, since vst3 is older than the modern standard). I think a comparison of the funknown header and the clap plugin header would clear this up, but I think we both understand it just fine! I’m just adding this comment for the folks following along.
And pragmatically, COM in its heyday was very closely tied to the ms c++ toolchain.
And of course you are correct many plugins use the clap helpers for c++ or the rust bindings rather than the raw c api in clap.
But brininging it back to the question at hand as opposed to this 20-30 year old history of object modeling in computer science, the point here is really again that vst3 *is* very c++ object oriented with specific patterns for things like ref counting and narrowing which are different than the modern standard (understandably, since vst3 is older than the modern standard). I think a comparison of the funknown header and the clap plugin header would clear this up, but I think we both understand it just fine! I’m just adding this comment for the folks following along.
-
Jeff McClintock Jeff McClintock https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=56398
- KVRist
- 432 posts since 30 Jan, 2005 from New Zealand
- KVRAF
- 8476 posts since 12 Feb, 2006 from Helsinki, Finland
I guess the argument here is that you can't directly turn extensions into interfaces and pass vtables because the 'this' pointers would have wrong offsets as all the CLAP methods take the same 'clap_plugin_t' pointer?Jeff McClintock wrote: Fri Jul 08, 2022 12:58 am The C++ 'object' presented by COM has the lowest runtime overhead when calling into it. CLAP has an extra layer or two of overhead. Which is a missed opportunity I think considering what a tiny alteration it would have taken to fix CLAP.
That's true.. but you don't need to go through the vtable at all if you instead just populate the extensions with pointers to regular (non-virtual) or static methods. Then it's just as efficient (just one indirect call) and if you really want to get fancy, you could probably populate the structures automatically with the help of some ATL-style CRTP.
