From 32c1028736a08fd2c544f3296fb0570d7e644fbb Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 13 Dec 2020 15:50:41 +0100 Subject: [PATCH] Implement IPluginFactory3::setHostContext() Now the plugin factories are fully implemented (at least, functionality wise, we still can't create most kinds of objects). --- README.md | 4 +-- src/common/communication/vst2.h | 2 +- src/common/logging/vst3.cpp | 7 +++++ src/common/logging/vst3.h | 1 + src/common/serialization/vst3.h | 3 +- src/common/serialization/vst3/component.h | 5 ++-- .../serialization/vst3/host-application.cpp | 2 +- .../serialization/vst3/host-application.h | 16 +++++----- .../serialization/vst3/plugin-factory.h | 21 ++++++++++++-- .../bridges/vst3-impls/plugin-factory.cpp | 29 +++++++++++++++---- .../bridges/vst3-impls/plugin-factory.h | 7 +++++ src/wine-host/bridges/vst2.cpp | 2 +- src/wine-host/bridges/vst3.cpp | 13 +++++++++ src/wine-host/bridges/vst3.h | 7 +++++ 14 files changed, 95 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index be611af7..7bd354e0 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,11 @@ imcomplete list of things that still have to be done before this can be used: - Left to implement: - `YaHostApplicationHostImpl::createComponent`. - - `IPluginFactory3::setHostContext()` using the same host application context proxy method. - The rest of `IComponent`'s functions - - `IPluginFactory3::setHostContext()` - All other mandatory interfaces - All other optional interfaces - Fully implemented: - - Nothing yet + - `GetPluginFactory()` and `IPluginFactory{,2,3}` - Update the GitHub Actions workflows. - Update yabridgectl to handle buth VST2 and VST3 plugins. - Update all documentation to refer to VST2 and VST3 support separately, and diff --git a/src/common/communication/vst2.h b/src/common/communication/vst2.h index 26c038a6..60bb1e95 100644 --- a/src/common/communication/vst2.h +++ b/src/common/communication/vst2.h @@ -380,7 +380,7 @@ class Vst2Sockets : public Sockets { * @param plugin The `AEffect` instance that should be passed to the callback * function. During `WantsAEffect` we'll send back a copy of this, and when we * get sent an `AEffect` instance (e.g. during `audioMasterIOChanged()`) we'll - * write the updated values to this isntance. + * write the updated values to this instance. * @param callback The function to call with the arguments received from the * socket, either `AEffect::dispatcher()` or `audioMasterCallback()`. * diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index 43ee3330..3241ac8e 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -78,6 +78,13 @@ void Vst3Logger::log_request(bool is_host_vst, [](auto& message) { message << "GetPluginFactory()"; }); } +void Vst3Logger::log_request(bool is_host_vst, + const YaPluginFactory::SetHostContext&) { + log_request_base(is_host_vst, [](auto& message) { + message << "IPluginFactory3::setHostContext(IHostApplication*)"; + }); +} + void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) { log_request_base(is_host_vst, [](auto& message) { message << "Requesting "; diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index 324186bf..a580575f 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -61,6 +61,7 @@ class Vst3Logger { void log_request(bool is_host_vst, const YaComponent::Initialize&); 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 YaPluginFactory::SetHostContext&); void log_request(bool is_host_vst, const WantsConfiguration&); void log_response(bool is_host_vst, const Ack&); diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index 449547b8..60683920 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -61,7 +61,8 @@ using ControlRequest = std::variant; + YaPluginFactory::Construct, + YaPluginFactory::SetHostContext>; template void serialize(S& s, ControlRequest& payload) { diff --git a/src/common/serialization/vst3/component.h b/src/common/serialization/vst3/component.h index b370be25..11ecf078 100644 --- a/src/common/serialization/vst3/component.h +++ b/src/common/serialization/vst3/component.h @@ -43,7 +43,8 @@ * for everything other than the edit controller's class ID. * * TODO: I think it's expected that components also implement `IAudioProcessor` - * and `IConnectionPoint`. + * and `IConnectionPoint`. We should use the same approach as in the + * plugin factory to implement multiple, possibly optional, interfaces. */ class YaComponent : public Steinberg::Vst::IComponent { public: @@ -57,7 +58,7 @@ class YaComponent : public Steinberg::Vst::IComponent { * Read arguments from an existing implementation. */ ConstructArgs(Steinberg::IPtr component, - size_t isntance_id); + size_t instance_id); /** * The unique identifier for this specific instance. diff --git a/src/common/serialization/vst3/host-application.cpp b/src/common/serialization/vst3/host-application.cpp index 30f9a14e..b5695028 100644 --- a/src/common/serialization/vst3/host-application.cpp +++ b/src/common/serialization/vst3/host-application.cpp @@ -20,7 +20,7 @@ YaHostApplication::ConstructArgs::ConstructArgs() {} YaHostApplication::ConstructArgs::ConstructArgs( Steinberg::IPtr context, - size_t component_instance_id) + std::optional component_instance_id) : component_instance_id(component_instance_id) { Steinberg::Vst::String128 name_array; if (context->getName(name_array) == Steinberg::kResultOk) { diff --git a/src/common/serialization/vst3/host-application.h b/src/common/serialization/vst3/host-application.h index 6a88644f..80845748 100644 --- a/src/common/serialization/vst3/host-application.h +++ b/src/common/serialization/vst3/host-application.h @@ -48,16 +48,15 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication { * Read arguments from an existing implementation. */ ConstructArgs(Steinberg::IPtr context, - size_t component_isntance_id); + std::optional component_instance_id); /** * The unique instance identifier of the component this host context has - * been passed to and thus belongs to. - * - * TODO: When we implement `IPluginFactory3::setHostContext()` this - * should be made optional. + * been passed to and thus belongs to, if we are handling + * `IpluginBase::initialize()`. When handling + * `IPluginFactory::setHostContext()` this will be empty. */ - native_size_t component_instance_id; + std::optional component_instance_id; /** * For `IHostApplication::getName`. @@ -66,7 +65,10 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication { template void serialize(S& s) { - s.value8b(component_instance_id); + s.ext(component_instance_id, bitsery::ext::StdOptional{}, + [](S& s, native_size_t& instance_id) { + s.value8b(instance_id); + }); s.ext(name, bitsery::ext::StdOptional{}, [](S& s, std::u16string& name) { s.text2b(name, std::extent_v); diff --git a/src/common/serialization/vst3/plugin-factory.h b/src/common/serialization/vst3/plugin-factory.h index 979038ce..532a9bb9 100644 --- a/src/common/serialization/vst3/plugin-factory.h +++ b/src/common/serialization/vst3/plugin-factory.h @@ -25,6 +25,7 @@ #include "../../bitsery/ext/vst3.h" #include "base.h" +#include "host-application.h" // TODO: After implementing one or two more of these, abstract away some of the // nasty bits @@ -155,10 +156,26 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 { // From `IPluginFactory3` tresult PLUGIN_API getClassInfoUnicode(int32 index, Steinberg::PClassInfoW* info) override; + /** - * We'll pass a `IHostApplication` to the Windows VST3 plugin's factory when - * this is called so it can send messages. + * Message to pass through a call to `IPluginFactory3::setHostContext()` to + * the Wine plugin host. A proxy `YaHostApplication` should be created on + * the Wine plugin host and then passed as an argument to + * `IPluginFactory3::setHostContext()`. If the host called + * `IPluginFactory3::setHostContext()` with something other than an + * `IHostApplication*`, we return an error immediately and log the call. */ + struct SetHostContext { + using Response = UniversalTResult; + + YaHostApplication::ConstructArgs host_application_context_args; + + template + void serialize(S& s) { + s.object(host_application_context_args); + } + }; + virtual tresult PLUGIN_API setHostContext(Steinberg::FUnknown* context) override = 0; diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.cpp b/src/plugin/bridges/vst3-impls/plugin-factory.cpp index 0f971f39..27b31ba3 100644 --- a/src/plugin/bridges/vst3-impls/plugin-factory.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-factory.cpp @@ -64,10 +64,27 @@ YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid, } tresult PLUGIN_API -YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* /*context*/) { - // TODO: The docs don't clearly specify what this should be doing, but from - // what I've seen this is only used to pass a `IHostApplication` - // instance. That's used to allow the plugin to create objects in the - // host. - return Steinberg::kNotImplemented; +YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* context) { + // This `context` will likely be an `IHostApplication`. If it is, we will + // store it for future calls, create a proxy object on the Wine side, and + // then pass it to the Windows VST3 plugin's plugin factory using the same + // function. If we get passed anything else we'll just return instead since + // there's nothing we can do with it. + host_application_context = context; + + if (host_application_context) { + YaHostApplication::ConstructArgs host_application_context_args( + host_application_context, std::nullopt); + + return bridge + .send_message(YaPluginFactory::SetHostContext{ + .host_application_context_args = + std::move(host_application_context_args)}) + .native(); + } else { + bridge.logger.log_unknown_interface( + "In IPluginFactory3::setHostContext(), ignoring", + context ? std::optional(context->iid) : std::nullopt); + return Steinberg::kNotImplemented; + } } diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.h b/src/plugin/bridges/vst3-impls/plugin-factory.h index aee5a149..963ff26e 100644 --- a/src/plugin/bridges/vst3-impls/plugin-factory.h +++ b/src/plugin/bridges/vst3-impls/plugin-factory.h @@ -30,4 +30,11 @@ class YaPluginFactoryPluginImpl : public YaPluginFactory { private: Vst3PluginBridge& bridge; + + /** + * An `IHostApplication` instance if we get one through + * `IPluginFactory3::setHostContext()`. + */ + Steinberg::FUnknownPtr + host_application_context; }; diff --git a/src/wine-host/bridges/vst2.cpp b/src/wine-host/bridges/vst2.cpp index ac349839..ff16659d 100644 --- a/src/wine-host/bridges/vst2.cpp +++ b/src/wine-host/bridges/vst2.cpp @@ -100,7 +100,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, sockets.connect(); // Initialize after communication has been set up - // We'll try to do the same `get_bridge_isntance` trick as in + // We'll try to do the same `get_bridge_instance` trick as in // `plugin/plugin.cpp`, but since the plugin will probably call the host // callback while it's initializing we sadly have to use a global here. { diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 1696a6d8..8e8798e8 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -111,6 +111,19 @@ void Vst3Bridge::run() { -> YaPluginFactory::Construct::Response { return YaPluginFactory::ConstructArgs( module->getFactory().get()); + }, + [&](YaPluginFactory::SetHostContext& request) + -> YaPluginFactory::SetHostContext::Response { + plugin_factory_host_application_context = + Steinberg::owned(new YaHostApplicationHostImpl( + *this, + std::move(request.host_application_context_args))); + + Steinberg::FUnknownPtr factory_3( + module->getFactory().get()); + assert(factory_3); + return factory_3->setHostContext( + plugin_factory_host_application_context); }}); } diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 9bfd01e4..b871155a 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -99,6 +99,13 @@ class Vst3Bridge : public HostBridge { */ std::atomic_size_t current_instance_id; + /** + * The host application context proxy object if we got passed a host + * application context during a call to `IPluginFactory3::setHostContext()` + * by the host. + */ + Steinberg::IPtr plugin_factory_host_application_context; + // Below are managed instances we created for // `IPluginFactory::createInstance()`. The keys in all of these maps are the // unique identifiers we generated for them so we can identify specific