mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 20:10:13 +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:
|
||||
- `YaHostApplicationHostImpl::createComponent`.
|
||||
- `IPluginFactory3::setHostContext()` using the same host application context proxy method.
|
||||
- The rest of `IComponent`'s functions
|
||||
- `IPluginFactory3::setHostContext()`
|
||||
- All other mandatory interfaces
|
||||
- All other optional interfaces
|
||||
- Fully implemented:
|
||||
- Nothing yet
|
||||
- `GetPluginFactory()` and `IPluginFactory{,2,3}`
|
||||
- Update the GitHub Actions workflows.
|
||||
- Update yabridgectl to handle buth VST2 and VST3 plugins.
|
||||
- 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
|
||||
* 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
|
||||
* 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
|
||||
* socket, either `AEffect::dispatcher()` or `audioMasterCallback()`.
|
||||
*
|
||||
|
||||
@@ -78,6 +78,13 @@ void Vst3Logger::log_request(bool is_host_vst,
|
||||
[](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&) {
|
||||
log_request_base(is_host_vst, [](auto& message) {
|
||||
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::Terminate&);
|
||||
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_response(bool is_host_vst, const Ack&);
|
||||
|
||||
@@ -61,7 +61,8 @@ using ControlRequest = std::variant<YaComponent::Construct,
|
||||
YaComponent::Destruct,
|
||||
YaComponent::Initialize,
|
||||
YaComponent::Terminate,
|
||||
YaPluginFactory::Construct>;
|
||||
YaPluginFactory::Construct,
|
||||
YaPluginFactory::SetHostContext>;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, ControlRequest& payload) {
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
* for everything other than the edit controller's class ID.
|
||||
*
|
||||
* 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 {
|
||||
public:
|
||||
@@ -57,7 +58,7 @@ class YaComponent : public Steinberg::Vst::IComponent {
|
||||
* Read arguments from an existing implementation.
|
||||
*/
|
||||
ConstructArgs(Steinberg::IPtr<Steinberg::Vst::IComponent> component,
|
||||
size_t isntance_id);
|
||||
size_t instance_id);
|
||||
|
||||
/**
|
||||
* The unique identifier for this specific instance.
|
||||
|
||||
@@ -20,7 +20,7 @@ YaHostApplication::ConstructArgs::ConstructArgs() {}
|
||||
|
||||
YaHostApplication::ConstructArgs::ConstructArgs(
|
||||
Steinberg::IPtr<Steinberg::Vst::IHostApplication> context,
|
||||
size_t component_instance_id)
|
||||
std::optional<size_t> component_instance_id)
|
||||
: component_instance_id(component_instance_id) {
|
||||
Steinberg::Vst::String128 name_array;
|
||||
if (context->getName(name_array) == Steinberg::kResultOk) {
|
||||
|
||||
@@ -48,16 +48,15 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication {
|
||||
* Read arguments from an existing implementation.
|
||||
*/
|
||||
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
|
||||
* been passed to and thus belongs to.
|
||||
*
|
||||
* TODO: When we implement `IPluginFactory3::setHostContext()` this
|
||||
* should be made optional.
|
||||
* been passed to and thus belongs to, if we are handling
|
||||
* `IpluginBase::initialize()`. When handling
|
||||
* `IPluginFactory::setHostContext()` this will be empty.
|
||||
*/
|
||||
native_size_t component_instance_id;
|
||||
std::optional<native_size_t> component_instance_id;
|
||||
|
||||
/**
|
||||
* For `IHostApplication::getName`.
|
||||
@@ -66,7 +65,10 @@ class YaHostApplication : public Steinberg::Vst::IHostApplication {
|
||||
|
||||
template <typename 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& s, std::u16string& name) {
|
||||
s.text2b(name, std::extent_v<Steinberg::Vst::String128>);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "../../bitsery/ext/vst3.h"
|
||||
#include "base.h"
|
||||
#include "host-application.h"
|
||||
|
||||
// TODO: After implementing one or two more of these, abstract away some of the
|
||||
// nasty bits
|
||||
@@ -155,10 +156,26 @@ 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.
|
||||
* Message to pass through a call to `IPluginFactory3::setHostContext()` to
|
||||
* 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
|
||||
setHostContext(Steinberg::FUnknown* context) override = 0;
|
||||
|
||||
|
||||
@@ -64,10 +64,27 @@ YaPluginFactoryPluginImpl::createInstance(Steinberg::FIDString cid,
|
||||
}
|
||||
|
||||
tresult PLUGIN_API
|
||||
YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* /*context*/) {
|
||||
// TODO: The docs don't clearly specify what this should be doing, but from
|
||||
// what I've seen this is only used to pass a `IHostApplication`
|
||||
// instance. That's used to allow the plugin to create objects in the
|
||||
// host.
|
||||
return Steinberg::kNotImplemented;
|
||||
YaPluginFactoryPluginImpl::setHostContext(Steinberg::FUnknown* context) {
|
||||
// This `context` will likely be an `IHostApplication`. If it is, we will
|
||||
// store it for future calls, create a proxy object on the Wine side, and
|
||||
// then pass it to the Windows VST3 plugin's plugin factory using the same
|
||||
// function. If we get passed anything else we'll just return instead since
|
||||
// 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:
|
||||
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();
|
||||
|
||||
// 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
|
||||
// callback while it's initializing we sadly have to use a global here.
|
||||
{
|
||||
|
||||
@@ -111,6 +111,19 @@ void Vst3Bridge::run() {
|
||||
-> YaPluginFactory::Construct::Response {
|
||||
return YaPluginFactory::ConstructArgs(
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
// `IPluginFactory::createInstance()`. The keys in all of these maps are the
|
||||
// unique identifiers we generated for them so we can identify specific
|
||||
|
||||
Reference in New Issue
Block a user