From c82eb35243c4788ab83d07f337747eb3a2c38311 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 29 Apr 2021 01:02:10 +0200 Subject: [PATCH] Also cache audioMasterGetCurrentProcessLevel() Melda plugins seem to call this during every processing cycle. --- CHANGELOG.md | 2 ++ src/common/serialization/vst2.h | 11 +++++++++-- src/plugin/bridges/vst2.cpp | 11 ++++++++++- src/wine-host/bridges/vst2.cpp | 30 +++++++++++++++++++++++++----- src/wine-host/bridges/vst2.h | 6 ++++++ 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d31b0fa6..9fa7c002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ Versioning](https://semver.org/spec/v2.0.0.html). information repeatedly for every sample they process. Previously yabridge had a `cache_time_info` compatibility option to mitigate the performance hit for those plugins, but this new caching behaviour supercedes that. +- Similarly, yabridge will also cache the current process level during audio + processing to reduce bridging overhead for plugins that use this. - We now always force the CPU's flush-to-zero flag to be set when processing audio. Most plugins will already do this themselves, but plugins like _Kush Audio REDDI_ and _Expressive E Noisy_ that don't will otherwise suffer from diff --git a/src/common/serialization/vst2.h b/src/common/serialization/vst2.h index becdc0c7..f13acb1b 100644 --- a/src/common/serialization/vst2.h +++ b/src/common/serialization/vst2.h @@ -559,11 +559,16 @@ struct AudioBuffers { * We'll send the current transport information as part of an audio * processing call. This lets us a void an unnecessary callback (or in some * cases, more than one) during every processing cycle. - * - * TODO: Do the same thing for the current process level */ std::optional current_time_info; + /** + * Some plugins will also ask for the current process level during audio + * processing. To prevent unnecessary expensive callbacks, we'll send this + * information along with the processing call. + */ + int current_process_level; + /** * We'll periodically synchronize the realtime priority setting of the * host's audio thread with the Wine plugin host. We'll do this @@ -592,6 +597,8 @@ struct AudioBuffers { s.value4b(sample_frames); s.ext(current_time_info, bitsery::ext::StdOptional{}); + s.value4b(current_process_level); + s.ext(new_realtime_priority, bitsery::ext::StdOptional{}, [](S& s, int& priority) { s.value4b(priority); }); } diff --git a/src/plugin/bridges/vst2.cpp b/src/plugin/bridges/vst2.cpp index a1583ff0..a2ee1e26 100644 --- a/src/plugin/bridges/vst2.cpp +++ b/src/plugin/bridges/vst2.cpp @@ -568,10 +568,18 @@ void Vst2PluginBridge::do_process(T** inputs, T** outputs, int sample_frames) { current_time_info = *returned_time_info; } + // Some plugisn also ask for the current process level, so we'll cache that + // information as well + const int current_process_level = static_cast(host_callback_function( + &plugin, audioMasterGetCurrentProcessLevel, 0, 0, nullptr, 0.0)); + if (returned_time_info) { + current_time_info = *returned_time_info; + } + // We'll synchronize the scheduling priority of the audio thread on the Wine // plugin host with that of the host's audio thread every once in a while std::optional new_realtime_priority; - time_t now = std::time(nullptr); + const time_t now = std::time(nullptr); if (now > last_audio_thread_priority_synchronization + audio_thread_priority_synchronization_interval) { new_realtime_priority = get_realtime_priority(); @@ -590,6 +598,7 @@ void Vst2PluginBridge::do_process(T** inputs, T** outputs, int sample_frames) { const AudioBuffers request{.buffers = input_buffers, .sample_frames = sample_frames, .current_time_info = current_time_info, + .current_process_level = current_process_level, .new_realtime_priority = new_realtime_priority}; sockets.host_vst_process_replacing.send(request, process_buffer); diff --git a/src/wine-host/bridges/vst2.cpp b/src/wine-host/bridges/vst2.cpp index dd0fb193..6044a5e8 100644 --- a/src/wine-host/bridges/vst2.cpp +++ b/src/wine-host/bridges/vst2.cpp @@ -190,11 +190,17 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, // we'll send the current transport information as part of the // request so we cache it to avoid unnecessary callbacks from // the audio thread - std::optional cache_guard = - request.current_time_info - ? std::optional( - time_info_cache.set(*request.current_time_info)) - : std::nullopt; + std::optional + time_info_cache_guard = + request.current_time_info + ? std::optional(time_info_cache.set( + *request.current_time_info)) + : std::nullopt; + + // We'll also cache the process level, since some plugins will + // ask for this during every processing cycle + decltype(process_level_cache)::Guard process_level_cache_guard = + process_level_cache.set(request.current_process_level); // As suggested by Jack Winter, we'll synchronize this thread's // audio processing priority with that of the host's audio @@ -266,6 +272,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, .buffers = output_buffers_single_precision, .sample_frames = request.sample_frames, .current_time_info = std::nullopt, + .current_process_level = 0, .new_realtime_priority = std::nullopt}; sockets.host_vst_process_replacing.send(response, buffer); @@ -295,6 +302,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context, .buffers = output_buffers_double_precision, .sample_frames = request.sample_frames, .current_time_info = std::nullopt, + .current_process_level = 0, .new_realtime_priority = std::nullopt}; sockets.host_vst_process_replacing.send(response, buffer); @@ -620,6 +628,18 @@ intptr_t Vst2Bridge::host_callback(AEffect* effect, return result; } } break; + case audioMasterGetCurrentProcessLevel: { + // We also send the current process level for similar reasons + const int* current_process_level = process_level_cache.get(); + if (current_process_level) { + logger.log_event(false, opcode, index, value, nullptr, option, + std::nullopt); + logger.log_event_response(false, opcode, *current_process_level, + nullptr, std::nullopt, true); + + return *current_process_level; + } + } break; } HostCallbackDataConverter converter(effect, last_time_info); diff --git a/src/wine-host/bridges/vst2.h b/src/wine-host/bridges/vst2.h index e94c2398..a9eac078 100644 --- a/src/wine-host/bridges/vst2.h +++ b/src/wine-host/bridges/vst2.h @@ -129,6 +129,12 @@ class Vst2Bridge : public HostBridge { */ ScopedValueCache time_info_cache; + /** + * Some plugins will also ask for the current process level during audio + * processing, so we'll also cache that to prevent expensive callbacks. + */ + ScopedValueCache process_level_cache; + // FIXME: This emits `-Wignored-attributes` as of Wine 5.22 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-attributes"