Add boilerplate for PluginFactory serialization

This commit is contained in:
Robbert van der Helm
2020-12-05 02:34:15 +01:00
parent 930ebbf7d1
commit 42f3639e93
5 changed files with 236 additions and 1 deletions
+3 -1
View File
@@ -77,6 +77,7 @@ vst3_plugin_sources = [
'src/common/communication/common.cpp',
'src/common/logging/common.cpp',
'src/common/logging/vst3.cpp',
'src/common/serialization/vst3/plugin-factory.cpp',
'src/common/configuration.cpp',
'src/common/plugins.cpp',
'src/common/utils.cpp',
@@ -105,7 +106,8 @@ host_sources = [
if with_vst3
host_sources += [
'src/wine-host/bridges/vst3.cpp'
'src/common/serialization/vst3/plugin-factory.cpp',
'src/wine-host/bridges/vst3.cpp',
]
endif
+1
View File
@@ -23,6 +23,7 @@
#include "../configuration.h"
#include "../utils.h"
#include "common.h"
#include "vst3/plugin-factory.h"
// Event handling for our VST3 plugins works slightly different from how we
// handle VST2 plugins. VST3 does not have a centralized event dispatching
+54
View File
@@ -0,0 +1,54 @@
# VST3 serialization
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<I>`) 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.
## Serializing simple objects
TODO: Think of a better naming scheme
Serializing an object that implements `ISimple` that only stores data and can't
perform any callbacks 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.
Our
solution approach for serializing Our solution here is to build an object that's compatible with
`IPluginFactory3` that copies all data from the original object
## Serializing versioned interfaces
For serializing versioned interfaces, such as `IPluginFactory3`, we'll do
something similar:
1. As with simple object, we define a class called `YaPluginFactory` that
inherits from `IPluginFactory3`.
2. Now we start copying data starting with `IPluginFactory`, then moving on to
`IPluginFactory2`, and then finally `IPluginFactory3`. When at some point our
`FUnknownPtr<I>` 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.
@@ -0,0 +1,97 @@
// yabridge: a Wine VST bridge
// Copyright (C) 2020 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "plugin-factory.h"
YaPluginFactory::YaPluginFactory(){FUNKNOWN_CTOR}
YaPluginFactory::YaPluginFactory(Steinberg::IPluginFactory* /*factory*/){
FUNKNOWN_CTOR
// TODO: Copy everything from `factory`
}
YaPluginFactory::~YaPluginFactory() {
FUNKNOWN_DTOR
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
IMPLEMENT_REFCOUNT(YaPluginFactory)
#pragma GCC diagnostic pop
tresult PLUGIN_API YaPluginFactory::queryInterface(Steinberg::FIDString _iid,
void** obj) {
QUERY_INTERFACE(_iid, obj, Steinberg::FUnknown::iid,
Steinberg::IPluginFactory)
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory::iid,
Steinberg::IPluginFactory)
if (actual_iid == Steinberg::IPluginFactory2::iid ||
actual_iid == Steinberg::IPluginFactory3::iid) {
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory2::iid,
Steinberg::IPluginFactory2)
}
if (actual_iid == Steinberg::IPluginFactory3::iid) {
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory3::iid,
Steinberg::IPluginFactory3)
}
*obj = nullptr;
return Steinberg::kNoInterface;
}
tresult PLUGIN_API
YaPluginFactory::getFactoryInfo(Steinberg::PFactoryInfo* info) {
// TODO: Implement
return 0;
}
int32 PLUGIN_API YaPluginFactory::countClasses() {
// TODO: Implement
return 0;
}
tresult PLUGIN_API YaPluginFactory::getClassInfo(Steinberg::int32 index,
Steinberg::PClassInfo* info) {
// TODO: Implement
return 0;
}
tresult PLUGIN_API YaPluginFactory::createInstance(Steinberg::FIDString cid,
Steinberg::FIDString _iid,
void** obj) {
// TODO: Implement
return 0;
}
tresult PLUGIN_API
YaPluginFactory::getClassInfo2(int32 index, Steinberg::PClassInfo2* info) {
// TODO: Implement
return 0;
}
tresult PLUGIN_API
YaPluginFactory::getClassInfoUnicode(int32 index,
Steinberg::PClassInfoW* info) {
// TODO: Implement
return 0;
}
tresult PLUGIN_API
YaPluginFactory::setHostContext(Steinberg::FUnknown* context) {
// TODO: Implement
return 0;
}
@@ -0,0 +1,81 @@
// yabridge: a Wine VST bridge
// Copyright (C) 2020 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <pluginterfaces/base/ipluginbase.h>
#include "../../bitsery/ext/vst3.h"
namespace {
using Steinberg::int32, Steinberg::tresult;
} // namespace
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
/**
* Wraps around `IPluginFactory{1,2,3}` for serialization purposes. See
* `README.md` for more information on how this works.
*/
class YaPluginFactory : public Steinberg::IPluginFactory3 {
public:
YaPluginFactory();
/**
* Create a copy of an existing plugin factory. Depending on the
supported
* interface function more or less of this struct will be left empty, and
* `iid` will be set accordingly.
*
* TODO: Check if we don't need a custom query interface, we probably do.
*/
explicit YaPluginFactory(Steinberg::IPluginFactory* factory);
~YaPluginFactory();
DECLARE_FUNKNOWN_METHODS
// From `IPluginFactory`
tresult PLUGIN_API getFactoryInfo(Steinberg::PFactoryInfo* info) override;
int32 PLUGIN_API countClasses() override;
tresult PLUGIN_API getClassInfo(Steinberg::int32 index,
Steinberg::PClassInfo* info) override;
tresult PLUGIN_API createInstance(Steinberg::FIDString cid,
Steinberg::FIDString _iid,
void** obj) override;
// From `IPluginFactory2`
tresult PLUGIN_API getClassInfo2(int32 index,
Steinberg::PClassInfo2* info) override;
// From `IPluginFactory3`
tresult PLUGIN_API
getClassInfoUnicode(int32 index, Steinberg::PClassInfoW* info) override;
tresult PLUGIN_API setHostContext(Steinberg::FUnknown* context) override;
/**
* The IID of the interface we should report as.
*/
Steinberg::FUID actual_iid;
template <typename S>
void serialize(S& s) {
s.ext(actual_iid, bitsery::ext::FUID());
}
};
#pragma GCC diagnostic pop