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: - 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
+1 -1
View File
@@ -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()`.
* *
+7
View File
@@ -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>";
+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::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&);
+2 -1
View File
@@ -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) {
+3 -2
View File
@@ -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>);
+19 -2
View File
@@ -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;
}; };
+1 -1
View File
@@ -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.
{ {
+13
View File
@@ -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);
}}); }});
} }
+7
View File
@@ -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