From f5b4a28bd07b428afe5e8ff1ceea0c7e64610f35 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 30 Jan 2021 00:02:06 +0100 Subject: [PATCH] Cache bus information during processing This works around an issue with REAPER. During every processing cycle REAPER would query how many input and output busses we have, and it would then enumerate over all of those busses. This meant that if a VST3 plugin has 32 output busses, then REAPER will do 34 extra function calls before processing audio. --- .../bridges/vst3-impls/plugin-proxy.cpp | 73 +++++++++++++++++-- src/plugin/bridges/vst3-impls/plugin-proxy.h | 34 +++++++++ 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 3688a8e3..a4ee2059 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -135,6 +135,15 @@ Vst3PluginProxyImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { } tresult PLUGIN_API Vst3PluginProxyImpl::setProcessing(TBool state) { + // REAPER will repeatedly query the plugin for its bus information on every + // processing cycle. Because this really adds up in terms of latency we + // sadly have to deviate from yabridge's principles and implement a cache + if (state) { + processing_bus_cache.emplace(); + } else { + processing_bus_cache.reset(); + } + return bridge.send_audio_processor_message(YaAudioProcessor::SetProcessing{ .instance_id = instance_id(), .state = state}); } @@ -202,8 +211,31 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setIoMode(Steinberg::Vst::IoMode mode) { int32 PLUGIN_API Vst3PluginProxyImpl::getBusCount(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir) { - return bridge.send_audio_processor_message(YaComponent::GetBusCount{ - .instance_id = instance_id(), .type = type, .dir = dir}); + const auto request = YaComponent::GetBusCount{ + .instance_id = instance_id(), .type = type, .dir = dir}; + + // During processing we'll cache this info to work around an implementation + // issue in REAPER + std::tuple args{ + type, dir}; + if (processing_bus_cache) { + if (auto it = processing_bus_cache->bus_count.find(args); + it != processing_bus_cache->bus_count.end()) { + bridge.logger.log_request(true, request); + // TODO: Add to the log message that this information was cached + bridge.logger.log_response(true, PrimitiveWrapper(it->second)); + + return it->second; + } + } + + const int32 result = bridge.send_audio_processor_message(request); + + if (processing_bus_cache) { + processing_bus_cache->bus_count[args] = result; + } + + return result; } tresult PLUGIN_API @@ -211,14 +243,39 @@ Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 index, Steinberg::Vst::BusInfo& bus /*out*/) { - const GetBusInfoResponse response = bridge.send_audio_processor_message( - YaComponent::GetBusInfo{.instance_id = instance_id(), - .type = type, - .dir = dir, - .index = index, - .bus = bus}); + const auto request = YaComponent::GetBusInfo{.instance_id = instance_id(), + .type = type, + .dir = dir, + .index = index, + .bus = bus}; + + // During processing we'll cache this info to work around an implementation + // issue in REAPER + std::tuple + args{type, dir, index}; + if (processing_bus_cache) { + if (auto it = processing_bus_cache->bus_info.find(args); + it != processing_bus_cache->bus_info.end()) { + bridge.logger.log_request(true, request); + // TODO: Add to the log message that this information was cached + bridge.logger.log_response( + true, GetBusInfoResponse{.result = Steinberg::kResultOk, + .updated_bus = it->second}); + + bus = it->second; + + return Steinberg::kResultOk; + } + } + + const GetBusInfoResponse response = + bridge.send_audio_processor_message(request); bus = response.updated_bus; + if (processing_bus_cache) { + processing_bus_cache->bus_info[args] = response.updated_bus; + } + return response.result; } diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.h b/src/plugin/bridges/vst3-impls/plugin-proxy.h index 9c11c33b..795761c3 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.h +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.h @@ -389,4 +389,38 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { * @related Vst3PluginProxyImpl::register_context_menu */ std::atomic_size_t current_context_menu_id; + + /** + * A cache for `IAudioProcessor::getBusCount()` and + * `IAudioProcessor::getBusInfo()` to work around an implementation issue in + * REAPER. If during processing a plugin returns a value for one of these + * function calls, we'll memoize the function call using the maps defined + * below. + * + * @see processing_bus_cache + */ + struct BusInfoCache { + std::map< + std::tuple, + int32> + bus_count; + std::map, + Steinberg::Vst::BusInfo> + bus_info; + }; + + /** + * HACK: To work around some behaviour in REAPER where it will repeatedly + * query the same bus information for bus during every processing + * cycle, we'll cache this information during processing. Otherwise + * this will cause `input_busses + output_busses + 2` extra + * unnecessary back and forths for every processing cycle. This can + * really add up for plugins with 16, or even 32 outputs. + * + * Since this information cannot change during processing, this will not + * contain a value while the plugin is not processing audio. + */ + std::optional processing_bus_cache; };