mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-08 20:40:11 +02:00
Use the new approach for creating plugin factory
Directly serializing and deserializing into objects was and more boilerplate heavy (since we now need two implementations even though we only use one), and also much less flexible because we can't wrap payloads in structs or provide optional values that way.
This commit is contained in:
+14
-15
@@ -45,18 +45,18 @@ void Vst3Logger::log_request(bool is_host_vst,
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaPluginFactory::Construct&) {
|
||||
log_request_base(is_host_vst,
|
||||
[](auto& message) { message << "GetPluginFactory()"; });
|
||||
}
|
||||
|
||||
void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
|
||||
log_request_base(is_host_vst, [](auto& message) {
|
||||
message << "Requesting <Configuration>";
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_request(bool is_host_vst, const WantsPluginFactory&) {
|
||||
log_request_base(is_host_vst, [](auto& message) {
|
||||
message << "Requesting <IPluginFactory*>";
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst, const Ack&) {
|
||||
log_response_base(is_host_vst, [&](auto& message) { message << "ACK"; });
|
||||
}
|
||||
@@ -76,16 +76,15 @@ void Vst3Logger::log_response(
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPluginFactory::ConstructArgs& args) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << "<IPluginFactory*> with " << args.num_classes
|
||||
<< " registered classes";
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
|
||||
log_response_base(is_host_vst,
|
||||
[](auto& message) { message << "<Configuration"; });
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPluginFactory& factory) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << "<IPluginFactory*> with "
|
||||
<< const_cast<YaPluginFactory&>(factory).countClasses()
|
||||
<< " registered classes";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,15 +50,15 @@ class Vst3Logger {
|
||||
void log_request(bool is_host_vst, const YaComponent::Construct&);
|
||||
void log_request(bool is_host_vst, const YaComponent::Destruct&);
|
||||
void log_request(bool is_host_vst, const YaComponent::Terminate&);
|
||||
void log_request(bool is_host_vst, const YaPluginFactory::Construct&);
|
||||
void log_request(bool is_host_vst, const WantsConfiguration&);
|
||||
void log_request(bool is_host_vst, const WantsPluginFactory&);
|
||||
|
||||
void log_response(bool is_host_vst, const Ack&);
|
||||
void log_response(
|
||||
bool is_host_vst,
|
||||
const std::variant<YaComponent::ConstructArgs, UniversalTResult>&);
|
||||
void log_response(bool is_host_vst, const YaPluginFactory::ConstructArgs&);
|
||||
void log_response(bool is_host_vst, const Configuration&);
|
||||
void log_response(bool is_host_vst, const YaPluginFactory&);
|
||||
|
||||
Logger& logger;
|
||||
|
||||
|
||||
@@ -52,17 +52,6 @@ struct WantsConfiguration {
|
||||
void serialize(S&) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marker struct to indicate the other side (the Wine plugin host) should send a
|
||||
* copy of the hosted Windows VST3 plugin's `IPluginFactory{,2,3}` interface.
|
||||
*/
|
||||
struct WantsPluginFactory {
|
||||
using Response = YaPluginFactory&;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S&) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* When we send a control message from the plugin to the Wine VST host, this
|
||||
* encodes the information we request or the operation we want to perform. A
|
||||
@@ -71,7 +60,7 @@ struct WantsPluginFactory {
|
||||
using ControlRequest = std::variant<YaComponent::Construct,
|
||||
YaComponent::Destruct,
|
||||
YaComponent::Terminate,
|
||||
WantsPluginFactory>;
|
||||
YaPluginFactory::Construct>;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, ControlRequest& payload) {
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
TODO: Once this is more fleshed out, move this document to `docs/`, and perhaps
|
||||
replace this readme with a link to that document.
|
||||
|
||||
TODO: There are now two approaches in use: the factory takes an interface
|
||||
pointer for serialization and deserializes into an object directly, and the
|
||||
component uses an args struct because the alternative involving pointers is just
|
||||
too unsafe (as we also have to communicate additional payload data). This should
|
||||
probably be unified into only using the latter appraoch.
|
||||
|
||||
The VST3 SDK uses an architecture where every object inherits from an interface,
|
||||
and every interface inherits from `FUnknown` which offers a dynamic casting
|
||||
interface through `queryInterface()`. Every interface gets a unique identifier.
|
||||
@@ -30,6 +24,11 @@ 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:
|
||||
|
||||
TODO: This is now slightly out of date. Instead of serializing and deserializing
|
||||
directly into interface implementations through references, we now only pass
|
||||
structs with payload data around to make the receiving process much more
|
||||
flexible.
|
||||
|
||||
1. For an interface `IFoo`, we provide a possibly abstract implementation called
|
||||
`YaFoo`.
|
||||
2. This class has a constructor that takes an `IPtr<IFoo>` interface pointer and
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
|
||||
#include <public.sdk/source/vst/utility/stringconvert.h>
|
||||
|
||||
YaPluginFactory::YaPluginFactory(){FUNKNOWN_CTOR}
|
||||
YaPluginFactory::ConstructArgs::ConstructArgs() {}
|
||||
|
||||
YaPluginFactory::YaPluginFactory(
|
||||
YaPluginFactory::ConstructArgs::ConstructArgs(
|
||||
Steinberg::IPtr<Steinberg::IPluginFactory> factory) {
|
||||
FUNKNOWN_CTOR
|
||||
|
||||
known_iids.insert(factory->iid);
|
||||
// `IPluginFactory::getFactoryInfo`
|
||||
if (Steinberg::PFactoryInfo info;
|
||||
@@ -75,7 +73,11 @@ YaPluginFactory::YaPluginFactory(
|
||||
}
|
||||
}
|
||||
|
||||
YaPluginFactory::~YaPluginFactory() {
|
||||
YaPluginFactory::YaPluginFactory(const ConstructArgs&& args)
|
||||
: arguments(std::move(args)){FUNKNOWN_CTOR}
|
||||
|
||||
// clang-format just doesn't understand these macros, I guess
|
||||
YaPluginFactory::~YaPluginFactory() {
|
||||
FUNKNOWN_DTOR
|
||||
}
|
||||
|
||||
@@ -88,15 +90,15 @@ tresult PLUGIN_API YaPluginFactory::queryInterface(Steinberg::FIDString _iid,
|
||||
void** obj) {
|
||||
QUERY_INTERFACE(_iid, obj, Steinberg::FUnknown::iid,
|
||||
Steinberg::IPluginFactory)
|
||||
if (known_iids.contains(Steinberg::IPluginFactory::iid)) {
|
||||
if (arguments.known_iids.contains(Steinberg::IPluginFactory::iid)) {
|
||||
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory::iid,
|
||||
Steinberg::IPluginFactory)
|
||||
}
|
||||
if (known_iids.contains(Steinberg::IPluginFactory2::iid)) {
|
||||
if (arguments.known_iids.contains(Steinberg::IPluginFactory2::iid)) {
|
||||
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory2::iid,
|
||||
Steinberg::IPluginFactory2)
|
||||
}
|
||||
if (known_iids.contains(Steinberg::IPluginFactory3::iid)) {
|
||||
if (arguments.known_iids.contains(Steinberg::IPluginFactory3::iid)) {
|
||||
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginFactory3::iid,
|
||||
Steinberg::IPluginFactory3)
|
||||
}
|
||||
@@ -107,8 +109,8 @@ tresult PLUGIN_API YaPluginFactory::queryInterface(Steinberg::FIDString _iid,
|
||||
|
||||
tresult PLUGIN_API
|
||||
YaPluginFactory::getFactoryInfo(Steinberg::PFactoryInfo* info) {
|
||||
if (info && factory_info) {
|
||||
*info = *factory_info;
|
||||
if (info && arguments.factory_info) {
|
||||
*info = *arguments.factory_info;
|
||||
return Steinberg::kResultOk;
|
||||
} else {
|
||||
return Steinberg::kNotInitialized;
|
||||
@@ -116,17 +118,17 @@ YaPluginFactory::getFactoryInfo(Steinberg::PFactoryInfo* info) {
|
||||
}
|
||||
|
||||
int32 PLUGIN_API YaPluginFactory::countClasses() {
|
||||
return num_classes;
|
||||
return arguments.num_classes;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API YaPluginFactory::getClassInfo(Steinberg::int32 index,
|
||||
Steinberg::PClassInfo* info) {
|
||||
if (index >= static_cast<int32>(class_infos_unicode.size())) {
|
||||
if (index >= static_cast<int32>(arguments.class_infos_unicode.size())) {
|
||||
return Steinberg::kInvalidArgument;
|
||||
}
|
||||
|
||||
if (class_infos_1[index]) {
|
||||
*info = *class_infos_1[index];
|
||||
if (arguments.class_infos_1[index]) {
|
||||
*info = *arguments.class_infos_1[index];
|
||||
return Steinberg::kResultOk;
|
||||
} else {
|
||||
return Steinberg::kResultFalse;
|
||||
@@ -135,12 +137,12 @@ tresult PLUGIN_API YaPluginFactory::getClassInfo(Steinberg::int32 index,
|
||||
|
||||
tresult PLUGIN_API
|
||||
YaPluginFactory::getClassInfo2(int32 index, Steinberg::PClassInfo2* info) {
|
||||
if (index >= static_cast<int32>(class_infos_1.size())) {
|
||||
if (index >= static_cast<int32>(arguments.class_infos_1.size())) {
|
||||
return Steinberg::kInvalidArgument;
|
||||
}
|
||||
|
||||
if (class_infos_2[index]) {
|
||||
*info = *class_infos_2[index];
|
||||
if (arguments.class_infos_2[index]) {
|
||||
*info = *arguments.class_infos_2[index];
|
||||
return Steinberg::kResultOk;
|
||||
} else {
|
||||
return Steinberg::kResultFalse;
|
||||
@@ -150,12 +152,12 @@ YaPluginFactory::getClassInfo2(int32 index, Steinberg::PClassInfo2* info) {
|
||||
tresult PLUGIN_API
|
||||
YaPluginFactory::getClassInfoUnicode(int32 index,
|
||||
Steinberg::PClassInfoW* info) {
|
||||
if (index >= static_cast<int32>(class_infos_unicode.size())) {
|
||||
if (index >= static_cast<int32>(arguments.class_infos_unicode.size())) {
|
||||
return Steinberg::kInvalidArgument;
|
||||
}
|
||||
|
||||
if (class_infos_unicode[index]) {
|
||||
*info = *class_infos_unicode[index];
|
||||
if (arguments.class_infos_unicode[index]) {
|
||||
*info = *arguments.class_infos_unicode[index];
|
||||
return Steinberg::kResultOk;
|
||||
} else {
|
||||
return Steinberg::kResultFalse;
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
|
||||
// TODO: After implementing one or two more of these, abstract away some of the
|
||||
// nasty bits
|
||||
// TODO: Should we have some clearer way to indicate to us which fields are
|
||||
// going to return copied results directly and which make a callback?
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||
@@ -40,16 +38,99 @@
|
||||
*/
|
||||
class YaPluginFactory : public Steinberg::IPluginFactory3 {
|
||||
public:
|
||||
YaPluginFactory();
|
||||
/**
|
||||
* These are the arguments for creating a `YaPluginFactoryPluginImpl`.
|
||||
*/
|
||||
struct ConstructArgs {
|
||||
ConstructArgs();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
ConstructArgs(Steinberg::IPtr<Steinberg::IPluginFactory> factory);
|
||||
|
||||
/**
|
||||
* The IIDs that the interface we serialized supports.
|
||||
*/
|
||||
std::set<Steinberg::FUID> known_iids;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory::getFactoryInfo`.
|
||||
*/
|
||||
std::optional<Steinberg::PFactoryInfo> factory_info;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory::countClasses`.
|
||||
*/
|
||||
int num_classes;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory::getClassInfo`. We need to store all four class
|
||||
* info versions if the plugin can provide them since we don't know
|
||||
* which version of the interface the host will use. Will be
|
||||
* `std::nullopt` if the plugin doesn't return a class info.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfo>> class_infos_1;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory2::getClassInfo2`, works the same way as the
|
||||
* above.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfo2>> class_infos_2;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory3::getClassInfoUnicode`, works the same way as the
|
||||
* above.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfoW>> class_infos_unicode;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.ext(known_iids, bitsery::ext::StdSet{32},
|
||||
[](S& s, Steinberg::FUID& iid) {
|
||||
s.ext(iid, bitsery::ext::FUID{});
|
||||
});
|
||||
s.ext(factory_info, bitsery::ext::StdOptional{});
|
||||
s.value4b(num_classes);
|
||||
s.container(class_infos_1, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfo>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
s.container(class_infos_2, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfo2>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
s.container(class_infos_unicode, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfoW>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Message to request the `IPluginFactory{,2,3}`'s information from the Wine
|
||||
* plugin host.
|
||||
*/
|
||||
explicit YaPluginFactory(
|
||||
Steinberg::IPtr<Steinberg::IPluginFactory> factory);
|
||||
struct Construct {
|
||||
using Response = ConstructArgs;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S&) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate this instance with arguments read from the Windows VST3
|
||||
* plugin's plugin factory.
|
||||
*/
|
||||
YaPluginFactory(const ConstructArgs&& args);
|
||||
|
||||
/**
|
||||
* We do not need to implement the destructor, since when the sockets are
|
||||
* closed, RAII will clean up the Windows VST3 module we loaded along with
|
||||
* its factory for us.
|
||||
*/
|
||||
virtual ~YaPluginFactory();
|
||||
|
||||
DECLARE_FUNKNOWN_METHODS
|
||||
@@ -81,58 +162,8 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
|
||||
virtual tresult PLUGIN_API
|
||||
setHostContext(Steinberg::FUnknown* context) override = 0;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.ext(known_iids, bitsery::ext::StdSet{32},
|
||||
[](S& s, Steinberg::FUID& iid) {
|
||||
s.ext(iid, bitsery::ext::FUID{});
|
||||
});
|
||||
s.ext(factory_info, bitsery::ext::StdOptional{});
|
||||
s.value4b(num_classes);
|
||||
s.container(class_infos_1, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfo>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
s.container(class_infos_2, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfo2>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
s.container(class_infos_unicode, 2048,
|
||||
[](S& s, std::optional<Steinberg::PClassInfoW>& info) {
|
||||
s.ext(info, bitsery::ext::StdOptional{});
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* The IIDs that the interface we serialized supports.
|
||||
*/
|
||||
std::set<Steinberg::FUID> known_iids;
|
||||
|
||||
/**
|
||||
* For `IPluginFactory::getFactoryInfo`.
|
||||
*/
|
||||
std::optional<Steinberg::PFactoryInfo> factory_info;
|
||||
/**
|
||||
* For `IPluginFactory::countClasses`.
|
||||
*/
|
||||
int num_classes;
|
||||
/**
|
||||
* For `IPluginFactory::getClassInfo`. We need to store all four class info
|
||||
* versions if the plugin can provide them since we don't know which version
|
||||
* of the interface the host will use. Will be `std::nullopt` if the plugin
|
||||
* doesn't return a class info.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfo>> class_infos_1;
|
||||
/**
|
||||
* For `IPluginFactory2::getClassInfo2`, works the same way as the above.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfo2>> class_infos_2;
|
||||
/**
|
||||
* For `IPluginFactory3::getClassInfoUnicode`, works the same way as the
|
||||
* above.
|
||||
*/
|
||||
std::vector<std::optional<Steinberg::PClassInfoW>> class_infos_unicode;
|
||||
protected:
|
||||
ConstructArgs arguments;
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
Reference in New Issue
Block a user