Add the base for an IComponent implementation

This commit is contained in:
Robbert van der Helm
2020-12-07 23:52:17 +01:00
parent e5cd777713
commit 5e85517130
8 changed files with 223 additions and 15 deletions
+22
View File
@@ -24,6 +24,18 @@
Vst3Logger::Vst3Logger(Logger& generic_logger) : logger(generic_logger) {}
void Vst3Logger::log_request(bool is_host_vst, const CreateInstaneIComponent&) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
std::ostringstream message;
// TODO: Log the cid in some readable way, if possible
message << get_log_prefix(is_host_vst)
<< " >> IPluginFactory::createComponent(cid, IComponent::iid, "
"&obj)";
log(message.str());
}
}
void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
std::ostringstream message;
@@ -53,6 +65,16 @@ void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
}
}
void Vst3Logger::log_response(bool is_host_vst, const YaComponent&) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
std::ostringstream message;
// TODO: Add the instance ID after we implement that
message << get_log_prefix(is_host_vst) << " <IComponent*>";
log(message.str());
}
}
void Vst3Logger::log_response(bool is_host_vst,
const YaPluginFactory& factory) {
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
+2
View File
@@ -45,10 +45,12 @@ 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 CreateInstaneIComponent&);
void log_request(bool is_host_vst, const WantsConfiguration&);
void log_request(bool is_host_vst, const WantsPluginFactory&);
void log_response(bool is_host_vst, const Configuration&);
void log_response(bool is_host_vst, const YaComponent&);
void log_response(bool is_host_vst, const YaPluginFactory&);
Logger& logger;
+18 -1
View File
@@ -23,6 +23,7 @@
#include "../configuration.h"
#include "../utils.h"
#include "common.h"
#include "vst3/component.h"
#include "vst3/plugin-factory.h"
// Event handling for our VST3 plugins works slightly different from how we
@@ -37,6 +38,21 @@
// TODO: If this approach works, maybe we can also refactor the VST2 handling to
// do this since it's a bit safer and easier to read
/**
* Request the Wine plugin host to instantiate a new IComponent to pass through
* a call to `IPluginFactory::createInstance(cid, IComponent::iid, ...)`.
*/
struct CreateInstaneIComponent {
using Response = YaComponent&;
Steinberg::TUID cid;
template <typename S>
void serialize(S& s) {
s.container1b(cid);
}
};
/**
* Marker struct to indicate the other side (the plugin) should send a copy of
* the configuration.
@@ -64,7 +80,8 @@ struct WantsPluginFactory {
* 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<WantsPluginFactory>;
using ControlRequest =
std::variant<CreateInstaneIComponent, WantsPluginFactory>;
template <typename S>
void serialize(S& s, ControlRequest& payload) {
+1 -1
View File
@@ -50,7 +50,7 @@ instantiated and managed by the host. The model works as follows:
## Plugin Factory
TODO: Explain how we implement `createInstance()`
TODO: Explain how we implement `createInstance()`, based on the todo comment there.
## Safety notes
@@ -0,0 +1,51 @@
// yabridge: a Wine VST bridge
// Copyright (C) 2020 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "component.h"
YaComponent::YaComponent(){FUNKNOWN_CTOR}
YaComponent::YaComponent(
Steinberg::IPtr<Steinberg::Vst::IComponent> component) {
FUNKNOWN_CTOR
// `IComponent::getControllerClassId`
component->getControllerClassId(edit_controller_cid);
// Everything else is handled directly through callbacks to minimize the
// potential for errors
}
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::IPluginBase)
QUERY_INTERFACE(_iid, obj, Steinberg::IPluginBase::iid,
Steinberg::IPluginBase)
QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IComponent::iid,
Steinberg::Vst::IComponent)
*obj = nullptr;
return Steinberg::kNoInterface;
}
+93
View File
@@ -0,0 +1,93 @@
// yabridge: a Wine VST bridge
// Copyright (C) 2020 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <pluginterfaces/vst/ivstcomponent.h>
using Steinberg::TBool, Steinberg::int32, Steinberg::tresult;
#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.
*
* 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.
*/
class YaComponent : public Steinberg::Vst::IComponent {
public:
YaComponent();
/**
* Create a copy of an existing component.
*/
explicit YaComponent(Steinberg::IPtr<Steinberg::Vst::IComponent> component);
virtual ~YaComponent();
DECLARE_FUNKNOWN_METHODS
// From `IPluginBase`
virtual tresult PLUGIN_API initialize(FUnknown* context) override = 0;
virtual tresult PLUGIN_API terminate() override = 0;
// From `IComponent`
tresult PLUGIN_API getControllerClassId(Steinberg::TUID classId) override;
virtual tresult PLUGIN_API
setIoMode(Steinberg::Vst::IoMode mode) override = 0;
virtual int32 PLUGIN_API
getBusCount(Steinberg::Vst::MediaType type,
Steinberg::Vst::BusDirection dir) override = 0;
virtual tresult PLUGIN_API
getBusInfo(Steinberg::Vst::MediaType type,
Steinberg::Vst::BusDirection dir,
int32 index,
Steinberg::Vst::BusInfo& bus /*out*/) override = 0;
virtual tresult PLUGIN_API
getRoutingInfo(Steinberg::Vst::RoutingInfo& inInfo,
Steinberg::Vst::RoutingInfo& outInfo /*out*/) override = 0;
virtual tresult PLUGIN_API activateBus(Steinberg::Vst::MediaType type,
Steinberg::Vst::BusDirection dir,
int32 index,
TBool state) override = 0;
virtual tresult PLUGIN_API setActive(TBool state) override = 0;
virtual tresult PLUGIN_API
setState(Steinberg::IBStream* state) override = 0;
virtual tresult PLUGIN_API
getState(Steinberg::IBStream* state) override = 0;
template <typename S>
void serialize(S& s) {
s.container1b(edit_controller_cid);
}
private:
/**
* The class ID of this component's corresponding editor controller.
*/
Steinberg::TUID edit_controller_cid;
// TODO: As explained in a few other places, `YaComponent` objects should be
// assigned a unique ID for identification
};
#pragma GCC diagnostic pop
+9 -13
View File
@@ -44,8 +44,7 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
YaPluginFactory();
/**
* Create a copy of an existing plugin factory. Depending on the
supported
* Create a copy of an existing plugin factory. Depending on the supported
* interface function more or less of this struct will be left empty, and
* `iid` will be set accordingly.
*/
@@ -61,17 +60,10 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
int32 PLUGIN_API countClasses() override;
tresult PLUGIN_API getClassInfo(Steinberg::int32 index,
Steinberg::PClassInfo* info) override;
// TODO: Figure out how to implement this. Some considerations:
// - We have to sent a control message to the Wine plugin host to ask
// it to create an instance of `_iid`.
// - We then create a `Ya*` implementation of the same interface on
// the plugin side.
// - These two should be wired up so that when the host calls a
// function on it, it should be sent to the instance on the Wine
// plugin host side with the same cid.
// - We should have a list of interfaces we support. When we receive a
// request to create an instance of something we don't support, then
// we should log that and then fail.
/**
* See the implementation in `YaPluginFactoryPluginImpl` for how this is
* handled.
*/
virtual tresult PLUGIN_API createInstance(Steinberg::FIDString cid,
Steinberg::FIDString _iid,
void** obj) override = 0;
@@ -83,6 +75,10 @@ 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.
*/
virtual tresult PLUGIN_API
setHostContext(Steinberg::FUnknown* context) override = 0;
+27
View File
@@ -51,6 +51,33 @@ void Vst3Bridge::run() {
sockets.host_vst_control.receive_messages(
std::nullopt,
overload{
[&](const CreateInstaneIComponent& args)
-> CreateInstaneIComponent::Response {
Steinberg::IPtr<Steinberg::Vst::IComponent> component =
module->getFactory()
.createInstance<Steinberg::Vst::IComponent>(args.cid);
// TODO: Next steps are:
// - Generate a new unique ID using an atomic size_t and
// fetch-and-add.
// - Add an `std::map<size_t,
// Steinberg::IPtr<Steinberg::Vst::IComponent>`
// to this class and add `component` with the generated
// ID to that.
// - Add that ID to `YaComponent` and set it in the object
// we create here.
// - In case `factory` is a null pointer, allow returning
// `nullopt`. Not sure how that is going to work with
// the deserialization.
if (!component) {
// TODO: Handle
}
// TODO: Implement `YaComponentHostImpl` and create an instance
// based on `component`
YaComponent* removeme = nullptr;
return *removeme;
},
[&](const WantsPluginFactory&) -> WantsPluginFactory::Response {
return *plugin_factory;
}});