mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-06 19:40:10 +02:00
Reload supported interfaces after IPluginBase::initialize()
This is needed as a workaround to support Waves VST3 plugins. Right now does does not actually fix the issue because the arguments are not updated in the subclasses. The next commit will fix this.
This commit is contained in:
@@ -55,6 +55,10 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
happened often with _JUCE_ based plugins like Sonic Academy's _Kick 2_ and
|
||||
_Anaglyph_. While this is technically a workaround for a bad interaction with
|
||||
JUCE and Wine, it should make these plugins a lot more pleasant to use.
|
||||
- Fixed **Waves** VST3 plugins not being able to initialize correctly. These
|
||||
plugins would at runtime change their query interface to support more VST3
|
||||
interfaces, including the required edit controller interface. Yabridge now
|
||||
requeries the supported interfaces at a later stage to work around this.
|
||||
- Fixed missing transport information for VST2 plugins in **Ardour**, breaking
|
||||
host sync and LFOs in certain plugins. This was a regression from yabridge
|
||||
3.2.0.
|
||||
|
||||
@@ -699,7 +699,7 @@ bool Vst3Logger::log_request(
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaPluginBase::Initialize& request) {
|
||||
const Vst3PluginProxy::Initialize& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IPluginBase::initialize(context = <FUnknown*>)";
|
||||
@@ -1468,6 +1468,12 @@ void Vst3Logger::log_response(bool is_host_vst,
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const Vst3PluginProxy::InitializeResponse& response) {
|
||||
log_response(is_host_vst, response.result);
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const Vst3PluginProxy::GetStateResponse& response) {
|
||||
|
||||
@@ -67,6 +67,7 @@ class Vst3Logger {
|
||||
bool log_request(bool is_host_vst, const Vst3PlugViewProxy::Destruct&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::Construct&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::Destruct&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::Initialize&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::SetState&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::GetState&);
|
||||
bool log_request(
|
||||
@@ -150,7 +151,6 @@ class Vst3Logger {
|
||||
bool log_request(
|
||||
bool is_host_vst,
|
||||
const YaPlugViewContentScaleSupport::SetContentScaleFactor&);
|
||||
bool log_request(bool is_host_vst, const YaPluginBase::Initialize&);
|
||||
bool log_request(bool is_host_vst, const YaPluginBase::Terminate&);
|
||||
bool log_request(bool is_host_vst, const YaPluginFactory3::SetHostContext&);
|
||||
bool log_request(
|
||||
@@ -252,6 +252,8 @@ class Vst3Logger {
|
||||
void log_response(
|
||||
bool is_host_vst,
|
||||
const std::variant<Vst3PluginProxy::ConstructArgs, UniversalTResult>&);
|
||||
void log_response(bool is_host_vst,
|
||||
const Vst3PluginProxy::InitializeResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const Vst3PluginProxy::GetStateResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
|
||||
@@ -70,6 +70,11 @@ using ControlRequest =
|
||||
Vst3PlugViewProxy::Destruct,
|
||||
Vst3PluginProxy::Construct,
|
||||
Vst3PluginProxy::Destruct,
|
||||
// This is actually part of `YaPluginBase`, but thanks to Waves
|
||||
// we had to move this message to the main `Vst3PluginProxy`
|
||||
// class
|
||||
Vst3PluginProxy::Initialize,
|
||||
// These are defined in both `IComponent` and `IEditController`
|
||||
Vst3PluginProxy::SetState,
|
||||
Vst3PluginProxy::GetState,
|
||||
YaAudioPresentationLatency::SetAudioPresentationLatencySamples,
|
||||
@@ -119,7 +124,6 @@ using ControlRequest =
|
||||
YaPlugView::CanResize,
|
||||
YaPlugView::CheckSizeConstraint,
|
||||
YaPlugViewContentScaleSupport::SetContentScaleFactor,
|
||||
YaPluginBase::Initialize,
|
||||
YaPluginBase::Terminate,
|
||||
YaPluginFactory3::SetHostContext,
|
||||
YaProcessContextRequirements::GetProcessContextRequirements,
|
||||
|
||||
@@ -232,6 +232,53 @@ class Vst3PluginProxy : public YaAudioPresentationLatency,
|
||||
*/
|
||||
inline size_t instance_id() const noexcept { return arguments.instance_id; }
|
||||
|
||||
// These have to be defined here instead of in `YaPluginBase` because we
|
||||
// need to reference the `ConstructArgs`
|
||||
|
||||
/**
|
||||
* The response code and updated supported interface list after a call to
|
||||
* `IPluginBase::initialize()`.
|
||||
*
|
||||
* HACK: This is needed to support Waves VST3 plugins because they only
|
||||
* expose the edit controller interface after this point
|
||||
*/
|
||||
struct InitializeResponse {
|
||||
UniversalTResult result;
|
||||
|
||||
// This is a very ugly hack, but we'll just have to requery all
|
||||
// supported interfaces and replace the original constructargs in the
|
||||
// plugin-side proxy object
|
||||
Vst3PluginProxy::ConstructArgs updated_plugin_interfaces;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.object(result);
|
||||
s.object(updated_plugin_interfaces);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Message to pass through a call to `IPluginBase::initialize()` to the Wine
|
||||
* plugin host. We will read what interfaces the passed context object
|
||||
* implements so we can then create a proxy object on the Wine side that the
|
||||
* plugin can use to make callbacks with. The lifetime of this
|
||||
* `Vst3HostContextProxy` object should be bound to the `IComponent` we are
|
||||
* proxying.
|
||||
*/
|
||||
struct Initialize {
|
||||
using Response = InitializeResponse;
|
||||
|
||||
native_size_t instance_id;
|
||||
|
||||
Vst3HostContextProxy::ConstructArgs host_context_args;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value8b(instance_id);
|
||||
s.object(host_context_args);
|
||||
}
|
||||
};
|
||||
|
||||
// We'll define messages for functions that have identical definitions in
|
||||
// multiple interfaces below. When the Wine plugin host process handles
|
||||
// these it should check which of the interfaces is supported on the host.
|
||||
@@ -287,7 +334,7 @@ class Vst3PluginProxy : public YaAudioPresentationLatency,
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
protected:
|
||||
ConstructArgs arguments;
|
||||
};
|
||||
|
||||
|
||||
@@ -63,28 +63,9 @@ class YaPluginBase : public Steinberg::IPluginBase {
|
||||
|
||||
inline bool supported() const noexcept { return arguments.supported; }
|
||||
|
||||
/**
|
||||
* Message to pass through a call to `IPluginBase::initialize()` to the Wine
|
||||
* plugin host. We will read what interfaces the passed context object
|
||||
* implements so we can then create a proxy object on the Wine side that the
|
||||
* plugin can use to make callbacks with. The lifetime of this
|
||||
* `Vst3HostContextProxy` object should be bound to the `IComponent` we are
|
||||
* proxying.
|
||||
*/
|
||||
struct Initialize {
|
||||
using Response = UniversalTResult;
|
||||
|
||||
native_size_t instance_id;
|
||||
|
||||
Vst3HostContextProxy::ConstructArgs host_context_args;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.value8b(instance_id);
|
||||
s.object(host_context_args);
|
||||
}
|
||||
};
|
||||
|
||||
// The request and response for `IPluginBase::initialize()` is defined
|
||||
// within `Vst3PluginProxy` because it (thanks to Waves) requires all
|
||||
// supported interfaces to be queried again
|
||||
virtual tresult PLUGIN_API initialize(FUnknown* context) override = 0;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1057,10 +1057,18 @@ tresult PLUGIN_API Vst3PluginProxyImpl::initialize(FUnknown* context) {
|
||||
host_application = host_context;
|
||||
plug_interface_support = host_context;
|
||||
|
||||
return bridge.send_message(YaPluginBase::Initialize{
|
||||
.instance_id = instance_id(),
|
||||
.host_context_args = Vst3HostContextProxy::ConstructArgs(
|
||||
host_context, instance_id())});
|
||||
const InitializeResponse response =
|
||||
bridge.send_message(Vst3PluginProxy::Initialize{
|
||||
.instance_id = instance_id(),
|
||||
.host_context_args = Vst3HostContextProxy::ConstructArgs(
|
||||
host_context, instance_id())});
|
||||
|
||||
// HACK: For some reason, Waves plugins will only allow querying the
|
||||
// `IEditController` interface after this point, so we need to
|
||||
// update the list of interfaces we support for this object.
|
||||
arguments = response.updated_plugin_interfaces;
|
||||
|
||||
return response.result;
|
||||
} else {
|
||||
bridge.logger.log(
|
||||
"WARNING: Null pointer passed to 'IPluginBase::initialize()'");
|
||||
|
||||
@@ -906,8 +906,8 @@ void Vst3Bridge::run() {
|
||||
.get();
|
||||
}
|
||||
},
|
||||
[&](YaPluginBase::Initialize& request)
|
||||
-> YaPluginBase::Initialize::Response {
|
||||
[&](Vst3PluginProxy::Initialize& request)
|
||||
-> Vst3PluginProxy::Initialize::Response {
|
||||
Vst3PluginInstance& instance =
|
||||
object_instances.at(request.instance_id);
|
||||
|
||||
@@ -923,24 +923,43 @@ void Vst3Bridge::run() {
|
||||
// `IPlugView::{initialize,terminate}`, we'll run these
|
||||
// functions from the main GUI thread
|
||||
return main_context
|
||||
.run_in_context([&]() -> tresult {
|
||||
// The plugin may try to spawn audio worker threads
|
||||
// during its initialization
|
||||
set_realtime_priority(true);
|
||||
// This static cast is required to upcast to `FUnknown*`
|
||||
const tresult result =
|
||||
instance.interfaces.plugin_base->initialize(
|
||||
static_cast<YaHostApplication*>(
|
||||
instance.host_context_proxy));
|
||||
set_realtime_priority(false);
|
||||
.run_in_context(
|
||||
[&]() -> Vst3PluginProxy::InitializeResponse {
|
||||
// The plugin may try to spawn audio worker threads
|
||||
// during its initialization
|
||||
set_realtime_priority(true);
|
||||
// This static cast is required to upcast to
|
||||
// `FUnknown*`
|
||||
const tresult result =
|
||||
instance.interfaces.plugin_base->initialize(
|
||||
static_cast<YaHostApplication*>(
|
||||
instance.host_context_proxy));
|
||||
set_realtime_priority(false);
|
||||
|
||||
// The Win32 message loop will not be run up to this
|
||||
// point to prevent plugins with partially initialized
|
||||
// states from misbehaving
|
||||
instance.is_initialized = true;
|
||||
// HACK: Waves plugins for some reason only add
|
||||
// `IEditController` to their query interface
|
||||
// after `IPluginBase::initialize()` has been
|
||||
// called, so we need to update the list of
|
||||
// supported interfaces at this point. This
|
||||
// needs to be done on both the Wine and the
|
||||
// plugin since, so we also need to return an
|
||||
// updated list of supported interfaces.
|
||||
instance.interfaces =
|
||||
Vst3PluginInterfaces(instance.object);
|
||||
|
||||
return result;
|
||||
})
|
||||
Vst3PluginProxy::ConstructArgs updated_interfaces(
|
||||
instance.object, request.instance_id);
|
||||
|
||||
// The Win32 message loop will not be run up to this
|
||||
// point to prevent plugins with partially
|
||||
// initialized states from misbehaving
|
||||
instance.is_initialized = true;
|
||||
|
||||
return Vst3PluginProxy::InitializeResponse{
|
||||
.result = result,
|
||||
.updated_plugin_interfaces =
|
||||
updated_interfaces};
|
||||
})
|
||||
.get();
|
||||
},
|
||||
[&](const YaPluginBase::Terminate& request)
|
||||
|
||||
Reference in New Issue
Block a user