diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 87233b6d..a5ea3952 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -63,6 +63,12 @@ bool Vst3PluginProxyImpl::unregister_context_menu(size_t context_menu_id) { return context_menus.erase(context_menu_id); } +void Vst3PluginProxyImpl::clear_bus_cache() { + if (processing_bus_cache) { + processing_bus_cache.emplace(); + } +} + tresult PLUGIN_API Vst3PluginProxyImpl::setAudioPresentationLatencySamples( Steinberg::Vst::BusDirection dir, int32 busIndex, @@ -80,6 +86,8 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setBusArrangements( int32 numIns, Steinberg::Vst::SpeakerArrangement* outputs, int32 numOuts) { + clear_bus_cache(); + // NOTE: Ardour passes a null pointer when `numIns` or `numOuts` is 0, so we // need to work around that return bridge.send_audio_processor_message( @@ -311,20 +319,11 @@ Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type, tresult PLUGIN_API Vst3PluginProxyImpl::setActive(TBool state) { // HACK: Even though we have implemented this cache specifically for REAPER, - // REAPER mixes up `IComponent::setActive` and - // `IAudioProcessor::setProcessing`. `IAudioProcessor::setProcessing` - // is called before setting up bus arrangements, so without this the - // cache would be filled with default data rather than the bus - // arrangement chosen by REAPER. So now our workaround to get - // acceptable performance in REAPER needs a workaround of its ownn. - // Great! - // TODO: We probably also need a reset on - // `IComponentHandler::restartComponent()` - if (state) { - processing_bus_cache.emplace(); - } else { - processing_bus_cache.reset(); - } + // REAPER doesn't use `IComponent::setProcessing` properly and calls + // it before doing setting up input and output busses. So now our + // workaround to get acceptable performance in REAPER needs a + // workaround of its ownn. Great! + clear_bus_cache(); return bridge.send_audio_processor_message( YaComponent::SetActive{.instance_id = instance_id(), .state = state}); diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.h b/src/plugin/bridges/vst3-impls/plugin-proxy.h index 795761c3..4acd0733 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.h +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.h @@ -54,6 +54,25 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { */ bool unregister_context_menu(size_t context_menu_id); + /** + * Clear the bus count and information cache. We need this cache for REAPER + * as it makes `num_inputs + num_outputs + 2` function calls to retrieve + * this information every single processing cycle. For plugins with a lot of + * outputs this really adds up. According to the VST3 workflow diagrams bus + * information cannot change anymore once `IAudioProcessor::setProcessing()` + * has been called, but REAPER doesn't quite follow the spec here and it + * will set bus arrangements and activate the plugin only after it's called + * `IAudioProcessor::setProcessing()`. Because of that we'll have to + * manually flush this cache when the stores information potentially becomes + * invalid. + * + * HACK: Once REAPER stops calling these functions, we should remove this + * caching layer ASAP as it can only cause issues + * + * @see processing_bus_cache + */ + void clear_bus_cache(); + // From `IAudioPresentationLatency` tresult PLUGIN_API setAudioPresentationLatencySamples(Steinberg::Vst::BusDirection dir, diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 6ec9820e..f6e46afd 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -85,9 +85,15 @@ Vst3PluginBridge::Vst3PluginBridge() }, [&](const YaComponentHandler::RestartComponent& request) -> YaComponentHandler::RestartComponent::Response { - return plugin_proxies.at(request.owner_instance_id) - .get() - .component_handler->restartComponent(request.flags); + Vst3PluginProxyImpl& proxy_object = + plugin_proxies.at(request.owner_instance_id).get(); + + // To err on the safe side, we'll just always clear out bus + // info cache whenever a plugin requests a restart + proxy_object.clear_bus_cache(); + + return proxy_object.component_handler->restartComponent( + request.flags); }, [&](const YaComponentHandler2::SetDirty& request) -> YaComponentHandler2::SetDirty::Response {