From 36535fc3b5503da0322753a2c212b902e28d5538 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 19 Jan 2021 17:42:11 +0100 Subject: [PATCH] Fix VST2 editor resizing in REAPER After solving this for VST3 plugins it become clear that REAPER required us to do the exact same thing for VST2 plugins. --- CHANGELOG.md | 2 + src/plugin/bridges/vst2.cpp | 73 ++++++++++++++++++++++++++----------- src/plugin/bridges/vst2.h | 11 ++++++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f909c2..b26e8e5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,8 @@ TODO: Add an updated screenshot with some fancy VST3-only plugins to the readme ### Fixed +- VST2 plugin editor resizing in **REAPER** would not cause the FX window to be + resized like it would in every other host. This has now been fixed. - The function for suspending and resuming audio, `effMainsChanged()`, is now always executed from the GUI thread. This fixes **EZdrummer** not producing any sound because the plugin makes the incorrect assumption that diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index 8b540593..ba01a44f 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -75,29 +75,43 @@ Vst2PluginBridge::Vst2PluginBridge(audioMasterCallback host_callback) sockets.vst_host_callback.receive_events( std::pair(logger, false), [&](Event& event, bool /*on_main_thread*/) { - // MIDI events sent from the plugin back to the host are a - // special case here. They have to sent during the - // `processReplacing()` function or else the host will ignore - // them. Because of this we'll temporarily save any MIDI events - // we receive here, and then we'll actually send them to the - // host at the end of the `process_replacing()` function. - // TODO: We might be able to make editor resizing work in REAPER - // by calling `audioMasterSizeWindow()` from within - // `effEditIdle()`. Something similar was required for - // VST3 plugins in REAPER. - if (event.opcode == audioMasterProcessEvents) { - std::lock_guard lock(incoming_midi_events_mutex); + switch (event.opcode) { + // MIDI events sent from the plugin back to the host are + // a special case here. They have to sent during the + // `processReplacing()` function or else the host will + // ignore them. Because of this we'll temporarily save + // any MIDI events we receive here, and then we'll + // actually send them to the host at the end of the + // `process_replacing()` function. + case audioMasterProcessEvents: { + std::lock_guard lock(incoming_midi_events_mutex); - incoming_midi_events.push_back( - std::get(event.payload)); - EventResult response{.return_value = 1, - .payload = nullptr, - .value_payload = std::nullopt}; + incoming_midi_events.push_back( + std::get(event.payload)); + EventResult response{.return_value = 1, + .payload = nullptr, + .value_payload = std::nullopt}; - return response; - } else { - return passthrough_event(&plugin, host_callback_function, - event); + return response; + } break; + // REAPER requires that `audioMasterSizeWindow()` calls are + // handled from the GUI thread, which is the thread that + // will call `effEditIdle()`. To account for this, we'll + // store the last resize request and then only pass it to + // the host when it calls `effEditIdle()`. + case audioMasterSizeWindow: { + std::lock_guard lock(incoming_resize_mutex); + + incoming_resize = std::pair(event.index, event.value); + EventResult response{.return_value = 1, + .payload = nullptr, + .value_payload = std::nullopt}; + + return response; + } break; + default: + return passthrough_event(&plugin, + host_callback_function, event); } }); }); @@ -413,6 +427,23 @@ intptr_t Vst2PluginBridge::dispatch(AEffect* /*plugin*/, // blocked by for instance an open dropdown. logger.log_event(true, opcode, index, value, nullptr, option, std::nullopt); + + // REAPEr requires `audioMasterSizeWindow()` calls to be done from + // the GUI thread. In every other host this doesn't make a + // difference, but in REAPER the FX window only resizes when this is + // called from here. + { + std::unique_lock lock(incoming_resize_mutex); + if (incoming_resize) { + const auto& [width, height] = *incoming_resize; + incoming_resize.reset(); + lock.unlock(); + + host_callback_function(&plugin, audioMasterSizeWindow, + width, height, nullptr, 0.0); + } + } + logger.log_event_response(true, opcode, 0, nullptr, std::nullopt); return 0; }; break; diff --git a/src/plugin/bridges/vst2.h b/src/plugin/bridges/vst2.h index 92b2dfb8..9e3c34be 100644 --- a/src/plugin/bridges/vst2.h +++ b/src/plugin/bridges/vst2.h @@ -187,4 +187,15 @@ class Vst2PluginBridge : PluginBridge> { * now happens in two different threads. */ std::mutex incoming_midi_events_mutex; + + /** + * REAPER requires us to call `audioMasterSizeWidnow()` from the same thread + * that's calling `effEditIdle()`. If we call this from any other thread, + * then the FX window won't be resized. To accommodate for this, we'll store + * the width and the height passed to the last call to + * `audioMasterSizeWindow`. If this contains a value, we'll then call + * `audioMasterSizeWindow()` with the new size during `effEditIdle()`. + */ + std::optional> incoming_resize; + std::mutex incoming_resize_mutex; };