From c5b428dfe955927cb681853ccdc87cebd1736523 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 29 Apr 2024 18:07:17 +0100 Subject: [PATCH] VST3 Client: Add support for IParameterFinder interface This allows hosts such as Cubase and MultitrackStudio to locate parameters based on the current mouse position. Users must override and implement getControlParameterIndex in order for the parameter to be reported to the host. The DSPModulePluginDemo shows one possible strategy for implementing this function. --- examples/Plugins/DSPModulePluginDemo.h | 25 ++++++++++ .../juce_audio_plugin_client_VST3.cpp | 49 ++++++++++++++++++- .../processors/juce_AudioProcessorEditor.h | 7 ++- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/examples/Plugins/DSPModulePluginDemo.h b/examples/Plugins/DSPModulePluginDemo.h index 0d42665df8..f7a3a09a90 100644 --- a/examples/Plugins/DSPModulePluginDemo.h +++ b/examples/Plugins/DSPModulePluginDemo.h @@ -1620,6 +1620,15 @@ public: ladderControls); } + /* Called by VST3 and AAX hosts to determine which parameter is under the mouse. */ + int getControlParameterIndex (Component& comp) override + { + if (auto* parent = findParentComponentWithParamMenu (&comp)) + return parent->getParameterIndex(); + + return -1; + } + private: class ComponentWithParamMenu : public Component { @@ -1636,11 +1645,27 @@ private: .withMousePosition()); } + int getParameterIndex() const + { + return param.getParameterIndex(); + } + private: AudioProcessorEditor& editor; RangedAudioParameter& param; }; + static ComponentWithParamMenu* findParentComponentWithParamMenu (Component* c) + { + if (c == nullptr) + return nullptr; + + if (auto* derived = dynamic_cast (c)) + return derived; + + return findParentComponentWithParamMenu (c->getParentComponent()); + } + class AttachedSlider final : public ComponentWithParamMenu { public: diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp index b1d20058cd..523a212ad9 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp @@ -1740,7 +1740,8 @@ private: //============================================================================== class JuceVST3Editor final : public Vst::EditorView, - public Steinberg::IPlugViewContentScaleSupport, + public Vst::IParameterFinder, + public IPlugViewContentScaleSupport, private Timer { public: @@ -1759,7 +1760,10 @@ private: tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override { - const auto result = testFor (*this, targetIID, UniqueBase{}); + const auto result = testForMultiple (*this, + targetIID, + UniqueBase{}, + UniqueBase{}); if (result.isOk()) return result.extract (obj); @@ -2042,7 +2046,48 @@ private: #endif } + tresult PLUGIN_API findParameter (int32 xPos, int32 yPos, Vst::ParamID& resultTag) override + { + if (const auto paramId = findParameterImpl (xPos, yPos)) + { + resultTag = *paramId; + return kResultTrue; + } + + return kResultFalse; + } + private: + std::optional findParameterImpl (int32 xPos, int32 yPos) const + { + auto* wrapper = component.get(); + + if (wrapper == nullptr) + return {}; + + auto* componentAtPosition = wrapper->getComponentAt (xPos, yPos); + + if (componentAtPosition == nullptr) + return {}; + + auto* editor = wrapper->pluginEditor.get(); + + if (editor == nullptr) + return {}; + + const auto parameterIndex = editor->getControlParameterIndex (*componentAtPosition); + + if (parameterIndex < 0) + return {}; + + auto processor = owner->audioProcessor; + + if (processor == nullptr) + return {}; + + return processor->getVSTParamIDForIndex (parameterIndex); + } + void timerCallback() override { stopTimer(); diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h index 311a2c9fc2..7a3760ae06 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h @@ -94,7 +94,12 @@ public: If the given component represents a particular plugin parameter, then this method should return the index of that parameter. If not, it should return -1. - Currently only AAX plugins will call this, and implementing it is optional. + If not overridden, this will return -1 for all components. + + This function will be called by the host in AAX and VST3 plug-ins in order to map + screen locations to parameters. For example, in Steinberg hosts, this enables the + "AI Knob" functionality, which enables hardware to control the parameter currently + under the mouse. */ virtual int getControlParameterIndex (Component&);