From 11bf7532fad1209a0f33e52ab8998a17b8c5d60b Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 17 Dec 2020 13:07:42 +0100 Subject: [PATCH] Rename the monolitic class to Vst3PluginProxy Now it's starting to look promising. --- docs/vst3.md | 6 +- meson.build | 6 +- src/common/logging/vst3.cpp | 23 +-- src/common/logging/vst3.h | 6 +- src/common/serialization/vst3.h | 6 +- src/common/serialization/vst3/README.md | 16 +- .../serialization/vst3/audio-processor.h | 2 +- src/common/serialization/vst3/component.h | 2 +- .../serialization/vst3/host-application.h | 2 +- src/common/serialization/vst3/plugin-base.h | 2 +- .../serialization/vst3/plugin-monolith.h | 152 +----------------- .../{plugin-monolith.cpp => plugin-proxy.cpp} | 16 +- src/common/serialization/vst3/plugin-proxy.h | 151 +++++++++++++++++ src/plugin/bridges/vst3-impls/component.h | 96 +---------- .../bridges/vst3-impls/plugin-factory.cpp | 23 ++- .../bridges/vst3-impls/plugin-factory.h | 7 +- .../{component.cpp => plugin-proxy.cpp} | 68 ++++---- src/plugin/bridges/vst3-impls/plugin-proxy.h | 95 +++++++++++ src/plugin/bridges/vst3.cpp | 6 +- src/plugin/bridges/vst3.h | 7 +- src/wine-host/bridges/vst3.cpp | 14 +- 21 files changed, 352 insertions(+), 354 deletions(-) rename src/common/serialization/vst3/{plugin-monolith.cpp => plugin-proxy.cpp} (85%) create mode 100644 src/common/serialization/vst3/plugin-proxy.h rename src/plugin/bridges/vst3-impls/{component.cpp => plugin-proxy.cpp} (75%) create mode 100644 src/plugin/bridges/vst3-impls/plugin-proxy.h diff --git a/docs/vst3.md b/docs/vst3.md index df08998b..aa0930f0 100644 --- a/docs/vst3.md +++ b/docs/vst3.md @@ -4,10 +4,10 @@ TODO: Flesh this out further TODO: Link to `src/common/serialization/vst3/README.md` -TODO: Mention the new `Ya::supports()` mechanism for monolithic interfaces -through multiple inheritance +TODO: Mention the new `Ya::supports()` mechanism for the monolithic proxy +objects through multiple inheritance -TODO: Explain the monolith. +TODO: Explain the monolith The VST3 SDK uses an architecture where every concrete object inherits from an interface, and every interface inherits from `FUnknown`. `FUnkonwn` offers a diff --git a/meson.build b/meson.build index 99c9644e..baead088 100644 --- a/meson.build +++ b/meson.build @@ -85,15 +85,15 @@ vst3_plugin_sources = [ 'src/common/serialization/vst3/param-value-queue.cpp', 'src/common/serialization/vst3/parameter-changes.cpp', 'src/common/serialization/vst3/plugin-base.cpp', - 'src/common/serialization/vst3/plugin-monolith.cpp', + 'src/common/serialization/vst3/plugin-proxy.cpp', 'src/common/serialization/vst3/plugin-factory.cpp', 'src/common/serialization/vst3/process-data.cpp', 'src/common/configuration.cpp', 'src/common/plugins.cpp', 'src/common/utils.cpp', 'src/plugin/bridges/vst3.cpp', - 'src/plugin/bridges/vst3-impls/component.cpp', 'src/plugin/bridges/vst3-impls/plugin-factory.cpp', + 'src/plugin/bridges/vst3-impls/plugin-proxy.cpp', 'src/plugin/host-process.cpp', 'src/plugin/utils.cpp', 'src/plugin/vst3-plugin.cpp', @@ -127,7 +127,7 @@ if with_vst3 'src/common/serialization/vst3/param-value-queue.cpp', 'src/common/serialization/vst3/parameter-changes.cpp', 'src/common/serialization/vst3/plugin-base.cpp', - 'src/common/serialization/vst3/plugin-monolith.cpp', + 'src/common/serialization/vst3/plugin-proxy.cpp', 'src/common/serialization/vst3/plugin-factory.cpp', 'src/common/serialization/vst3/process-data.cpp', 'src/wine-host/bridges/vst3-impls/host-application.cpp', diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index 51140247..6d285400 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -37,7 +37,7 @@ void Vst3Logger::log_unknown_interface( } void Vst3Logger::log_request(bool is_host_vst, - const YaPluginMonolith::Construct&) { + const Vst3PluginProxy::Construct&) { log_request_base(is_host_vst, [&](auto& message) { // TODO: Log the CID on verbosity level 2, and then also report all CIDs // in the plugin factory @@ -51,7 +51,7 @@ void Vst3Logger::log_request(bool is_host_vst, } void Vst3Logger::log_request(bool is_host_vst, - const YaPluginMonolith::Destruct& request) { + const Vst3PluginProxy::Destruct& request) { log_request_base(is_host_vst, [&](auto& message) { message << "::~IComponent()"; @@ -70,8 +70,9 @@ void Vst3Logger::log_request( }); } -void Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::GetBusArrangement& request) { +void Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::GetBusArrangement& request) { log_request_base(is_host_vst, [&](auto& message) { message << "::getBusArrangement(dir = " << request.dir @@ -79,8 +80,9 @@ void Vst3Logger::log_request(bool is_host_vst, }); } -void Vst3Logger::log_request(bool is_host_vst, - const YaAudioProcessor::CanProcessSampleSize& request) { +void Vst3Logger::log_request( + bool is_host_vst, + const YaAudioProcessor::CanProcessSampleSize& request) { log_request_base(is_host_vst, [&](auto& message) { message << "::canProcessSampleSize(symbolicSampleSize = " @@ -256,12 +258,11 @@ void Vst3Logger::log_response(bool is_host_vst, const Ack&) { log_response_base(is_host_vst, [&](auto& message) { message << "ACK"; }); } -void Vst3Logger::log_response( - bool is_host_vst, - const std::variant& - result) { +void Vst3Logger::log_response(bool is_host_vst, + const std::variant& result) { log_response_base(is_host_vst, [&](auto& message) { - std::visit(overload{[&](const YaPluginMonolith::ConstructArgs& args) { + std::visit(overload{[&](const Vst3PluginProxy::ConstructArgs& args) { message << ""; }, diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index a45b2e41..36d04315 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -56,8 +56,8 @@ class Vst3Logger { // flag here indicates whether the request was initiated on the host side // (what we'll call a control message). - void log_request(bool is_host_vst, const YaPluginMonolith::Construct&); - void log_request(bool is_host_vst, const YaPluginMonolith::Destruct&); + void log_request(bool is_host_vst, const Vst3PluginProxy::Construct&); + void log_request(bool is_host_vst, const Vst3PluginProxy::Destruct&); void log_request(bool is_host_vst, const YaAudioProcessor::SetBusArrangements&); void log_request(bool is_host_vst, @@ -88,7 +88,7 @@ class Vst3Logger { void log_response(bool is_host_vst, const Ack&); void log_response( bool is_host_vst, - const std::variant&); + const std::variant&); void log_response(bool is_host_vst, const YaAudioProcessor::GetBusArrangementResponse&); void log_response(bool is_host_vst, diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index d8aa9a54..df052f14 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -24,7 +24,7 @@ #include "../utils.h" #include "common.h" #include "vst3/plugin-factory.h" -#include "vst3/plugin-monolith.h" +#include "vst3/plugin-proxy.h" // Event handling for our VST3 plugins works slightly different from how we // handle VST2 plugins. VST3 does not have a centralized event dispatching @@ -57,8 +57,8 @@ struct WantsConfiguration { * encodes the information we request or the operation we want to perform. A * request of type `ControlRequest(T)` should send back a `T::Response`. */ -using ControlRequest = std::variant. - -#pragma once - -#include -#include - -#include "../common.h" -#include "audio-processor.h" -#include "base.h" -#include "component.h" -#include "host-application.h" -#include "plugin-base.h" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" - -/** - * An abstract class that optionally implements all VST3 interfaces a plugin - * object could implement. A more in depth explanation can be found in - * `docs/vst3.md`, but the way this works is that we begin with an `FUnknown` - * pointer from the Windows VST3 plugin obtained by a call to - * `IPluginFactory::createInstance()` (with an interface decided by the host). - * We then go through all the plugin interfaces and check whether that object - * supports them one by one. For each supported interface we remember that the - * plugin supports it, and we'll optionally write down some static data (such as - * the edit controller cid) that can't change over the lifetime of the - * application. On the plugin side we then return a `YaPluginMonolith` - * implementation that contains all of this information about interfaces the - * object we're proxying might support. This way we can allow casts to all of - * those object types in `queryInterface()`, essentially perfectly mimicing the - * original object. - * - * This monolith approach is also important when it comes to `IConnectionPoint`. - * The host should be able to connect arbitrary objects together, and the plugin - * can then use the query interface smart pointer casting system to cast those - * objects to the types they want. By having a huge monolithic class that - * implements any interface such an object might also implement, we can allow - * perfect proxying behaviour for connecting components. - */ -class YaPluginMonolith : public YaAudioProcessor, - public YaComponent, - public YaPluginBase { - public: - /** - * These are the arguments for creating a `YaPluginMonolithImpl`. - */ - struct ConstructArgs { - ConstructArgs(); - - /** - * Read from an existing object. We will try to mimic this object, so - * we'll support any interfaces this object also supports. - */ - ConstructArgs(Steinberg::IPtr object, size_t instance_id); - - /** - * The unique identifier for this specific object instance. - */ - native_size_t instance_id; - - YaAudioProcessor::ConstructArgs audio_processor_args; - YaComponent::ConstructArgs component_args; - YaPluginBase::ConstructArgs plugin_base_args; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.object(audio_processor_args); - s.object(component_args); - s.object(plugin_base_args); - } - }; - - /** - * Message to request the Wine plugin host to instantiate a new IComponent - * to pass through a call to `IComponent::createInstance(cid, - * IComponent::iid, ...)`. - */ - struct Construct { - using Response = std::variant; - - ArrayUID cid; - - // TODO: Add an enum class to reify the type of object we want to - // instantiate so we can initialize things other than - // `IComponent`, like `IEditController.` - - template - void serialize(S& s) { - s.container1b(cid); - } - }; - - /** - * Instantiate this object instance with arguments read from another - * interface implementation. - */ - YaPluginMonolith(const ConstructArgs&& args); - - /** - * Message to request the Wine plugin host to destroy this object instance - * with the given instance ID. Sent from the destructor of - * `YaPluginMonolithImpl`. This will cause all smart pointers to the actual - * object in the Wine plugin host to be dropped. - */ - struct Destruct { - using Response = Ack; - - native_size_t instance_id; - - template - void serialize(S& s) { - s.value8b(instance_id); - } - }; - - /** - * @remark The plugin side implementation should send a control message to - * clean up the instance on the Wine side in its destructor. - */ - virtual ~YaPluginMonolith() = 0; - - DECLARE_FUNKNOWN_METHODS - - protected: - ConstructArgs arguments; -}; - -#pragma GCC diagnostic pop - -template -void serialize( - S& s, - std::variant& result) { - s.ext(result, bitsery::ext::StdVariant{}); -} +Vst3PluginProxyVst3PluginProxyVst3PluginProxyVst3PluginProxy diff --git a/src/common/serialization/vst3/plugin-monolith.cpp b/src/common/serialization/vst3/plugin-proxy.cpp similarity index 85% rename from src/common/serialization/vst3/plugin-monolith.cpp rename to src/common/serialization/vst3/plugin-proxy.cpp index 814185a3..a286abc5 100644 --- a/src/common/serialization/vst3/plugin-monolith.cpp +++ b/src/common/serialization/vst3/plugin-proxy.cpp @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "plugin-monolith.h" +#include "plugin-proxy.h" -YaPluginMonolith::ConstructArgs::ConstructArgs() {} +Vst3PluginProxy::ConstructArgs::ConstructArgs() {} -YaPluginMonolith::ConstructArgs::ConstructArgs( +Vst3PluginProxy::ConstructArgs::ConstructArgs( Steinberg::IPtr object, size_t instance_id) : instance_id(instance_id), @@ -26,23 +26,23 @@ YaPluginMonolith::ConstructArgs::ConstructArgs( component_args(object), plugin_base_args(object) {} -YaPluginMonolith::YaPluginMonolith(const ConstructArgs&& args) +Vst3PluginProxy::Vst3PluginProxy(const ConstructArgs&& args) : YaAudioProcessor(std::move(args.audio_processor_args)), YaComponent(std::move(args.component_args)), YaPluginBase(std::move(args.plugin_base_args)), arguments(std::move(args)){FUNKNOWN_CTOR} - YaPluginMonolith::~YaPluginMonolith() { + Vst3PluginProxy::~Vst3PluginProxy() { FUNKNOWN_DTOR } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" -IMPLEMENT_REFCOUNT(YaPluginMonolith) +IMPLEMENT_REFCOUNT(Vst3PluginProxy) #pragma GCC diagnostic pop -tresult PLUGIN_API YaPluginMonolith::queryInterface(Steinberg::FIDString _iid, - void** obj) { +tresult PLUGIN_API Vst3PluginProxy::queryInterface(Steinberg::FIDString _iid, + void** obj) { if (YaPluginBase::supported()) { // We had to expand the macro here because we need to cast through // `YaPluginBase`, since `IpluginBase` is also a base of `IComponent` diff --git a/src/common/serialization/vst3/plugin-proxy.h b/src/common/serialization/vst3/plugin-proxy.h new file mode 100644 index 00000000..c6bf9fc2 --- /dev/null +++ b/src/common/serialization/vst3/plugin-proxy.h @@ -0,0 +1,151 @@ +// 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 . + +#pragma once + +#include +#include + +#include "../common.h" +#include "audio-processor.h" +#include "base.h" +#include "component.h" +#include "host-application.h" +#include "plugin-base.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +/** + * An abstract class that optionally implements all VST3 interfaces a plugin + * object could implement. A more in depth explanation can be found in + * `docs/vst3.md`, but the way this works is that we begin with an `FUnknown` + * pointer from the Windows VST3 plugin obtained by a call to + * `IPluginFactory::createInstance()` (with an interface decided by the host). + * We then go through all the plugin interfaces and check whether that object + * supports them one by one. For each supported interface we remember that the + * plugin supports it, and we'll optionally write down some static data (such as + * the edit controller cid) that can't change over the lifetime of the + * application. On the plugin side we then return a `Vst3PluginProxyImpl` object + * that contains all of this information about interfaces the object we're + * proxying might support. This way we can allow casts to all of those object + * types in `queryInterface()`, essentially perfectly mimicing the original + * object. + * + * This monolith approach is also important when it comes to `IConnectionPoint`. + * The host should be able to connect arbitrary objects together, and the plugin + * can then use the query interface smart pointer casting system to cast those + * objects to the types they want. By having a huge monolithic class that + * implements any interface such an object might also implement, we can allow + * perfect proxying behaviour for connecting components. + */ +class Vst3PluginProxy : public YaAudioProcessor, + public YaComponent, + public YaPluginBase { + public: + /** + * These are the arguments for creating a `Vst3PluginProxyImpl`. + */ + struct ConstructArgs { + ConstructArgs(); + + /** + * Read from an existing object. We will try to mimic this object, so + * we'll support any interfaces this object also supports. + */ + ConstructArgs(Steinberg::IPtr object, size_t instance_id); + + /** + * The unique identifier for this specific object instance. + */ + native_size_t instance_id; + + YaAudioProcessor::ConstructArgs audio_processor_args; + YaComponent::ConstructArgs component_args; + YaPluginBase::ConstructArgs plugin_base_args; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.object(audio_processor_args); + s.object(component_args); + s.object(plugin_base_args); + } + }; + + /** + * Message to request the Wine plugin host to instantiate a new IComponent + * to pass through a call to `IComponent::createInstance(cid, + * IComponent::iid, ...)`. + */ + struct Construct { + using Response = std::variant; + + ArrayUID cid; + + // TODO: Add an enum class to reify the type of object we want to + // instantiate so we can initialize things other than + // `IComponent`, like `IEditController.` + + template + void serialize(S& s) { + s.container1b(cid); + } + }; + + /** + * Instantiate this object instance with arguments read from another + * interface implementation. + */ + Vst3PluginProxy(const ConstructArgs&& args); + + /** + * Message to request the Wine plugin host to destroy this object instance + * with the given instance ID. Sent from the destructor of + * `Vst3PluginProxyImpl`. This will cause all smart pointers to the actual + * object in the Wine plugin host to be dropped. + */ + struct Destruct { + using Response = Ack; + + native_size_t instance_id; + + template + void serialize(S& s) { + s.value8b(instance_id); + } + }; + + /** + * @remark The plugin side implementation should send a control message to + * clean up the instance on the Wine side in its destructor. + */ + virtual ~Vst3PluginProxy() = 0; + + DECLARE_FUNKNOWN_METHODS + + protected: + ConstructArgs arguments; +}; + +#pragma GCC diagnostic pop + +template +void serialize( + S& s, + std::variant& result) { + s.ext(result, bitsery::ext::StdVariant{}); +} diff --git a/src/plugin/bridges/vst3-impls/component.h b/src/plugin/bridges/vst3-impls/component.h index 03ecdbd7..bb620bb3 100644 --- a/src/plugin/bridges/vst3-impls/component.h +++ b/src/plugin/bridges/vst3-impls/component.h @@ -1,95 +1 @@ -// 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 . - -#pragma once - -#include - -#include "../vst3.h" - -class YaPluginMonolithImpl : public YaPluginMonolith { - public: - YaPluginMonolithImpl(Vst3PluginBridge& bridge, - YaPluginMonolith::ConstructArgs&& args); - - /** - * When the reference count reaches zero and this destructor is called, - * we'll send a request to the Wine plugin host to destroy the corresponding - * object. - */ - ~YaPluginMonolithImpl(); - - /** - * We'll override the query interface to log queries for interfaces we do - * not (yet) support. - */ - tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, - void** obj) override; - - // From `IAudioProcessor` - tresult PLUGIN_API - setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, - int32 numIns, - Steinberg::Vst::SpeakerArrangement* outputs, - int32 numOuts) override; - tresult PLUGIN_API - getBusArrangement(Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::SpeakerArrangement& arr) override; - tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override; - uint32 PLUGIN_API getLatencySamples() override; - tresult PLUGIN_API - setupProcessing(Steinberg::Vst::ProcessSetup& setup) override; - tresult PLUGIN_API setProcessing(TBool state) override; - tresult PLUGIN_API process(Steinberg::Vst::ProcessData& data) override; - uint32 PLUGIN_API getTailSamples() override; - - // From `IComponent` - tresult PLUGIN_API setIoMode(Steinberg::Vst::IoMode mode) override; - int32 PLUGIN_API getBusCount(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir) override; - tresult PLUGIN_API - getBusInfo(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::BusInfo& bus /*out*/) override; - tresult PLUGIN_API - getRoutingInfo(Steinberg::Vst::RoutingInfo& inInfo, - Steinberg::Vst::RoutingInfo& outInfo /*out*/) override; - tresult PLUGIN_API activateBus(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir, - int32 index, - TBool state) override; - tresult PLUGIN_API setActive(TBool state) override; - tresult PLUGIN_API setState(Steinberg::IBStream* state) override; - tresult PLUGIN_API getState(Steinberg::IBStream* state) override; - - // From `IPluginBase` - tresult PLUGIN_API initialize(FUnknown* context) override; - tresult PLUGIN_API terminate() override; - - private: - Vst3PluginBridge& bridge; - - /** - * An `IHostApplication` instance if we get one through - * `IPluginBase::initialize()`. This should be the same for all plugin - * instances so we should not have to store it here separately, but for the - * sake of correctness we will. - */ - Steinberg::FUnknownPtr - host_application_context; -}; +YaPluginProxyImplYaPluginProxyImplYaPluginProxyImpl diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.cpp b/src/plugin/bridges/vst3-impls/plugin-factory.cpp index 6b65b2e1..06a8319f 100644 --- a/src/plugin/bridges/vst3-impls/plugin-factory.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-factory.cpp @@ -18,17 +18,16 @@ #include -#include "component.h" +#include "plugin-proxy.h" -YaPluginFactoryPluginImpl::YaPluginFactoryPluginImpl( - Vst3PluginBridge& bridge, - YaPluginFactory::ConstructArgs&& args) +YaPluginFactoryImpl::YaPluginFactoryImpl(Vst3PluginBridge& bridge, + YaPluginFactory::ConstructArgs&& args) : YaPluginFactory(std::move(args)), bridge(bridge) {} tresult PLUGIN_API -YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid, - Steinberg::FIDString _iid, - void** obj) { +YaPluginFactoryImpl::createInstance(Steinberg::FIDString cid, + Steinberg::FIDString _iid, + void** obj) { // TODO: Do the same thing for other types // These arw pointers are scary. The idea here is that we return a newly @@ -38,13 +37,13 @@ YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid, ArrayUID cid_array; std::copy(cid, cid + sizeof(Steinberg::TUID), cid_array.begin()); if (Steinberg::FIDStringsEqual(_iid, Steinberg::Vst::IComponent::iid)) { - std::variant result = - bridge.send_message(YaPluginMonolith::Construct{.cid = cid_array}); + std::variant result = + bridge.send_message(Vst3PluginProxy::Construct{.cid = cid_array}); return std::visit( overload{ - [&](YaPluginMonolith::ConstructArgs&& args) -> tresult { + [&](Vst3PluginProxy::ConstructArgs&& args) -> tresult { *obj = static_cast( - new YaPluginMonolithImpl(bridge, std::move(args))); + new Vst3PluginProxyImpl(bridge, std::move(args))); return Steinberg::kResultOk; }, [&](const UniversalTResult& code) -> tresult { return code; }}, @@ -69,7 +68,7 @@ YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid, } tresult PLUGIN_API -YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* context) { +YaPluginFactoryImpl::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 diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.h b/src/plugin/bridges/vst3-impls/plugin-factory.h index 30a2f093..7743f2d2 100644 --- a/src/plugin/bridges/vst3-impls/plugin-factory.h +++ b/src/plugin/bridges/vst3-impls/plugin-factory.h @@ -18,11 +18,10 @@ #include "../vst3.h" -// TODO Rename to YaPluginFactoryImpl -class YaPluginFactoryPluginImpl : public YaPluginFactory { +class YaPluginFactoryImpl : public YaPluginFactory { public: - YaPluginFactoryPluginImpl(Vst3PluginBridge& bridge, - YaPluginFactory::ConstructArgs&& args); + YaPluginFactoryImpl(Vst3PluginBridge& bridge, + YaPluginFactory::ConstructArgs&& args); tresult PLUGIN_API createInstance(Steinberg::FIDString cid, Steinberg::FIDString _iid, diff --git a/src/plugin/bridges/vst3-impls/component.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp similarity index 75% rename from src/plugin/bridges/vst3-impls/component.cpp rename to src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 8f802e5a..e79bb986 100644 --- a/src/plugin/bridges/vst3-impls/component.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -14,25 +14,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "component.h" +#include "plugin-proxy.h" -YaPluginMonolithImpl::YaPluginMonolithImpl( - Vst3PluginBridge& bridge, - YaPluginMonolith::ConstructArgs&& args) - : YaPluginMonolith(std::move(args)), bridge(bridge) { +Vst3PluginProxyImpl::Vst3PluginProxyImpl(Vst3PluginBridge& bridge, + Vst3PluginProxy::ConstructArgs&& args) + : Vst3PluginProxy(std::move(args)), bridge(bridge) { bridge.register_component(arguments.instance_id, *this); } -YaPluginMonolithImpl::~YaPluginMonolithImpl() { +Vst3PluginProxyImpl::~Vst3PluginProxyImpl() { bridge.send_message( - YaPluginMonolith::Destruct{.instance_id = arguments.instance_id}); + Vst3PluginProxy::Destruct{.instance_id = arguments.instance_id}); bridge.unregister_component(arguments.instance_id); } tresult PLUGIN_API -YaPluginMonolithImpl::queryInterface(const Steinberg::TUID _iid, void** obj) { +Vst3PluginProxyImpl::queryInterface(const Steinberg::TUID _iid, void** obj) { // TODO: Successful queries should also be logged - const tresult result = YaPluginMonolith::queryInterface(_iid, obj); + const tresult result = Vst3PluginProxy::queryInterface(_iid, obj); if (result != Steinberg::kResultOk) { bridge.logger.log_unknown_interface("In IComponent::queryInterface()", Steinberg::FUID::fromTUID(_iid)); @@ -41,7 +40,7 @@ YaPluginMonolithImpl::queryInterface(const Steinberg::TUID _iid, void** obj) { return result; } -tresult PLUGIN_API YaPluginMonolithImpl::setBusArrangements( +tresult PLUGIN_API Vst3PluginProxyImpl::setBusArrangements( Steinberg::Vst::SpeakerArrangement* inputs, int32 numIns, Steinberg::Vst::SpeakerArrangement* outputs, @@ -58,7 +57,7 @@ tresult PLUGIN_API YaPluginMonolithImpl::setBusArrangements( }); } -tresult PLUGIN_API YaPluginMonolithImpl::getBusArrangement( +tresult PLUGIN_API Vst3PluginProxyImpl::getBusArrangement( Steinberg::Vst::BusDirection dir, int32 index, Steinberg::Vst::SpeakerArrangement& arr) { @@ -75,30 +74,30 @@ tresult PLUGIN_API YaPluginMonolithImpl::getBusArrangement( } tresult PLUGIN_API -YaPluginMonolithImpl::canProcessSampleSize(int32 symbolicSampleSize) { +Vst3PluginProxyImpl::canProcessSampleSize(int32 symbolicSampleSize) { return bridge.send_message(YaAudioProcessor::CanProcessSampleSize{ .instance_id = arguments.instance_id, .symbolic_sample_size = symbolicSampleSize}); } -uint32 PLUGIN_API YaPluginMonolithImpl::getLatencySamples() { +uint32 PLUGIN_API Vst3PluginProxyImpl::getLatencySamples() { return bridge.send_message(YaAudioProcessor::GetLatencySamples{ .instance_id = arguments.instance_id}); } tresult PLUGIN_API -YaPluginMonolithImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { +Vst3PluginProxyImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { return bridge.send_message(YaAudioProcessor::SetupProcessing{ .instance_id = arguments.instance_id, .setup = setup}); } -tresult PLUGIN_API YaPluginMonolithImpl::setProcessing(TBool state) { +tresult PLUGIN_API Vst3PluginProxyImpl::setProcessing(TBool state) { return bridge.send_message(YaAudioProcessor::SetProcessing{ .instance_id = arguments.instance_id, .state = state}); } tresult PLUGIN_API -YaPluginMonolithImpl::process(Steinberg::Vst::ProcessData& data) { +Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) { ProcessResponse response = bridge.send_message(YaAudioProcessor::Process{ .instance_id = arguments.instance_id, .data = data}); @@ -107,29 +106,28 @@ YaPluginMonolithImpl::process(Steinberg::Vst::ProcessData& data) { return response.result; } -uint32 PLUGIN_API YaPluginMonolithImpl::getTailSamples() { +uint32 PLUGIN_API Vst3PluginProxyImpl::getTailSamples() { return bridge.send_message( YaAudioProcessor::GetTailSamples{.instance_id = arguments.instance_id}); } -tresult PLUGIN_API -YaPluginMonolithImpl::setIoMode(Steinberg::Vst::IoMode mode) { +tresult PLUGIN_API Vst3PluginProxyImpl::setIoMode(Steinberg::Vst::IoMode mode) { return bridge.send_message(YaComponent::SetIoMode{ .instance_id = arguments.instance_id, .mode = mode}); } int32 PLUGIN_API -YaPluginMonolithImpl::getBusCount(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir) { +Vst3PluginProxyImpl::getBusCount(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir) { return bridge.send_message(YaComponent::GetBusCount{ .instance_id = arguments.instance_id, .type = type, .dir = dir}); } tresult PLUGIN_API -YaPluginMonolithImpl::getBusInfo(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::BusInfo& bus /*out*/) { +Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::BusInfo& bus /*out*/) { const GetBusInfoResponse response = bridge.send_message( YaComponent::GetBusInfo{.instance_id = arguments.instance_id, .type = type, @@ -141,7 +139,7 @@ YaPluginMonolithImpl::getBusInfo(Steinberg::Vst::MediaType type, return response.result; } -tresult PLUGIN_API YaPluginMonolithImpl::getRoutingInfo( +tresult PLUGIN_API Vst3PluginProxyImpl::getRoutingInfo( Steinberg::Vst::RoutingInfo& inInfo, Steinberg::Vst::RoutingInfo& outInfo /*out*/) { const GetRoutingInfoResponse response = bridge.send_message( @@ -155,10 +153,10 @@ tresult PLUGIN_API YaPluginMonolithImpl::getRoutingInfo( } tresult PLUGIN_API -YaPluginMonolithImpl::activateBus(Steinberg::Vst::MediaType type, - Steinberg::Vst::BusDirection dir, - int32 index, - TBool state) { +Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir, + int32 index, + TBool state) { return bridge.send_message( YaComponent::ActivateBus{.instance_id = arguments.instance_id, .type = type, @@ -167,17 +165,17 @@ YaPluginMonolithImpl::activateBus(Steinberg::Vst::MediaType type, .state = state}); } -tresult PLUGIN_API YaPluginMonolithImpl::setActive(TBool state) { +tresult PLUGIN_API Vst3PluginProxyImpl::setActive(TBool state) { return bridge.send_message(YaComponent::SetActive{ .instance_id = arguments.instance_id, .state = state}); } -tresult PLUGIN_API YaPluginMonolithImpl::setState(Steinberg::IBStream* state) { +tresult PLUGIN_API Vst3PluginProxyImpl::setState(Steinberg::IBStream* state) { return bridge.send_message(YaComponent::SetState{ .instance_id = arguments.instance_id, .state = state}); } -tresult PLUGIN_API YaPluginMonolithImpl::getState(Steinberg::IBStream* state) { +tresult PLUGIN_API Vst3PluginProxyImpl::getState(Steinberg::IBStream* state) { const GetStateResponse response = bridge.send_message( YaComponent::GetState{.instance_id = arguments.instance_id}); @@ -186,7 +184,7 @@ tresult PLUGIN_API YaPluginMonolithImpl::getState(Steinberg::IBStream* state) { return response.result; } -tresult PLUGIN_API YaPluginMonolithImpl::initialize(FUnknown* context) { +tresult PLUGIN_API Vst3PluginProxyImpl::initialize(FUnknown* context) { // This `context` will likely be an `IHostApplication`. If it is, we will // store it here, and we'll proxy through all calls to it made from the Wine // side. Otherwise we'll still call `IPluginBase::initialize()` but with a @@ -210,7 +208,7 @@ tresult PLUGIN_API YaPluginMonolithImpl::initialize(FUnknown* context) { std::move(host_application_context_args)}); } -tresult PLUGIN_API YaPluginMonolithImpl::terminate() { +tresult PLUGIN_API Vst3PluginProxyImpl::terminate() { return bridge.send_message( YaPluginBase::Terminate{.instance_id = arguments.instance_id}); } diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.h b/src/plugin/bridges/vst3-impls/plugin-proxy.h new file mode 100644 index 00000000..f7ec18c7 --- /dev/null +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.h @@ -0,0 +1,95 @@ +// 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 . + +#pragma once + +#include + +#include "../vst3.h" + +class Vst3PluginProxyImpl : public Vst3PluginProxy { + public: + Vst3PluginProxyImpl(Vst3PluginBridge& bridge, + Vst3PluginProxy::ConstructArgs&& args); + + /** + * When the reference count reaches zero and this destructor is called, + * we'll send a request to the Wine plugin host to destroy the corresponding + * object. + */ + ~Vst3PluginProxyImpl(); + + /** + * We'll override the query interface to log queries for interfaces we do + * not (yet) support. + */ + tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, + void** obj) override; + + // From `IAudioProcessor` + tresult PLUGIN_API + setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, + int32 numIns, + Steinberg::Vst::SpeakerArrangement* outputs, + int32 numOuts) override; + tresult PLUGIN_API + getBusArrangement(Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::SpeakerArrangement& arr) override; + tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override; + uint32 PLUGIN_API getLatencySamples() override; + tresult PLUGIN_API + setupProcessing(Steinberg::Vst::ProcessSetup& setup) override; + tresult PLUGIN_API setProcessing(TBool state) override; + tresult PLUGIN_API process(Steinberg::Vst::ProcessData& data) override; + uint32 PLUGIN_API getTailSamples() override; + + // From `IComponent` + tresult PLUGIN_API setIoMode(Steinberg::Vst::IoMode mode) override; + int32 PLUGIN_API getBusCount(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir) override; + tresult PLUGIN_API + getBusInfo(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::BusInfo& bus /*out*/) override; + tresult PLUGIN_API + getRoutingInfo(Steinberg::Vst::RoutingInfo& inInfo, + Steinberg::Vst::RoutingInfo& outInfo /*out*/) override; + tresult PLUGIN_API activateBus(Steinberg::Vst::MediaType type, + Steinberg::Vst::BusDirection dir, + int32 index, + TBool state) override; + tresult PLUGIN_API setActive(TBool state) override; + tresult PLUGIN_API setState(Steinberg::IBStream* state) override; + tresult PLUGIN_API getState(Steinberg::IBStream* state) override; + + // From `IPluginBase` + tresult PLUGIN_API initialize(FUnknown* context) override; + tresult PLUGIN_API terminate() override; + + private: + Vst3PluginBridge& bridge; + + /** + * An `IHostApplication` instance if we get one through + * `IPluginBase::initialize()`. This should be the same for all plugin + * instances so we should not have to store it here separately, but for the + * sake of correctness we will. + */ + Steinberg::FUnknownPtr + host_application_context; +}; diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 61ae85a1..b2c16499 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -108,17 +108,17 @@ Steinberg::IPluginFactory* Vst3PluginBridge::get_plugin_factory() { YaPluginFactory::Construct{}, std::pair(logger, true)); plugin_factory = - new YaPluginFactoryPluginImpl(*this, std::move(factory_args)); + new YaPluginFactoryImpl(*this, std::move(factory_args)); } return plugin_factory; } void Vst3PluginBridge::register_component(size_t instance_id, - YaPluginMonolithImpl& component) { + Vst3PluginProxyImpl& component) { std::lock_guard lock(component_instances_mutex); component_instances.emplace(instance_id, - std::ref(component)); + std::ref(component)); } void Vst3PluginBridge::unregister_component(size_t instance_id) { diff --git a/src/plugin/bridges/vst3.h b/src/plugin/bridges/vst3.h index 341b402c..ad6fb009 100644 --- a/src/plugin/bridges/vst3.h +++ b/src/plugin/bridges/vst3.h @@ -24,7 +24,7 @@ #include "common.h" // Forward declaration -class YaPluginMonolithImpl; +class Vst3PluginProxyImpl; /** * This handles the communication between the native host and a VST3 plugin @@ -86,8 +86,7 @@ class Vst3PluginBridge : PluginBridge> { * * TODO: REname to `register_instance` or `register_object` */ - void register_component(size_t instance_id, - YaPluginMonolithImpl& component); + void register_component(size_t instance_id, Vst3PluginProxyImpl& component); /** * Remove a previously registered `YaComponentPluginImpl` from the list of @@ -151,7 +150,7 @@ class Vst3PluginBridge : PluginBridge> { * `register_component()` in the constractor, and an instance is then * removed through a call to `unregister_component()` in the destructor. */ - std::map> + std::map> component_instances; std::mutex component_instances_mutex; }; diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 57dfa2b6..816746b9 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -60,8 +60,8 @@ void Vst3Bridge::run() { sockets.host_vst_control.receive_messages( std::nullopt, overload{ - [&](const YaPluginMonolith::Construct& args) - -> YaPluginMonolith::Construct::Response { + [&](const Vst3PluginProxy::Construct& args) + -> Vst3PluginProxy::Construct::Response { Steinberg::TUID cid; std::copy(args.cid.begin(), args.cid.end(), cid); Steinberg::IPtr component = @@ -73,7 +73,7 @@ void Vst3Bridge::run() { const size_t instance_id = generate_instance_id(); component_instances[instance_id] = std::move(component); - return YaPluginMonolith::ConstructArgs( + return Vst3PluginProxy::ConstructArgs( component_instances[instance_id].component, instance_id); } else { @@ -81,8 +81,8 @@ void Vst3Bridge::run() { return UniversalTResult(Steinberg::kNotImplemented); } }, - [&](const YaPluginMonolith::Destruct& request) - -> YaPluginMonolith::Destruct::Response { + [&](const Vst3PluginProxy::Destruct& request) + -> Vst3PluginProxy::Destruct::Response { std::lock_guard lock(component_instances_mutex); component_instances.erase(request.instance_id); @@ -202,8 +202,8 @@ void Vst3Bridge::run() { -> YaPluginBase::Initialize::Response { // If we got passed a host context, we'll create a proxy object // and pass that to the initialize function. This object should - // be cleaned up again during `YaPluginMonolith::Destruct`. - // TOOD: This needs changing when we get to `YaHostMonolith` + // be cleaned up again during `Vst3PluginProxy::Destruct`. + // TODO: This needs changing when we get to `Vst3HostProxy` Steinberg::FUnknown* context = nullptr; if (request.host_application_context_args) { component_instances[request.instance_id]