Work around improperly late initializing plugins

This fixes the Roland Cloud plugins.
This commit is contained in:
Robbert van der Helm
2020-05-10 13:42:20 +02:00
parent ba91971829
commit 686ca11ba8
5 changed files with 53 additions and 7 deletions
+8
View File
@@ -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 and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html). 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 ## [1.1.2] - 2020-05-09
### Fixed ### Fixed
+9
View File
@@ -283,6 +283,14 @@ auto passthrough_event(AEffect* plugin, F callback) {
[&](DynamicSpeakerArrangement& speaker_arrangement) -> void* { [&](DynamicSpeakerArrangement& speaker_arrangement) -> void* {
return &speaker_arrangement.as_c_speaker_arrangement(); 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(); }, [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); },
[&](VstIOProperties& props) -> void* { return &props; }, [&](VstIOProperties& props) -> void* { return &props; },
[&](VstMidiKeyName& key_name) -> void* { return &key_name; }, [&](VstMidiKeyName& key_name) -> void* { return &key_name; },
@@ -335,6 +343,7 @@ auto passthrough_event(AEffect* plugin, F callback) {
[&](VstParameterProperties& props) -> EventResultPayload { [&](VstParameterProperties& props) -> EventResultPayload {
return props; return props;
}, },
[&](WantsAEffectUpdate&) -> EventResultPayload { return *plugin; },
[&](WantsVstRect&) -> EventResultPayload { [&](WantsVstRect&) -> EventResultPayload {
// The plugin has written a pointer to a VstRect struct into the // The plugin has written a pointer to a VstRect struct into the
// data poitner // data poitner
+4 -3
View File
@@ -201,14 +201,15 @@ void Logger::log_event(bool is_dispatch,
message << "<" << speaker_arrangement.speakers.size() message << "<" << speaker_arrangement.speakers.size()
<< " output_speakers>"; << " output_speakers>";
}, },
[&](const WantsChunkBuffer&) {
message << "<writable_buffer>";
},
[&](const VstIOProperties&) { message << "<io_properties>"; }, [&](const VstIOProperties&) { message << "<io_properties>"; },
[&](const VstMidiKeyName&) { message << "<key_name>"; }, [&](const VstMidiKeyName&) { message << "<key_name>"; },
[&](const VstParameterProperties&) { [&](const VstParameterProperties&) {
message << "<writable_buffer>"; message << "<writable_buffer>";
}, },
[&](const WantsAEffectUpdate&) { message << "<nullptr>"; },
[&](const WantsChunkBuffer&) {
message << "<writable_buffer>";
},
[&](const WantsVstRect&) { message << "<writable_buffer>"; }, [&](const WantsVstRect&) { message << "<writable_buffer>"; },
[&](const WantsVstTimeInfo&) { message << "<nullptr>"; }, [&](const WantsVstTimeInfo&) { message << "<nullptr>"; },
[&](const WantsString&) { message << "<writable_string>"; }}, [&](const WantsString&) { message << "<writable_string>"; }},
+13 -2
View File
@@ -285,6 +285,15 @@ class alignas(16) DynamicSpeakerArrangement {
std::vector<uint8_t> speaker_arrangement_buffer; std::vector<uint8_t> 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 * 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, * of its own buffers and uses the void pointer to store start of that data,
@@ -352,6 +361,7 @@ using EventPayload = std::variant<std::nullptr_t,
AEffect, AEffect,
DynamicVstEvents, DynamicVstEvents,
DynamicSpeakerArrangement, DynamicSpeakerArrangement,
WantsAEffectUpdate,
WantsChunkBuffer, WantsChunkBuffer,
VstIOProperties, VstIOProperties,
VstMidiKeyName, VstMidiKeyName,
@@ -382,8 +392,9 @@ void serialize(S& s, EventPayload& payload) {
[](S& s, VstIOProperties& props) { s.object(props); }, [](S& s, VstIOProperties& props) { s.object(props); },
[](S& s, VstMidiKeyName& key_name) { s.object(key_name); }, [](S& s, VstMidiKeyName& key_name) { s.object(key_name); },
[](S& s, VstParameterProperties& props) { s.object(props); }, [](S& s, VstParameterProperties& props) { s.object(props); },
[](S&, WantsChunkBuffer&) {}, [](S&, WantsVstRect&) {}, [](S&, WantsAEffectUpdate&) {}, [](S&, WantsChunkBuffer&) {},
[](S&, WantsVstTimeInfo&) {}, [](S&, WantsString&) {}}); [](S&, WantsVstRect&) {}, [](S&, WantsVstTimeInfo&) {},
[](S&, WantsString&) {}});
} }
/** /**
+19 -2
View File
@@ -229,8 +229,9 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
class DispatchDataConverter : DefaultDataConverter { class DispatchDataConverter : DefaultDataConverter {
public: public:
DispatchDataConverter(std::vector<uint8_t>& chunk_data, DispatchDataConverter(std::vector<uint8_t>& chunk_data,
AEffect& plugin,
VstRect& editor_rectangle) VstRect& editor_rectangle)
: chunk(chunk_data), rect(editor_rectangle) {} : chunk(chunk_data), plugin(plugin), rect(editor_rectangle) {}
EventPayload read(const int opcode, EventPayload read(const int opcode,
const int index, const int index,
@@ -239,6 +240,13 @@ class DispatchDataConverter : DefaultDataConverter {
// There are some events that need specific structs that we can't simply // There are some events that need specific structs that we can't simply
// serialize as a string because they might contain null bytes // serialize as a string because they might contain null bytes
switch (opcode) { 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: case effEditGetRect:
return WantsVstRect(); return WantsVstRect();
break; break;
@@ -325,6 +333,14 @@ class DispatchDataConverter : DefaultDataConverter {
void write(const int opcode, void* data, const EventResult& response) { void write(const int opcode, void* data, const EventResult& response) {
switch (opcode) { 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<AEffect>(response.payload);
update_aeffect(plugin, updated_plugin);
} break;
case effEditGetRect: { case effEditGetRect: {
// Write back the (hopefully) updated editor dimensions // Write back the (hopefully) updated editor dimensions
const auto new_rect = std::get<VstRect>(response.payload); const auto new_rect = std::get<VstRect>(response.payload);
@@ -423,6 +439,7 @@ class DispatchDataConverter : DefaultDataConverter {
private: private:
std::vector<uint8_t>& chunk; std::vector<uint8_t>& chunk;
AEffect& plugin;
VstRect& rect; VstRect& rect;
}; };
@@ -449,7 +466,7 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
return 0; return 0;
} }
DispatchDataConverter converter(chunk_data, editor_rectangle); DispatchDataConverter converter(chunk_data, plugin, editor_rectangle);
switch (opcode) { switch (opcode) {
case effClose: { case effClose: {