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:
Robbert van der Helm
2020-12-13 15:50:41 +01:00
parent 7c5f7a2e0e
commit 32c1028736
14 changed files with 95 additions and 24 deletions
+1 -3
View File
@@ -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
+1 -1
View File
@@ -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()`.
*
+7
View File
@@ -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>";
+1
View File
@@ -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&);
+2 -1
View File
@@ -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) {
+3 -2
View File
@@ -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>);
+19 -2
View File
@@ -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;
};
+1 -1
View File
@@ -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.
{
+13
View File
@@ -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);
}});
}
+7
View File
@@ -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