mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Add the base for an IComponent implementation
This commit is contained in:
@@ -24,6 +24,18 @@
|
|||||||
|
|
||||||
Vst3Logger::Vst3Logger(Logger& generic_logger) : logger(generic_logger) {}
|
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&) {
|
void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
|
||||||
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
|
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
|
||||||
std::ostringstream message;
|
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,
|
void Vst3Logger::log_response(bool is_host_vst,
|
||||||
const YaPluginFactory& factory) {
|
const YaPluginFactory& factory) {
|
||||||
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
|
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
|
||||||
|
|||||||
@@ -45,10 +45,12 @@ class Vst3Logger {
|
|||||||
// flag here indicates whether the request was initiated on the host side
|
// flag here indicates whether the request was initiated on the host side
|
||||||
// (what we'll call a control message).
|
// (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 WantsConfiguration&);
|
||||||
void log_request(bool is_host_vst, const WantsPluginFactory&);
|
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 Configuration&);
|
||||||
|
void log_response(bool is_host_vst, const YaComponent&);
|
||||||
void log_response(bool is_host_vst, const YaPluginFactory&);
|
void log_response(bool is_host_vst, const YaPluginFactory&);
|
||||||
|
|
||||||
Logger& logger;
|
Logger& logger;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "../configuration.h"
|
#include "../configuration.h"
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "vst3/component.h"
|
||||||
#include "vst3/plugin-factory.h"
|
#include "vst3/plugin-factory.h"
|
||||||
|
|
||||||
// Event handling for our VST3 plugins works slightly different from how we
|
// 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
|
// 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
|
// 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
|
* Marker struct to indicate the other side (the plugin) should send a copy of
|
||||||
* the configuration.
|
* the configuration.
|
||||||
@@ -64,7 +80,8 @@ struct WantsPluginFactory {
|
|||||||
* encodes the information we request or the operation we want to perform. A
|
* encodes the information we request or the operation we want to perform. A
|
||||||
* request of type `ControlRequest(T)` should send back a `T::Response`.
|
* 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>
|
template <typename S>
|
||||||
void serialize(S& s, ControlRequest& payload) {
|
void serialize(S& s, ControlRequest& payload) {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ instantiated and managed by the host. The model works as follows:
|
|||||||
|
|
||||||
## Plugin Factory
|
## Plugin Factory
|
||||||
|
|
||||||
TODO: Explain how we implement `createInstance()`
|
TODO: Explain how we implement `createInstance()`, based on the todo comment there.
|
||||||
|
|
||||||
## Safety notes
|
## 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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -44,8 +44,7 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
|
|||||||
YaPluginFactory();
|
YaPluginFactory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a copy of an existing plugin factory. Depending on the
|
* Create a copy of an existing plugin factory. Depending on the supported
|
||||||
supported
|
|
||||||
* interface function more or less of this struct will be left empty, and
|
* interface function more or less of this struct will be left empty, and
|
||||||
* `iid` will be set accordingly.
|
* `iid` will be set accordingly.
|
||||||
*/
|
*/
|
||||||
@@ -61,17 +60,10 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
|
|||||||
int32 PLUGIN_API countClasses() override;
|
int32 PLUGIN_API countClasses() override;
|
||||||
tresult PLUGIN_API getClassInfo(Steinberg::int32 index,
|
tresult PLUGIN_API getClassInfo(Steinberg::int32 index,
|
||||||
Steinberg::PClassInfo* info) override;
|
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
|
* See the implementation in `YaPluginFactoryPluginImpl` for how this is
|
||||||
// it to create an instance of `_iid`.
|
* handled.
|
||||||
// - 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.
|
|
||||||
virtual tresult PLUGIN_API createInstance(Steinberg::FIDString cid,
|
virtual tresult PLUGIN_API createInstance(Steinberg::FIDString cid,
|
||||||
Steinberg::FIDString _iid,
|
Steinberg::FIDString _iid,
|
||||||
void** obj) override = 0;
|
void** obj) override = 0;
|
||||||
@@ -83,6 +75,10 @@ class YaPluginFactory : public Steinberg::IPluginFactory3 {
|
|||||||
// From `IPluginFactory3`
|
// From `IPluginFactory3`
|
||||||
tresult PLUGIN_API
|
tresult PLUGIN_API
|
||||||
getClassInfoUnicode(int32 index, Steinberg::PClassInfoW* info) override;
|
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
|
virtual tresult PLUGIN_API
|
||||||
setHostContext(Steinberg::FUnknown* context) override = 0;
|
setHostContext(Steinberg::FUnknown* context) override = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,33 @@ void Vst3Bridge::run() {
|
|||||||
sockets.host_vst_control.receive_messages(
|
sockets.host_vst_control.receive_messages(
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
overload{
|
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 {
|
[&](const WantsPluginFactory&) -> WantsPluginFactory::Response {
|
||||||
return *plugin_factory;
|
return *plugin_factory;
|
||||||
}});
|
}});
|
||||||
|
|||||||
Reference in New Issue
Block a user