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&);