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; };