From e4f2e8c27f07438bd4fc11f017726769c120cce2 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 15 Jul 2021 21:05:36 +0200 Subject: [PATCH] Add a separate audio thread mutual recursion stack This should fix #118 without breaking our _other_ workaround from yabridge 3.4.0 to fix the issue where a plugin would freeze if it would try to resize itself while at the same time it sent parameter changes from the audio thread. (and both of these issues of course are caused by the same JUCE bug) --- CHANGELOG.md | 7 +++++++ src/wine-host/bridges/vst3.h | 33 +++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9791d7..ddb01913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ 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] + +### Fixed + +- Fixed a regression from yabridge 3.4.0 where JUCE-based VST3 plugins might + cause **Ardour** or **Mixbus** to freeze. + ## [3.4.0] - 2021-07-15 ### Added diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 233a6694..363db02c 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -337,8 +337,9 @@ class Vst3Bridge : public HostBridge { * didn't implement the VST3 output parameters, and if at the same * time a resize request comes in from the host that would mean that * the resize request is also called from the audio thread. To prevent - * this we will make sure to only do this mutual recursion stuff if - * this is actually called form the GUI thread. + * this we need to have two separate mutual recursion stacks for the + * GUI thread and for other threads. See the docstring on + * `audio_thread_mutual_recursion` for why _that_ is necessary. */ template typename T::Response send_mutually_recursive_message(const T& object) { @@ -346,7 +347,8 @@ class Vst3Bridge : public HostBridge { return mutual_recursion.fork( [&]() { return send_message(object); }); } else { - return send_message(object); + return audio_thread_mutual_recursion.fork( + [&]() { return send_message(object); }); } } @@ -381,7 +383,12 @@ class Vst3Bridge : public HostBridge { */ template std::invoke_result_t do_mutual_recursion_on_off_thread(F&& fn) { - return mutual_recursion.handle(std::forward(fn)); + if (const auto result = audio_thread_mutual_recursion.maybe_handle( + std::forward(fn))) { + return *result; + } else { + return mutual_recursion.handle(std::forward(fn)); + } } /** @@ -492,4 +499,22 @@ class Vst3Bridge : public HostBridge { * response. */ MutualRecursionHelper mutual_recursion; + + /** + * The same thing as above, but just for the pair of + * `IEditController::setParamNormalized()` and + * `IComponentHandler::performEdit()`, when + * `IComponentHandler::performEdit()` is called from an audio thread. + * + * HACK: This is sadly needed to work around an interaction between a bug in + * JUCE with a bug in Ardour/Mixbus. JUCE calls + * `IComponentHandler::performEdit()` from the audio thread instead of + * using the output parameters, and Ardour/Mixbus immediately call + * `IEditController::setParamNormalized()` with the same value after + * the plugin calls `IComponentHandler::performEdit()`. Both of these + * functions need to be run on the same thread (because of recursive + * mutexes), but they may not interfere with the GUI thread if + * `IComponentHandler::performEdit()` wasn't called from there. + */ + MutualRecursionHelper audio_thread_mutual_recursion; };