From 1db3c0371f9392610e8afff75b9e7d67427b6d9b Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 5 Dec 2020 15:05:05 +0100 Subject: [PATCH] Expand on the VST3 implementation readme --- src/common/serialization/vst3/README.md | 85 +++++++++++++------------ 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/common/serialization/vst3/README.md b/src/common/serialization/vst3/README.md index e25d87db..9249ef9d 100644 --- a/src/common/serialization/vst3/README.md +++ b/src/common/serialization/vst3/README.md @@ -4,51 +4,52 @@ TODO: Once this is more fleshed out, move this document to `docs/`, and perhaps replace this readme with a link to that document. The VST3 SDK uses an architecture where every object inherits from an interface, -and every interface inherits from `FUnknown` which offers a sort of query -interface. Newer versions of an interface with added functionality then inherit -from the previous version of that interface. Every interface (and thus also -newer versions of an old interface) get a unique identifier. It then uses a -smart pointer system (`FUnknownPtr`) that queries whether the `FUnknown` -matches a certain interface by checking whether the IDs match up, allowing casts -to that interface if the `FUnkonwn` matches. This means that an -`IPluginFactory*` may also be an `IPluginFactory2*` or an `IPluginFactory3*`. -For yabridge we need to be able to pass concrete serializable objects that -implement these interfaces around. +and every interface inherits from `FUnknown` which offers a dynamic casting +interface through `queryInterface()`. Every interface gets a unique identifier. +It then uses a smart pointer system (`FUnknownPtr`) that queries whether the +`FUnknown` matches a certain interface by checking whether the IDs match up, +allowing casts to that interface if the `FUnkonwn` matches. -## Serializing simple objects +Another important part of this system is interface versioning. Old interfaces +cannot be changed, so when the SDK adds new functionality to an existing +interface it defines a new interface that inherits from the old one. The +`queryInterface()` implementation should then allow casts to all of the +implemented interface versions. -TODO: Think of a better naming scheme +Lastly, the interfaces mostly provided a lot of getters for data, but some of +the interfaces also provide callback functions that should perform some +operation on the component implementing the interface. -Serializing an object that implements `ISimple` that only stores data and can't -perform any callbacks works as follows: +Yabridge's serialization and communication model for VST3 is thus a lot more +complicated than for VST2 since all of these objects are loosely coupled and are +instantiated and managed by the host. The model works as follows: -1. We define a class called `YaSimple` that inherits from `ISimple`. -2. We fetch all data from `ISimple` and store it in `YaSimple`. -3. `YaSimpl` can then be serialized with bitsery and transmitted like any other - object. +1. For an interface `IFoo`, we provide a possibly abstract implementation called + `YaFoo`. +2. This class has a constructor that takes an `IPtr` interface pointer and + copies all of the data from the interface's functions that do not perform any + side effects. +3. `YaFoo` then implements all the boilerplate required for `FUnknown`. This + includes the constructor, destructor and methods required for reference + counting, as well as the query interface. +4. If `IFoo` is a versioned interface such as `IPluginFactory3`, the above two + steps work slightly differently. When copying the data for a plugin factory, + we'll start copying from `IPluginFactory`, and we'll copy data from each + newer version of the interface that the `IPtr` supports. + During this process we keep track of which interfaces were supported by the + native plugin. In our query interface method we then only report support for + the same itnerfaces that were supported by `IPtr` returns a null pointer we know that the object doesn't - implement that version of the interface and we can stop. -3. During the copying process we'll also copy over the `iid`. This allows our - object to appear as the highest version of the interface we were able to copy - from. Doing this avoids complicated inheritance chains in our own - implemetnation. -4. `YaPluginFactory` can then be serialized with bitsery and transmitted like - any other object. - -## Processors and controllers - -TODO: Not sure how this will work yet. +Aside form the above, the plugin factory is the only place where we may +potentially report different values from those reported by the Windows VST3 +plugin. If we encounter an itnerface we do not yet support, we will log a +warning and we'll skip the interface since we wouldn't know how to handle it.