mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Implement IPluginFactory3::setHostContext()
Now the plugin factories are fully implemented (at least, functionality wise, we still can't create most kinds of objects).
This commit is contained in:
@@ -17,13 +17,11 @@ imcomplete list of things that still have to be done before this can be used:
|
|||||||
|
|
||||||
- Left to implement:
|
- Left to implement:
|
||||||
- `YaHostApplicationHostImpl::createComponent`.
|
- `YaHostApplicationHostImpl::createComponent`.
|
||||||
- `IPluginFactory3::setHostContext()` using the same host application context proxy method.
|
|
||||||
- The rest of `IComponent`'s functions
|
- The rest of `IComponent`'s functions
|
||||||
- `IPluginFactory3::setHostContext()`
|
|
||||||
- All other mandatory interfaces
|
- All other mandatory interfaces
|
||||||
- All other optional interfaces
|
- All other optional interfaces
|
||||||
- Fully implemented:
|
- Fully implemented:
|
||||||
- Nothing yet
|
- `GetPluginFactory()` and `IPluginFactory{,2,3}`
|
||||||
- Update the GitHub Actions workflows.
|
- Update the GitHub Actions workflows.
|
||||||
- Update yabridgectl to handle buth VST2 and VST3 plugins.
|
- Update yabridgectl to handle buth VST2 and VST3 plugins.
|
||||||
- Update all documentation to refer to VST2 and VST3 support separately, and
|
- Update all documentation to refer to VST2 and VST3 support separately, and
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ class Vst2Sockets : public Sockets {
|
|||||||
* @param plugin The `AEffect` instance that should be passed to the callback
|
* @param plugin The `AEffect` instance that should be passed to the callback
|
||||||
* function. During `WantsAEffect` we'll send back a copy of this, and when we
|
* function. During `WantsAEffect` we'll send back a copy of this, and when we
|
||||||
* get sent an `AEffect` instance (e.g. during `audioMasterIOChanged()`) we'll
|
* get sent an `AEffect` instance (e.g. during `audioMasterIOChanged()`) we'll
|
||||||
* write the updated values to this isntance.
|
* write the updated values to this instance.
|
||||||
* @param callback The function to call with the arguments received from the
|
* @param callback The function to call with the arguments received from the
|
||||||
* socket, either `AEffect::dispatcher()` or `audioMasterCallback()`.
|
* socket, either `AEffect::dispatcher()` or `audioMasterCallback()`.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ void Vst3Logger::log_request(bool is_host_vst,
|
|||||||
[](auto& message) { message << "GetPluginFactory()"; });
|
[](auto& message) { message << "GetPluginFactory()"; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Vst3Logger::log_request(bool is_host_vst,
|
||||||
|
const YaPluginFactory::SetHostContext&) {
|
||||||
|
log_request_base(is_host_vst, [](auto& message) {
|
||||||
|
message << "IPluginFactory3::setHostContext(IHostApplication*)";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
|
void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
|
||||||
log_request_base(is_host_vst, [](auto& message) {
|
log_request_base(is_host_vst, [](auto& message) {
|
||||||
message << "Requesting <Configuration>";
|
message << "Requesting <Configuration>";
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class Vst3Logger {
|
|||||||
void log_request(bool is_host_vst, const YaComponent::Initialize&);
|
void log_request(bool is_host_vst, const YaComponent::Initialize&);
|
||||||
void log_request(bool is_host_vst, const YaComponent::Terminate&);
|
void log_request(bool is_host_vst, const YaComponent::Terminate&);
|
||||||
void log_request(bool is_host_vst, const YaPluginFactory::Construct&);
|
void log_request(bool is_host_vst, const YaPluginFactory::Construct&);
|
||||||
|
void log_request(bool is_host_vst, const YaPluginFactory::SetHostContext&);
|
||||||
void log_request(bool is_host_vst, const WantsConfiguration&);
|
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 Ack&);
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ using ControlRequest = std::variant<YaComponent::Construct,
|
|||||||
YaComponent::Destruct,
|
YaComponent::Destruct,
|
||||||
YaComponent::Initialize,
|
YaComponent::Initialize,
|
||||||
YaComponent::Terminate,
|
YaComponent::Terminate,
|
||||||
YaPluginFactory::Construct>;
|
YaPluginFactory::Construct,
|
||||||
|
YaPluginFactory::SetHostContext>;
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
void serialize(S& s, ControlRequest& payload) {
|
void serialize(S& s, ControlRequest& payload) {
|
||||||
|
|||||||
@@ -43,7 +43,8 @@
|
|||||||
* for everything other than the edit controller's class ID.
|
* for everything other than the edit controller's class ID.
|
||||||
*
|
*
|
||||||
* TODO: I think it's expected that components also implement `IAudioProcessor`
|
* TODO: I think it's expected that components also implement `IAudioProcessor`
|
||||||
* and `IConnectionPoint`.
|
* and `IConnectionPoint`. We should use the same approach as in the
|
||||||
|
* plugin factory to implement multiple, possibly optional, interfaces.
|
||||||
*/
|
*/
|
||||||
class YaComponent : public Steinberg::Vst::IComponent {
|
class YaComponent : public Steinberg::Vst::IComponent {
|
||||||
public:
|
public:
|
||||||
@@ -57,7 +58,7 @@ class YaComponent : public Steinberg::Vst::IComponent {
|
|||||||
* Read arguments from an existing implementation.
|
* Read arguments from an existing implementation.
|
||||||
*/
|
*/
|
||||||
ConstructArgs(Steinberg::IPtr<Steinberg::Vst::IComponent> component,
|
ConstructArgs(Steinberg::IPtr<Steinberg::Vst::IComponent> component,
|
||||||
size_t isntance_id);
|
size_t instance_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique identifier for this specific instance.
|
* The unique identifier for this specific instance.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ YaHostApplication::ConstructArgs::ConstructArgs() {}
|
|||||||
|
|
||||||
YaHostApplication::ConstructArgs::ConstructArgs(
|
YaHostApplication::ConstructArgs::ConstructArgs(
|
||||||
Steinberg::IPtr<Steinberg::Vst::IHostApplication> context,
|
Steinberg::IPtr<Steinberg::Vst::IHostApplication> context,
|
||||||
size_t component_instance_id)
|
std::optional<size_t> component_instance_id)
|
||||||
: component_instance_id(component_instance_id) {
|
: component_instance_id(component_instance_id) {
|
||||||
Steinberg::Vst::String128 name_array;
|
Steinberg::Vst::String128 name_array;
|
||||||
if (context->getName(name_array) == Steinberg::kResultOk) {
|
if (context->getName(name_array) == Steinberg::kResultOk) {
|
||||||
|
|||||||
@@ -48,16 +48,15 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication {
|
|||||||
* Read arguments from an existing implementation.
|
* Read arguments from an existing implementation.
|
||||||
*/
|
*/
|
||||||
ConstructArgs(Steinberg::IPtr<Steinberg::Vst::IHostApplication> context,
|
ConstructArgs(Steinberg::IPtr<Steinberg::Vst::IHostApplication> context,
|
||||||
size_t component_isntance_id);
|
std::optional<size_t> component_instance_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique instance identifier of the component this host context has
|
* The unique instance identifier of the component this host context has
|
||||||
* been passed to and thus belongs to.
|
* been passed to and thus belongs to, if we are handling
|
||||||
*
|
* `IpluginBase::initialize()`. When handling
|
||||||
* TODO: When we implement `IPluginFactory3::setHostContext()` this
|
* `IPluginFactory::setHostContext()` this will be empty.
|
||||||
* should be made optional.
|
|
||||||
*/
|
*/
|
||||||
native_size_t component_instance_id;
|
std::optional<native_size_t> component_instance_id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For `IHostApplication::getName`.
|
* For `IHostApplication::getName`.
|
||||||
@@ -66,7 +65,10 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication {
|
|||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
void serialize(S& s) {
|
void serialize(S& s) {
|
||||||
s.value8b(component_instance_id);
|
s.ext(component_instance_id, bitsery::ext::StdOptional{},
|
||||||
|
[](S& s, native_size_t& instance_id) {
|
||||||
|
s.value8b(instance_id);
|
||||||
|
});
|
||||||
s.ext(name, bitsery::ext::StdOptional{},
|
s.ext(name, bitsery::ext::StdOptional{},
|
||||||
[](S& s, std::u16string& name) {
|
[](S& s, std::u16string& name) {
|
||||||
s.text2b(name, std::extent_v<Steinberg::Vst::String128>);
|
s.text2b(name, std::extent_v<Steinberg::Vst::String128>);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "../../bitsery/ext/vst3.h"
|
#include "../../bitsery/ext/vst3.h"
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
#include "host-application.h"
|
||||||
|
|
||||||
// TODO: After implementing one or two more of these, abstract away some of the
|
// TODO: After implementing one or two more of these, abstract away some of the
|
||||||
// nasty bits
|
// nasty bits
|
||||||
@@ -155,10 +156,26 @@ 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
|
* Message to pass through a call to `IPluginFactory3::setHostContext()` to
|
||||||
* this is called so it can send messages.
|
* the Wine plugin host. A proxy `YaHostApplication` should be created on
|
||||||
|
* the Wine plugin host and then passed as an argument to
|
||||||
|
* `IPluginFactory3::setHostContext()`. If the host called
|
||||||
|
* `IPluginFactory3::setHostContext()` with something other than an
|
||||||
|
* `IHostApplication*`, we return an error immediately and log the call.
|
||||||
*/
|
*/
|
||||||
|
struct SetHostContext {
|
||||||
|
using Response = UniversalTResult;
|
||||||
|
|
||||||
|
YaHostApplication::ConstructArgs host_application_context_args;
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
void serialize(S& s) {
|
||||||
|
s.object(host_application_context_args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
virtual tresult PLUGIN_API
|
virtual tresult PLUGIN_API
|
||||||
setHostContext(Steinberg::FUnknown* context) override = 0;
|
setHostContext(Steinberg::FUnknown* context) override = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -64,10 +64,27 @@ YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
tresult PLUGIN_API
|
tresult PLUGIN_API
|
||||||
YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* /*context*/) {
|
YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* context) {
|
||||||
// TODO: The docs don't clearly specify what this should be doing, but from
|
// This `context` will likely be an `IHostApplication`. If it is, we will
|
||||||
// what I've seen this is only used to pass a `IHostApplication`
|
// store it for future calls, create a proxy object on the Wine side, and
|
||||||
// instance. That's used to allow the plugin to create objects in the
|
// then pass it to the Windows VST3 plugin's plugin factory using the same
|
||||||
// host.
|
// function. If we get passed anything else we'll just return instead since
|
||||||
return Steinberg::kNotImplemented;
|
// there's nothing we can do with it.
|
||||||
|
host_application_context = context;
|
||||||
|
|
||||||
|
if (host_application_context) {
|
||||||
|
YaHostApplication::ConstructArgs host_application_context_args(
|
||||||
|
host_application_context, std::nullopt);
|
||||||
|
|
||||||
|
return bridge
|
||||||
|
.send_message(YaPluginFactory::SetHostContext{
|
||||||
|
.host_application_context_args =
|
||||||
|
std::move(host_application_context_args)})
|
||||||
|
.native();
|
||||||
|
} else {
|
||||||
|
bridge.logger.log_unknown_interface(
|
||||||
|
"In IPluginFactory3::setHostContext(), ignoring",
|
||||||
|
context ? std::optional(context->iid) : std::nullopt);
|
||||||
|
return Steinberg::kNotImplemented;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,4 +30,11 @@ class YaPluginFactoryPluginImpl : public YaPluginFactory {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Vst3PluginBridge& bridge;
|
Vst3PluginBridge& bridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `IHostApplication` instance if we get one through
|
||||||
|
* `IPluginFactory3::setHostContext()`.
|
||||||
|
*/
|
||||||
|
Steinberg::FUnknownPtr<Steinberg::Vst::IHostApplication>
|
||||||
|
host_application_context;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
|||||||
sockets.connect();
|
sockets.connect();
|
||||||
|
|
||||||
// Initialize after communication has been set up
|
// Initialize after communication has been set up
|
||||||
// We'll try to do the same `get_bridge_isntance` trick as in
|
// We'll try to do the same `get_bridge_instance` trick as in
|
||||||
// `plugin/plugin.cpp`, but since the plugin will probably call the host
|
// `plugin/plugin.cpp`, but since the plugin will probably call the host
|
||||||
// callback while it's initializing we sadly have to use a global here.
|
// callback while it's initializing we sadly have to use a global here.
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -111,6 +111,19 @@ void Vst3Bridge::run() {
|
|||||||
-> YaPluginFactory::Construct::Response {
|
-> YaPluginFactory::Construct::Response {
|
||||||
return YaPluginFactory::ConstructArgs(
|
return YaPluginFactory::ConstructArgs(
|
||||||
module->getFactory().get());
|
module->getFactory().get());
|
||||||
|
},
|
||||||
|
[&](YaPluginFactory::SetHostContext& request)
|
||||||
|
-> YaPluginFactory::SetHostContext::Response {
|
||||||
|
plugin_factory_host_application_context =
|
||||||
|
Steinberg::owned(new YaHostApplicationHostImpl(
|
||||||
|
*this,
|
||||||
|
std::move(request.host_application_context_args)));
|
||||||
|
|
||||||
|
Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> factory_3(
|
||||||
|
module->getFactory().get());
|
||||||
|
assert(factory_3);
|
||||||
|
return factory_3->setHostContext(
|
||||||
|
plugin_factory_host_application_context);
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,13 @@ class Vst3Bridge : public HostBridge {
|
|||||||
*/
|
*/
|
||||||
std::atomic_size_t current_instance_id;
|
std::atomic_size_t current_instance_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host application context proxy object if we got passed a host
|
||||||
|
* application context during a call to `IPluginFactory3::setHostContext()`
|
||||||
|
* by the host.
|
||||||
|
*/
|
||||||
|
Steinberg::IPtr<YaHostApplication> plugin_factory_host_application_context;
|
||||||
|
|
||||||
// Below are managed instances we created for
|
// Below are managed instances we created for
|
||||||
// `IPluginFactory::createInstance()`. The keys in all of these maps are the
|
// `IPluginFactory::createInstance()`. The keys in all of these maps are the
|
||||||
// unique identifiers we generated for them so we can identify specific
|
// unique identifiers we generated for them so we can identify specific
|
||||||
|
|||||||
Reference in New Issue
Block a user