diff --git a/docs/vst3.md b/docs/vst3.md
index a5ef5413..df08998b 100644
--- a/docs/vst3.md
+++ b/docs/vst3.md
@@ -7,6 +7,8 @@ TODO: Link to `src/common/serialization/vst3/README.md`
TODO: Mention the new `Ya::supports()` mechanism for monolithic interfaces
through multiple inheritance
+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
dynamic casting interface through `queryInterface()` and a reference counting
diff --git a/meson.build b/meson.build
index ef56acd9..99c9644e 100644
--- a/meson.build
+++ b/meson.build
@@ -85,6 +85,7 @@ 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-factory.cpp',
'src/common/serialization/vst3/process-data.cpp',
'src/common/configuration.cpp',
@@ -126,6 +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-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 18229a1f..51140247 100644
--- a/src/common/logging/vst3.cpp
+++ b/src/common/logging/vst3.cpp
@@ -37,7 +37,30 @@ void Vst3Logger::log_unknown_interface(
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::SetBusArrangements& request) {
+ const YaPluginMonolith::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
+ // TODO: When adding the enum class for instantiating different types,
+ // make sure to reflect those in the constructor and destructor
+ // logging
+ message << "IPluginFactory::createComponent(cid = ..., _iid = "
+ "IComponent::iid, "
+ "&obj)";
+ });
+}
+
+void Vst3Logger::log_request(bool is_host_vst,
+ const YaPluginMonolith::Destruct& request) {
+ log_request_base(is_host_vst, [&](auto& message) {
+ message << "::~IComponent()";
+ });
+}
+
+void Vst3Logger::log_request(
+ bool is_host_vst,
+ const YaAudioProcessor::SetBusArrangements& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::setBusArrangements(inputs = [SpeakerArrangement; "
@@ -48,7 +71,7 @@ void Vst3Logger::log_request(bool is_host_vst,
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::GetBusArrangement& request) {
+ const YaAudioProcessor::GetBusArrangement& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::getBusArrangement(dir = " << request.dir
@@ -57,7 +80,7 @@ void Vst3Logger::log_request(bool is_host_vst,
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::CanProcessSampleSize& request) {
+ const YaAudioProcessor::CanProcessSampleSize& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::canProcessSampleSize(symbolicSampleSize = "
@@ -65,8 +88,9 @@ void Vst3Logger::log_request(bool is_host_vst,
});
}
-void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::GetLatencySamples& request) {
+void Vst3Logger::log_request(
+ bool is_host_vst,
+ const YaAudioProcessor::GetLatencySamples& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::getLatencySamples()";
@@ -74,7 +98,7 @@ void Vst3Logger::log_request(bool is_host_vst,
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::SetupProcessing& request) {
+ const YaAudioProcessor::SetupProcessing& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::setupProcessing(setup = ::setProcessing(state = "
@@ -95,7 +119,7 @@ void Vst3Logger::log_request(bool is_host_vst,
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::Process& request) {
+ const YaAudioProcessor::Process& request) {
// TODO: Only log this on log level 2
log_request_base(is_host_vst, [&](auto& message) {
// TODO: Log about the process data
@@ -105,31 +129,13 @@ void Vst3Logger::log_request(bool is_host_vst,
}
void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::GetTailSamples& request) {
+ const YaAudioProcessor::GetTailSamples& request) {
log_request_base(is_host_vst, [&](auto& message) {
message << "::getTailSamples()";
});
}
-void Vst3Logger::log_request(bool is_host_vst, const YaComponent::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
- message << "IPluginFactory::createComponent(cid = ..., _iid = "
- "IComponent::iid, "
- "&obj)";
- });
-}
-
-void Vst3Logger::log_request(bool is_host_vst,
- const YaComponent::Destruct& request) {
- log_request_base(is_host_vst, [&](auto& message) {
- message << "::~IComponent()";
- });
-}
-
void Vst3Logger::log_request(bool is_host_vst,
const YaComponent::SetIoMode& request) {
log_request_base(is_host_vst, [&](auto& message) {
@@ -246,6 +252,26 @@ void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
});
}
+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) {
+ log_response_base(is_host_vst, [&](auto& message) {
+ std::visit(overload{[&](const YaPluginMonolith::ConstructArgs& args) {
+ message << "";
+ },
+ [&](const UniversalTResult& code) {
+ message << code.string();
+ }},
+ result);
+ });
+}
+
void Vst3Logger::log_response(
bool is_host_vst,
const YaAudioProcessor::GetBusArrangementResponse& response) {
@@ -268,25 +294,6 @@ void Vst3Logger::log_response(
});
}
-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) {
- log_response_base(is_host_vst, [&](auto& message) {
- std::visit(overload{[&](const YaComponent::ConstructArgs& args) {
- message << "";
- },
- [&](const UniversalTResult& code) {
- message << code.string();
- }},
- result);
- });
-}
-
void Vst3Logger::log_response(bool is_host_vst,
const YaComponent::GetBusInfoResponse& response) {
log_response_base(is_host_vst, [&](auto& message) {
diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h
index 53e92a65..a45b2e41 100644
--- a/src/common/logging/vst3.h
+++ b/src/common/logging/vst3.h
@@ -56,6 +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 YaAudioProcessor::SetBusArrangements&);
void log_request(bool is_host_vst,
@@ -69,8 +71,6 @@ class Vst3Logger {
void log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&);
void log_request(bool is_host_vst, const YaAudioProcessor::Process&);
void log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&);
- 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::SetIoMode&);
void log_request(bool is_host_vst, const YaComponent::GetBusCount&);
void log_request(bool is_host_vst, const YaComponent::GetBusInfo&);
@@ -86,13 +86,13 @@ class Vst3Logger {
void log_request(bool is_host_vst, const WantsConfiguration&);
void log_response(bool is_host_vst, const Ack&);
+ void log_response(
+ bool is_host_vst,
+ const std::variant&);
void log_response(bool is_host_vst,
const YaAudioProcessor::GetBusArrangementResponse&);
void log_response(bool is_host_vst,
const YaAudioProcessor::ProcessResponse&);
- void log_response(
- bool is_host_vst,
- const std::variant&);
void log_response(bool is_host_vst, const YaComponent::GetBusInfoResponse&);
void log_response(bool is_host_vst,
const YaComponent::GetRoutingInfoResponse&);
diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h
index 304899fd..d8aa9a54 100644
--- a/src/common/serialization/vst3.h
+++ b/src/common/serialization/vst3.h
@@ -23,8 +23,8 @@
#include "../configuration.h"
#include "../utils.h"
#include "common.h"
-#include "vst3/component.h"
#include "vst3/plugin-factory.h"
+#include "vst3/plugin-monolith.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,16 @@ 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 component)
+ Steinberg::IPtr object)
: supported(
- Steinberg::FUnknownPtr(component)) {}
+ Steinberg::FUnknownPtr(object)) {}
YaAudioProcessor::YaAudioProcessor(const ConstructArgs&& args)
: arguments(std::move(args)) {}
diff --git a/src/common/serialization/vst3/audio-processor.h b/src/common/serialization/vst3/audio-processor.h
index 140c014a..f3874fd7 100644
--- a/src/common/serialization/vst3/audio-processor.h
+++ b/src/common/serialization/vst3/audio-processor.h
@@ -29,7 +29,7 @@
/**
* Wraps around `IAudioProcessor` for serialization purposes. This is
- * instantiated as part of `YaComponent`.
+ * instantiated as part of `YaPluginMonolith`.
*/
class YaAudioProcessor : public Steinberg::Vst::IAudioProcessor {
public:
@@ -40,8 +40,8 @@ class YaAudioProcessor : public Steinberg::Vst::IAudioProcessor {
ConstructArgs();
/**
- * Check whether an existing implementation implements `IPluginBase` and
- * read arguments from it.
+ * Check whether an existing implementation implements `IAudioProcessor`
+ * and read arguments from it.
*/
ConstructArgs(Steinberg::IPtr object);
diff --git a/src/common/serialization/vst3/component.cpp b/src/common/serialization/vst3/component.cpp
index 1ae2c548..5d121d66 100644
--- a/src/common/serialization/vst3/component.cpp
+++ b/src/common/serialization/vst3/component.cpp
@@ -19,57 +19,22 @@
YaComponent::ConstructArgs::ConstructArgs() {}
YaComponent::ConstructArgs::ConstructArgs(
- Steinberg::IPtr component,
- size_t instance_id)
- : instance_id(instance_id),
- audio_processor_args(component),
- plugin_base_args(component) {
- // `IComponent::getControllerClassId`
- Steinberg::TUID cid;
- if (component->getControllerClassId(cid) == Steinberg::kResultOk) {
- edit_controller_cid = std::to_array(cid);
+ Steinberg::IPtr object) {
+ auto component = Steinberg::FUnknownPtr(object);
+
+ if (component) {
+ supported = true;
+
+ // `IComponent::getControllerClassId`
+ Steinberg::TUID cid;
+ if (component->getControllerClassId(cid) == Steinberg::kResultOk) {
+ edit_controller_cid = std::to_array(cid);
+ }
}
}
YaComponent::YaComponent(const ConstructArgs&& args)
- : YaAudioProcessor(std::move(args.audio_processor_args)),
- YaPluginBase(std::move(args.plugin_base_args)),
- arguments(std::move(args)){FUNKNOWN_CTOR}
-
- YaComponent::~YaComponent() {
- FUNKNOWN_DTOR
-}
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
-IMPLEMENT_REFCOUNT(YaComponent)
-#pragma GCC diagnostic pop
-
-tresult PLUGIN_API YaComponent::queryInterface(Steinberg::FIDString _iid,
- void** obj) {
- QUERY_INTERFACE(_iid, obj, Steinberg::FUnknown::iid,
- Steinberg::Vst::IComponent)
- 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`
- if (Steinberg::FUnknownPrivate ::iidEqual(
- _iid, Steinberg::IPluginBase::iid)) {
- addRef();
- *obj = static_cast(
- static_cast(this));
- return ::Steinberg ::kResultOk;
- }
- }
- QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IComponent::iid,
- Steinberg::Vst::IComponent)
- if (YaAudioProcessor::supported()) {
- QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IAudioProcessor::iid,
- Steinberg::Vst::IAudioProcessor)
- }
-
- *obj = nullptr;
- return Steinberg::kNoInterface;
-}
+ : arguments(std::move(args)) {}
tresult PLUGIN_API YaComponent::getControllerClassId(Steinberg::TUID classId) {
if (arguments.edit_controller_cid) {
diff --git a/src/common/serialization/vst3/component.h b/src/common/serialization/vst3/component.h
index 09c4f714..f0579990 100644
--- a/src/common/serialization/vst3/component.h
+++ b/src/common/serialization/vst3/component.h
@@ -16,68 +16,41 @@
#pragma once
-#include
-#include
-#include
-
-#include
#include
-#include
-#include
#include
#include
#include "../../bitsery/ext/vst3.h"
#include "../common.h"
-#include "audio-processor.h"
#include "base.h"
-#include "host-application.h"
-#include "plugin-base.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
/**
- * Wraps around `IComponent` for serialization purposes. See `README.md` for
- * more information on how this works. On the Wine plugin host side this is only
- * used for serialization, and on the plugin side have an implementation that
- * can send control messages.
- *
- * This implements all interfaces that an `IComponent` might also implement.
- *
- * We might be able to do some caching here with the buss infos, but since that
- * sounds like a huge potential source of errors we'll just do pure callbacks
- * for everything other than the edit controller's class ID.
- *
- * TODO: Rework this into `YaPluginMonolith`
- * TODO: Eventually this should (optionally) implement everything supported by
- * the SDK's `AudioEffect` component.
+ * Wraps around `IComponent` for serialization purposes. This is instantiated as
+ * part of `YaPluginMonolith`. Event though `IComponent` inherits from
+ * `IPlguinBase`, we'll implement that separately in `YaPluginBase` because
+ * `IEditController` also inherits from `IPluginBase`.
*/
-class YaComponent : public Steinberg::Vst::IComponent,
- public YaAudioProcessor,
- public YaPluginBase {
+class YaComponent : public Steinberg::Vst::IComponent {
public:
/**
- * These are the arguments for creating a `YaComponentPluginImpl`.
+ * These are the arguments for creating a `YaComponent`.
*/
struct ConstructArgs {
ConstructArgs();
/**
- * Read arguments from an existing implementation. Depending on the
- * supported interface function more or less of this struct will be left
- * empty, and `known_iids` will be set accordingly.
+ * Check whether an existing implementation implements `IComponent` and
+ * read arguments from it.
*/
- ConstructArgs(Steinberg::IPtr component,
- size_t instance_id);
+ ConstructArgs(Steinberg::IPtr object);
/**
- * The unique identifier for this specific instance.
+ * Whether the object supported this interface.
*/
- native_size_t instance_id;
-
- YaAudioProcessor::ConstructArgs audio_processor_args;
- YaPluginBase::ConstructArgs plugin_base_args;
+ bool supported;
/**
* The class ID of this component's corresponding editor controller. You
@@ -87,59 +60,19 @@ class YaComponent : public Steinberg::Vst::IComponent,
template
void serialize(S& s) {
- s.value8b(instance_id);
- s.object(audio_processor_args);
- s.object(plugin_base_args);
+ s.value1b(supported);
s.ext(edit_controller_cid, bitsery::ext::StdOptional{},
[](S& s, auto& cid) { s.container1b(cid); });
}
};
- /**
- * 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;
-
- template
- void serialize(S& s) {
- s.container1b(cid);
- }
- };
-
/**
* Instantiate this instance with arguments read from another interface
* implementation.
*/
YaComponent(const ConstructArgs&& args);
- /**
- * Message to request the Wine plugin host to destroy the IComponent
- * instance with the given instance ID. Sent from the destructor of
- * `YaComponentPluginImpl`.
- */
- 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 ~YaComponent() = 0;
-
- DECLARE_FUNKNOWN_METHODS
+ inline bool supported() { return arguments.supported; }
tresult PLUGIN_API getControllerClassId(Steinberg::TUID classId) override;
@@ -381,10 +314,3 @@ class YaComponent : public Steinberg::Vst::IComponent,
};
#pragma GCC diagnostic pop
-
-template
-void serialize(
- S& s,
- std::variant& result) {
- s.ext(result, bitsery::ext::StdVariant{});
-}
diff --git a/src/common/serialization/vst3/host-application.h b/src/common/serialization/vst3/host-application.h
index 80845748..f2a9056b 100644
--- a/src/common/serialization/vst3/host-application.h
+++ b/src/common/serialization/vst3/host-application.h
@@ -34,6 +34,9 @@
* application context passed during `IPluginBase::intialize()` as well as for
* `IPluginFactory3::setHostContext()`. This interface is thus implemented on
* both the native plugin side as well as the Wine plugin host side.
+ *
+ * TODO: When implementing more host interfaces, also rework this into a
+ * monolith class like with the plugin.
*/
class YaHostApplication : public Steinberg::Vst::IHostApplication {
public:
diff --git a/src/common/serialization/vst3/plugin-base.cpp b/src/common/serialization/vst3/plugin-base.cpp
index f595b6ab..e2abb0fd 100644
--- a/src/common/serialization/vst3/plugin-base.cpp
+++ b/src/common/serialization/vst3/plugin-base.cpp
@@ -19,8 +19,8 @@
YaPluginBase::ConstructArgs::ConstructArgs() {}
YaPluginBase::ConstructArgs::ConstructArgs(
- Steinberg::IPtr component)
- : supported(Steinberg::FUnknownPtr(component)) {}
+ Steinberg::IPtr object)
+ : supported(Steinberg::FUnknownPtr(object)) {}
YaPluginBase::YaPluginBase(const ConstructArgs&& args)
: arguments(std::move(args)) {}
diff --git a/src/common/serialization/vst3/plugin-base.h b/src/common/serialization/vst3/plugin-base.h
index 503fe2f4..a4d7e556 100644
--- a/src/common/serialization/vst3/plugin-base.h
+++ b/src/common/serialization/vst3/plugin-base.h
@@ -29,7 +29,7 @@
/**
* Wraps around `IPluginBase` for serialization purposes. Both components and
* edit controllers inherit from this. This is instantiated as part of
- * `YaComponent` or `YaEditController`.
+ * `YaPluginMonolith`.
*/
class YaPluginBase : public Steinberg::IPluginBase {
public:
diff --git a/src/common/serialization/vst3/plugin-monolith.cpp b/src/common/serialization/vst3/plugin-monolith.cpp
new file mode 100644
index 00000000..814185a3
--- /dev/null
+++ b/src/common/serialization/vst3/plugin-monolith.cpp
@@ -0,0 +1,75 @@
+// 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 .
+
+#include "plugin-monolith.h"
+
+YaPluginMonolith::ConstructArgs::ConstructArgs() {}
+
+YaPluginMonolith::ConstructArgs::ConstructArgs(
+ Steinberg::IPtr object,
+ size_t instance_id)
+ : instance_id(instance_id),
+ audio_processor_args(object),
+ component_args(object),
+ plugin_base_args(object) {}
+
+YaPluginMonolith::YaPluginMonolith(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() {
+ FUNKNOWN_DTOR
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
+IMPLEMENT_REFCOUNT(YaPluginMonolith)
+#pragma GCC diagnostic pop
+
+tresult PLUGIN_API YaPluginMonolith::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`
+ if (Steinberg::FUnknownPrivate ::iidEqual(_iid,
+ Steinberg::FUnknown::iid)) {
+ addRef();
+ *obj = static_cast(
+ static_cast(this));
+ return ::Steinberg ::kResultOk;
+ }
+ if (Steinberg::FUnknownPrivate ::iidEqual(
+ _iid, Steinberg::IPluginBase::iid)) {
+ addRef();
+ *obj = static_cast(
+ static_cast(this));
+ return ::Steinberg ::kResultOk;
+ }
+ }
+ if (YaComponent::supported()) {
+ QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IComponent::iid,
+ Steinberg::Vst::IComponent)
+ }
+ if (YaAudioProcessor::supported()) {
+ QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IAudioProcessor::iid,
+ Steinberg::Vst::IAudioProcessor)
+ }
+
+ *obj = nullptr;
+ return Steinberg::kNoInterface;
+}
diff --git a/src/common/serialization/vst3/plugin-monolith.h b/src/common/serialization/vst3/plugin-monolith.h
new file mode 100644
index 00000000..dc94f142
--- /dev/null
+++ b/src/common/serialization/vst3/plugin-monolith.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 `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{});
+}
diff --git a/src/plugin/bridges/vst3-impls/component.cpp b/src/plugin/bridges/vst3-impls/component.cpp
index 92329b35..8f802e5a 100644
--- a/src/plugin/bridges/vst3-impls/component.cpp
+++ b/src/plugin/bridges/vst3-impls/component.cpp
@@ -16,22 +16,23 @@
#include "component.h"
-YaComponentPluginImpl::YaComponentPluginImpl(Vst3PluginBridge& bridge,
- YaComponent::ConstructArgs&& args)
- : YaComponent(std::move(args)), bridge(bridge) {
+YaPluginMonolithImpl::YaPluginMonolithImpl(
+ Vst3PluginBridge& bridge,
+ YaPluginMonolith::ConstructArgs&& args)
+ : YaPluginMonolith(std::move(args)), bridge(bridge) {
bridge.register_component(arguments.instance_id, *this);
}
-YaComponentPluginImpl::~YaComponentPluginImpl() {
+YaPluginMonolithImpl::~YaPluginMonolithImpl() {
bridge.send_message(
- YaComponent::Destruct{.instance_id = arguments.instance_id});
+ YaPluginMonolith::Destruct{.instance_id = arguments.instance_id});
bridge.unregister_component(arguments.instance_id);
}
tresult PLUGIN_API
-YaComponentPluginImpl::queryInterface(const Steinberg::TUID _iid, void** obj) {
+YaPluginMonolithImpl::queryInterface(const Steinberg::TUID _iid, void** obj) {
// TODO: Successful queries should also be logged
- const tresult result = YaComponent::queryInterface(_iid, obj);
+ const tresult result = YaPluginMonolith::queryInterface(_iid, obj);
if (result != Steinberg::kResultOk) {
bridge.logger.log_unknown_interface("In IComponent::queryInterface()",
Steinberg::FUID::fromTUID(_iid));
@@ -40,7 +41,7 @@ YaComponentPluginImpl::queryInterface(const Steinberg::TUID _iid, void** obj) {
return result;
}
-tresult PLUGIN_API YaComponentPluginImpl::setBusArrangements(
+tresult PLUGIN_API YaPluginMonolithImpl::setBusArrangements(
Steinberg::Vst::SpeakerArrangement* inputs,
int32 numIns,
Steinberg::Vst::SpeakerArrangement* outputs,
@@ -57,7 +58,7 @@ tresult PLUGIN_API YaComponentPluginImpl::setBusArrangements(
});
}
-tresult PLUGIN_API YaComponentPluginImpl::getBusArrangement(
+tresult PLUGIN_API YaPluginMonolithImpl::getBusArrangement(
Steinberg::Vst::BusDirection dir,
int32 index,
Steinberg::Vst::SpeakerArrangement& arr) {
@@ -74,30 +75,30 @@ tresult PLUGIN_API YaComponentPluginImpl::getBusArrangement(
}
tresult PLUGIN_API
-YaComponentPluginImpl::canProcessSampleSize(int32 symbolicSampleSize) {
+YaPluginMonolithImpl::canProcessSampleSize(int32 symbolicSampleSize) {
return bridge.send_message(YaAudioProcessor::CanProcessSampleSize{
.instance_id = arguments.instance_id,
.symbolic_sample_size = symbolicSampleSize});
}
-uint32 PLUGIN_API YaComponentPluginImpl::getLatencySamples() {
+uint32 PLUGIN_API YaPluginMonolithImpl::getLatencySamples() {
return bridge.send_message(YaAudioProcessor::GetLatencySamples{
.instance_id = arguments.instance_id});
}
tresult PLUGIN_API
-YaComponentPluginImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) {
+YaPluginMonolithImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) {
return bridge.send_message(YaAudioProcessor::SetupProcessing{
.instance_id = arguments.instance_id, .setup = setup});
}
-tresult PLUGIN_API YaComponentPluginImpl::setProcessing(TBool state) {
+tresult PLUGIN_API YaPluginMonolithImpl::setProcessing(TBool state) {
return bridge.send_message(YaAudioProcessor::SetProcessing{
.instance_id = arguments.instance_id, .state = state});
}
tresult PLUGIN_API
-YaComponentPluginImpl::process(Steinberg::Vst::ProcessData& data) {
+YaPluginMonolithImpl::process(Steinberg::Vst::ProcessData& data) {
ProcessResponse response = bridge.send_message(YaAudioProcessor::Process{
.instance_id = arguments.instance_id, .data = data});
@@ -106,29 +107,29 @@ YaComponentPluginImpl::process(Steinberg::Vst::ProcessData& data) {
return response.result;
}
-uint32 PLUGIN_API YaComponentPluginImpl::getTailSamples() {
+uint32 PLUGIN_API YaPluginMonolithImpl::getTailSamples() {
return bridge.send_message(
YaAudioProcessor::GetTailSamples{.instance_id = arguments.instance_id});
}
tresult PLUGIN_API
-YaComponentPluginImpl::setIoMode(Steinberg::Vst::IoMode mode) {
+YaPluginMonolithImpl::setIoMode(Steinberg::Vst::IoMode mode) {
return bridge.send_message(YaComponent::SetIoMode{
.instance_id = arguments.instance_id, .mode = mode});
}
int32 PLUGIN_API
-YaComponentPluginImpl::getBusCount(Steinberg::Vst::MediaType type,
- Steinberg::Vst::BusDirection dir) {
+YaPluginMonolithImpl::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
-YaComponentPluginImpl::getBusInfo(Steinberg::Vst::MediaType type,
- Steinberg::Vst::BusDirection dir,
- int32 index,
- Steinberg::Vst::BusInfo& bus /*out*/) {
+YaPluginMonolithImpl::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,
@@ -140,7 +141,7 @@ YaComponentPluginImpl::getBusInfo(Steinberg::Vst::MediaType type,
return response.result;
}
-tresult PLUGIN_API YaComponentPluginImpl::getRoutingInfo(
+tresult PLUGIN_API YaPluginMonolithImpl::getRoutingInfo(
Steinberg::Vst::RoutingInfo& inInfo,
Steinberg::Vst::RoutingInfo& outInfo /*out*/) {
const GetRoutingInfoResponse response = bridge.send_message(
@@ -154,10 +155,10 @@ tresult PLUGIN_API YaComponentPluginImpl::getRoutingInfo(
}
tresult PLUGIN_API
-YaComponentPluginImpl::activateBus(Steinberg::Vst::MediaType type,
- Steinberg::Vst::BusDirection dir,
- int32 index,
- TBool state) {
+YaPluginMonolithImpl::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,
@@ -166,17 +167,17 @@ YaComponentPluginImpl::activateBus(Steinberg::Vst::MediaType type,
.state = state});
}
-tresult PLUGIN_API YaComponentPluginImpl::setActive(TBool state) {
+tresult PLUGIN_API YaPluginMonolithImpl::setActive(TBool state) {
return bridge.send_message(YaComponent::SetActive{
.instance_id = arguments.instance_id, .state = state});
}
-tresult PLUGIN_API YaComponentPluginImpl::setState(Steinberg::IBStream* state) {
+tresult PLUGIN_API YaPluginMonolithImpl::setState(Steinberg::IBStream* state) {
return bridge.send_message(YaComponent::SetState{
.instance_id = arguments.instance_id, .state = state});
}
-tresult PLUGIN_API YaComponentPluginImpl::getState(Steinberg::IBStream* state) {
+tresult PLUGIN_API YaPluginMonolithImpl::getState(Steinberg::IBStream* state) {
const GetStateResponse response = bridge.send_message(
YaComponent::GetState{.instance_id = arguments.instance_id});
@@ -185,7 +186,7 @@ tresult PLUGIN_API YaComponentPluginImpl::getState(Steinberg::IBStream* state) {
return response.result;
}
-tresult PLUGIN_API YaComponentPluginImpl::initialize(FUnknown* context) {
+tresult PLUGIN_API YaPluginMonolithImpl::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
@@ -209,7 +210,7 @@ tresult PLUGIN_API YaComponentPluginImpl::initialize(FUnknown* context) {
std::move(host_application_context_args)});
}
-tresult PLUGIN_API YaComponentPluginImpl::terminate() {
+tresult PLUGIN_API YaPluginMonolithImpl::terminate() {
return bridge.send_message(
YaPluginBase::Terminate{.instance_id = arguments.instance_id});
}
diff --git a/src/plugin/bridges/vst3-impls/component.h b/src/plugin/bridges/vst3-impls/component.h
index 8d2a293c..03ecdbd7 100644
--- a/src/plugin/bridges/vst3-impls/component.h
+++ b/src/plugin/bridges/vst3-impls/component.h
@@ -20,17 +20,17 @@
#include "../vst3.h"
-class YaComponentPluginImpl : public YaComponent {
+class YaPluginMonolithImpl : public YaPluginMonolith {
public:
- YaComponentPluginImpl(Vst3PluginBridge& bridge,
- YaComponent::ConstructArgs&& args);
+ 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.
*/
- ~YaComponentPluginImpl();
+ ~YaPluginMonolithImpl();
/**
* We'll override the query interface to log queries for interfaces we do
diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.cpp b/src/plugin/bridges/vst3-impls/plugin-factory.cpp
index a39ac230..6b65b2e1 100644
--- a/src/plugin/bridges/vst3-impls/plugin-factory.cpp
+++ b/src/plugin/bridges/vst3-impls/plugin-factory.cpp
@@ -38,13 +38,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(YaComponent::Construct{.cid = cid_array});
+ std::variant result =
+ bridge.send_message(YaPluginMonolith::Construct{.cid = cid_array});
return std::visit(
overload{
- [&](YaComponent::ConstructArgs&& args) -> tresult {
+ [&](YaPluginMonolith::ConstructArgs&& args) -> tresult {
*obj = static_cast(
- new YaComponentPluginImpl(bridge, std::move(args)));
+ new YaPluginMonolithImpl(bridge, std::move(args)));
return Steinberg::kResultOk;
},
[&](const UniversalTResult& code) -> tresult { return code; }},
diff --git a/src/plugin/bridges/vst3-impls/plugin-factory.h b/src/plugin/bridges/vst3-impls/plugin-factory.h
index 963ff26e..30a2f093 100644
--- a/src/plugin/bridges/vst3-impls/plugin-factory.h
+++ b/src/plugin/bridges/vst3-impls/plugin-factory.h
@@ -18,6 +18,7 @@
#include "../vst3.h"
+// TODO Rename to YaPluginFactoryImpl
class YaPluginFactoryPluginImpl : public YaPluginFactory {
public:
YaPluginFactoryPluginImpl(Vst3PluginBridge& bridge,
diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp
index c8e45e95..61ae85a1 100644
--- a/src/plugin/bridges/vst3.cpp
+++ b/src/plugin/bridges/vst3.cpp
@@ -115,10 +115,10 @@ Steinberg::IPluginFactory* Vst3PluginBridge::get_plugin_factory() {
}
void Vst3PluginBridge::register_component(size_t instance_id,
- YaComponentPluginImpl& component) {
+ YaPluginMonolithImpl& 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 402ad38b..341b402c 100644
--- a/src/plugin/bridges/vst3.h
+++ b/src/plugin/bridges/vst3.h
@@ -24,7 +24,7 @@
#include "common.h"
// Forward declaration
-class YaComponentPluginImpl;
+class YaPluginMonolithImpl;
/**
* This handles the communication between the native host and a VST3 plugin
@@ -83,9 +83,11 @@ class Vst3PluginBridge : PluginBridge> {
* context.
*
* @see component_instances
+ *
+ * TODO: REname to `register_instance` or `register_object`
*/
void register_component(size_t instance_id,
- YaComponentPluginImpl& component);
+ YaPluginMonolithImpl& component);
/**
* Remove a previously registered `YaComponentPluginImpl` from the list of
@@ -149,7 +151,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 1c0e9c36..57dfa2b6 100644
--- a/src/wine-host/bridges/vst3.cpp
+++ b/src/wine-host/bridges/vst3.cpp
@@ -60,6 +60,34 @@ void Vst3Bridge::run() {
sockets.host_vst_control.receive_messages(
std::nullopt,
overload{
+ [&](const YaPluginMonolith::Construct& args)
+ -> YaPluginMonolith::Construct::Response {
+ Steinberg::TUID cid;
+ std::copy(args.cid.begin(), args.cid.end(), cid);
+ Steinberg::IPtr component =
+ module->getFactory()
+ .createInstance(cid);
+ if (component) {
+ std::lock_guard lock(component_instances_mutex);
+
+ const size_t instance_id = generate_instance_id();
+ component_instances[instance_id] = std::move(component);
+
+ return YaPluginMonolith::ConstructArgs(
+ component_instances[instance_id].component,
+ instance_id);
+ } else {
+ // The actual result is lost here
+ return UniversalTResult(Steinberg::kNotImplemented);
+ }
+ },
+ [&](const YaPluginMonolith::Destruct& request)
+ -> YaPluginMonolith::Destruct::Response {
+ std::lock_guard lock(component_instances_mutex);
+ component_instances.erase(request.instance_id);
+
+ return Ack{};
+ },
[&](YaAudioProcessor::SetBusArrangements& request)
-> YaAudioProcessor::SetBusArrangements::Response {
return component_instances[request.instance_id]
@@ -113,34 +141,6 @@ void Vst3Bridge::run() {
return component_instances[request.instance_id]
.audio_processor->getTailSamples();
},
- [&](const YaComponent::Construct& args)
- -> YaComponent::Construct::Response {
- Steinberg::TUID cid;
- std::copy(args.cid.begin(), args.cid.end(), cid);
- Steinberg::IPtr component =
- module->getFactory()
- .createInstance(cid);
- if (component) {
- std::lock_guard lock(component_instances_mutex);
-
- const size_t instance_id = generate_instance_id();
- component_instances[instance_id] = std::move(component);
-
- return YaComponent::ConstructArgs(
- component_instances[instance_id].component,
- instance_id);
- } else {
- // The actual result is lost here
- return UniversalTResult(Steinberg::kNotImplemented);
- }
- },
- [&](const YaComponent::Destruct& request)
- -> YaComponent::Destruct::Response {
- std::lock_guard lock(component_instances_mutex);
- component_instances.erase(request.instance_id);
-
- return Ack{};
- },
[&](const YaComponent::SetIoMode& request)
-> YaComponent::SetIoMode::Response {
return component_instances[request.instance_id]
@@ -202,7 +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 `YaComponent::Destruct`.
+ // be cleaned up again during `YaPluginMonolith::Destruct`.
+ // TOOD: This needs changing when we get to `YaHostMonolith`
Steinberg::FUnknown* context = nullptr;
if (request.host_application_context_args) {
component_instances[request.instance_id]