mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-08 04:20:13 +02:00
Redesign how interface instantiation works
Transferring some argument pack is much easier than trying to deserialize into an existing object when you also have to transfer more information than just that object.
This commit is contained in:
@@ -77,6 +77,7 @@ vst3_plugin_sources = [
|
||||
'src/common/communication/common.cpp',
|
||||
'src/common/logging/common.cpp',
|
||||
'src/common/logging/vst3.cpp',
|
||||
'src/common/serialization/vst3/component.cpp',
|
||||
'src/common/serialization/vst3/plugin-factory.cpp',
|
||||
'src/common/configuration.cpp',
|
||||
'src/common/plugins.cpp',
|
||||
@@ -108,6 +109,7 @@ host_sources = [
|
||||
if with_vst3
|
||||
host_sources += [
|
||||
'src/common/logging/vst3.cpp',
|
||||
'src/common/serialization/vst3/component.cpp',
|
||||
'src/common/serialization/vst3/plugin-factory.cpp',
|
||||
'src/wine-host/bridges/vst3.cpp',
|
||||
'src/wine-host/bridges/vst3-impls.cpp',
|
||||
|
||||
@@ -56,20 +56,26 @@ void Vst3Logger::log_request(bool is_host_vst, const WantsPluginFactory&) {
|
||||
}
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const std::optional<YaComponent::Arguments>& args) {
|
||||
if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) {
|
||||
std::ostringstream message;
|
||||
message << get_log_prefix(is_host_vst) << " <Configuration>";
|
||||
if (args) {
|
||||
message << get_log_prefix(is_host_vst) << " <IComponent* #"
|
||||
<< args->instance_id << ">";
|
||||
} else {
|
||||
message << get_log_prefix(is_host_vst) << " <nullptr>";
|
||||
}
|
||||
|
||||
log(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst, const YaComponent&) {
|
||||
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
|
||||
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*>";
|
||||
message << get_log_prefix(is_host_vst) << " <Configuration>";
|
||||
|
||||
log(message.str());
|
||||
}
|
||||
|
||||
@@ -49,8 +49,9 @@ class Vst3Logger {
|
||||
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 std::optional<YaComponent::Arguments>&);
|
||||
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;
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
TODO: Once this is more fleshed out, move this document to `docs/`, and perhaps
|
||||
replace this readme with a link to that document.
|
||||
|
||||
TODO: There are now two approaches in use: the factory takes an interface
|
||||
pointer for serialization and deserializes into an object directly, and the
|
||||
component uses an args struct because the alternative involving pointers is just
|
||||
too unsafe (as we also have to communicate additional payload data). This should
|
||||
probably be unified into only using the latter appraoch.
|
||||
|
||||
The VST3 SDK uses an architecture where every object inherits from an interface,
|
||||
and every interface inherits from `FUnknown` which offers a dynamic casting
|
||||
interface through `queryInterface()`. Every interface gets a unique identifier.
|
||||
|
||||
@@ -16,14 +16,19 @@
|
||||
|
||||
#include "component.h"
|
||||
|
||||
YaComponent::YaComponent(){FUNKNOWN_CTOR}
|
||||
|
||||
YaComponent::YaComponent(
|
||||
Steinberg::IPtr<Steinberg::Vst::IComponent> component) {
|
||||
FUNKNOWN_CTOR
|
||||
|
||||
YaComponent::Arguments::Arguments(
|
||||
Steinberg::IPtr<Steinberg::Vst::IComponent> component,
|
||||
size_t instance_id)
|
||||
: instance_id(instance_id) {
|
||||
// `IComponent::getControllerClassId`
|
||||
component->getControllerClassId(edit_controller_cid);
|
||||
Steinberg::TUID cid;
|
||||
if (component->getControllerClassId(cid) == Steinberg::kResultOk) {
|
||||
edit_controller_cid = std::to_array(cid);
|
||||
}
|
||||
}
|
||||
|
||||
YaComponent::YaComponent(const Arguments&& args) : arguments(std::move(args)) {
|
||||
FUNKNOWN_CTOR
|
||||
|
||||
// Everything else is handled directly through callbacks to minimize the
|
||||
// potential for errors
|
||||
@@ -49,3 +54,13 @@ tresult PLUGIN_API YaComponent::queryInterface(Steinberg::FIDString _iid,
|
||||
*obj = nullptr;
|
||||
return Steinberg::kNoInterface;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API YaComponent::getControllerClassId(Steinberg::TUID classId) {
|
||||
if (arguments.edit_controller_cid) {
|
||||
std::copy(arguments.edit_controller_cid->begin(),
|
||||
arguments.edit_controller_cid->end(), classId);
|
||||
return Steinberg::kResultOk;
|
||||
} else {
|
||||
return Steinberg::kNotImplemented;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include "src/common/serialization/common.h"
|
||||
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/std_optional.h>
|
||||
#include <bitsery/traits/array.h>
|
||||
#include <pluginterfaces/vst/ivstcomponent.h>
|
||||
|
||||
using Steinberg::TBool, Steinberg::int32, Steinberg::tresult;
|
||||
using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::tresult;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||
@@ -36,14 +42,44 @@ using Steinberg::TBool, Steinberg::int32, Steinberg::tresult;
|
||||
class YaComponent : public Steinberg::Vst::IComponent {
|
||||
public:
|
||||
/**
|
||||
* Request the Wine plugin host to instantiate a new IComponent to pass
|
||||
* through a call to `IPluginFactory::createInstance(cid, IComponent::iid,
|
||||
* These are the arguments for creating a `YaComponentPluginImpl`.
|
||||
*/
|
||||
struct Arguments {
|
||||
/**
|
||||
* Read arguments from an existing implementation.
|
||||
*/
|
||||
Arguments(Steinberg::IPtr<Steinberg::Vst::IComponent> component,
|
||||
size_t isntance_id);
|
||||
|
||||
/**
|
||||
* The unique identifier for this specific instance.
|
||||
*/
|
||||
native_size_t instance_id;
|
||||
|
||||
/**
|
||||
* The class ID of this component's corresponding editor controller. You
|
||||
* can't use C-style array in `std::optional`s.
|
||||
*/
|
||||
std::optional<std::array<int8, std::extent_v<Steinberg::TUID>>>
|
||||
edit_controller_cid;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value8b(instance_id);
|
||||
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 `IPluginFactory::createInstance(cid,
|
||||
* IComponent::iid,
|
||||
* ...)`.
|
||||
*/
|
||||
struct Create {
|
||||
// TODO: This should be `std::optional<YaComponent>`, and we need a way
|
||||
// to deserialize that into an existing YaComponent.
|
||||
using Response = YaComponent&;
|
||||
// TODO: Create a `native_tvalue` wrapper, and then also add them here
|
||||
using Response = std::optional<Arguments>;
|
||||
|
||||
Steinberg::TUID cid;
|
||||
|
||||
@@ -53,12 +89,11 @@ class YaComponent : public Steinberg::Vst::IComponent {
|
||||
}
|
||||
};
|
||||
|
||||
YaComponent();
|
||||
|
||||
/**
|
||||
* Create a copy of an existing component.
|
||||
* Instantiate this instance with arguments read from another interface
|
||||
* implementation.
|
||||
*/
|
||||
explicit YaComponent(Steinberg::IPtr<Steinberg::Vst::IComponent> component);
|
||||
YaComponent(const Arguments&& args);
|
||||
|
||||
/**
|
||||
* @remark The plugin side implementation should send a control message to
|
||||
@@ -97,19 +132,16 @@ class YaComponent : public Steinberg::Vst::IComponent {
|
||||
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;
|
||||
Arguments arguments;
|
||||
|
||||
// TODO: As explained in a few other places, `YaComponent` objects should be
|
||||
// assigned a unique ID for identification
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, std::optional<YaComponent::Arguments>& args) {
|
||||
s.ext(args, bitsery::ext::StdOptional{});
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -66,17 +66,13 @@ void Vst3Bridge::run() {
|
||||
// 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
|
||||
if (component) {
|
||||
// TODO: Generate a unique instance ID
|
||||
return std::make_optional<YaComponent::Arguments>(
|
||||
component, 420691337);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: Implement `YaComponentHostImpl` and create an instance
|
||||
// based on `component`
|
||||
YaComponent* removeme = nullptr;
|
||||
return *removeme;
|
||||
},
|
||||
[&](const WantsPluginFactory&) -> WantsPluginFactory::Response {
|
||||
return *plugin_factory;
|
||||
|
||||
Reference in New Issue
Block a user