From 686ca11ba842075bc05028d7edd2fa0784bea80a Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 10 May 2020 13:42:20 +0200 Subject: [PATCH] Work around improperly late initializing plugins This fixes the Roland Cloud plugins. --- CHANGELOG.md | 8 ++++++++ src/common/events.h | 9 +++++++++ src/common/logging.cpp | 7 ++++--- src/common/serialization.h | 15 +++++++++++++-- src/plugin/plugin-bridge.cpp | 21 +++++++++++++++++++-- 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e8795c4..aefc8003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added a workaround for plugins that improperly defer part of their + initialization process without telling the host. This fixes the Roland Cloud + plugins. + ## [1.1.2] - 2020-05-09 ### Fixed diff --git a/src/common/events.h b/src/common/events.h index 5df65d74..0910d806 100644 --- a/src/common/events.h +++ b/src/common/events.h @@ -283,6 +283,14 @@ auto passthrough_event(AEffect* plugin, F callback) { [&](DynamicSpeakerArrangement& speaker_arrangement) -> void* { return &speaker_arrangement.as_c_speaker_arrangement(); }, + [&](WantsAEffectUpdate&) -> void* { + // The host will never actually ask for an updated `AEffect` + // object since that should not be a thing. This is purely a + // meant as a workaround for plugins that initialize their + // `AEffect` object after the plugin has already finished + // initializing. + return nullptr; + }, [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, [&](VstIOProperties& props) -> void* { return &props; }, [&](VstMidiKeyName& key_name) -> void* { return &key_name; }, @@ -335,6 +343,7 @@ auto passthrough_event(AEffect* plugin, F callback) { [&](VstParameterProperties& props) -> EventResultPayload { return props; }, + [&](WantsAEffectUpdate&) -> EventResultPayload { return *plugin; }, [&](WantsVstRect&) -> EventResultPayload { // The plugin has written a pointer to a VstRect struct into the // data poitner diff --git a/src/common/logging.cpp b/src/common/logging.cpp index 5e88f48d..6950dd26 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -201,14 +201,15 @@ void Logger::log_event(bool is_dispatch, message << "<" << speaker_arrangement.speakers.size() << " output_speakers>"; }, - [&](const WantsChunkBuffer&) { - message << ""; - }, [&](const VstIOProperties&) { message << ""; }, [&](const VstMidiKeyName&) { message << ""; }, [&](const VstParameterProperties&) { message << ""; }, + [&](const WantsAEffectUpdate&) { message << ""; }, + [&](const WantsChunkBuffer&) { + message << ""; + }, [&](const WantsVstRect&) { message << ""; }, [&](const WantsVstTimeInfo&) { message << ""; }, [&](const WantsString&) { message << ""; }}, diff --git a/src/common/serialization.h b/src/common/serialization.h index 31153852..de93fbe7 100644 --- a/src/common/serialization.h +++ b/src/common/serialization.h @@ -285,6 +285,15 @@ class alignas(16) DynamicSpeakerArrangement { std::vector speaker_arrangement_buffer; }; +/** + * Marker struct to indicate that the other side (the Wine VST host) should send + * an updated copy of the plugin's `AEffect` object. Should not be needed since + * the plugin should be calling `audioMasterIOChanged()` after it has changed + * its object, but some improperly coded plugins will only initialize their + * flags, IO properties and parameter counts after `effEditOpen()`. + */ +struct WantsAEffectUpdate {}; + /** * Marker struct to indicate that that the event writes arbitrary data into one * of its own buffers and uses the void pointer to store start of that data, @@ -352,6 +361,7 @@ using EventPayload = std::variant& chunk_data, + AEffect& plugin, VstRect& editor_rectangle) - : chunk(chunk_data), rect(editor_rectangle) {} + : chunk(chunk_data), plugin(plugin), rect(editor_rectangle) {} EventPayload read(const int opcode, const int index, @@ -239,6 +240,13 @@ class DispatchDataConverter : DefaultDataConverter { // There are some events that need specific structs that we can't simply // serialize as a string because they might contain null bytes switch (opcode) { + case effOpen: + // This should not be needed, but some improperly coded plugins + // such as the Roland Cloud plugins will initialize part of + // their `AEffect` only after the host calls `effOpen`, instead + // of during the initialization. + return WantsAEffectUpdate{}; + break; case effEditGetRect: return WantsVstRect(); break; @@ -325,6 +333,14 @@ class DispatchDataConverter : DefaultDataConverter { void write(const int opcode, void* data, const EventResult& response) { switch (opcode) { + case effOpen: { + // Update our `AEffect` object one last time for improperly + // coded late initialing plugins. Hopefully the host will see + // that the object is updated because these plugins don't send + // any notification about this. + const auto updated_plugin = std::get(response.payload); + update_aeffect(plugin, updated_plugin); + } break; case effEditGetRect: { // Write back the (hopefully) updated editor dimensions const auto new_rect = std::get(response.payload); @@ -423,6 +439,7 @@ class DispatchDataConverter : DefaultDataConverter { private: std::vector& chunk; + AEffect& plugin; VstRect& rect; }; @@ -449,7 +466,7 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/, return 0; } - DispatchDataConverter converter(chunk_data, editor_rectangle); + DispatchDataConverter converter(chunk_data, plugin, editor_rectangle); switch (opcode) { case effClose: {