| @@ -231,6 +231,11 @@ public: | |||||
| return getParamForVSTParamID (bypassParamID); | return getParamForVSTParamID (bypassParamID); | ||||
| } | } | ||||
| AudioProcessorParameter* getProgramParameter() const noexcept | |||||
| { | |||||
| return getParamForVSTParamID (JuceAudioProcessor::paramPreset); | |||||
| } | |||||
| static Vst::UnitID getUnitID (const AudioProcessorParameterGroup* group) | static Vst::UnitID getUnitID (const AudioProcessorParameterGroup* group) | ||||
| { | { | ||||
| if (group == nullptr || group->getParent() == nullptr) | if (group == nullptr || group->getParent() == nullptr) | ||||
| @@ -336,6 +341,20 @@ private: | |||||
| vstParamIDs.add (vstParamID); | vstParamIDs.add (vstParamID); | ||||
| paramMap.set (static_cast<int32> (vstParamID), juceParam); | paramMap.set (static_cast<int32> (vstParamID), juceParam); | ||||
| } | } | ||||
| auto numPrograms = audioProcessor->getNumPrograms(); | |||||
| if (numPrograms > 1) | |||||
| { | |||||
| ownedProgramParameter = std::make_unique<AudioParameterInt> ("juceProgramParameter", "Program", | |||||
| 0, numPrograms - 1, | |||||
| audioProcessor->getCurrentProgram()); | |||||
| juceParameters.params.add (ownedProgramParameter.get()); | |||||
| vstParamIDs.add (JuceAudioProcessor::paramPreset); | |||||
| paramMap.set (static_cast<int32> (JuceAudioProcessor::paramPreset), ownedProgramParameter.get()); | |||||
| } | |||||
| } | } | ||||
| Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param) | Vst::ParamID generateVSTParamIDForParam (AudioProcessorParameter* param) | ||||
| @@ -363,7 +382,7 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| LegacyAudioParametersWrapper juceParameters; | LegacyAudioParametersWrapper juceParameters; | ||||
| HashMap<int32, AudioProcessorParameter*> paramMap; | HashMap<int32, AudioProcessorParameter*> paramMap; | ||||
| std::unique_ptr<AudioProcessorParameter> ownedBypassParameter; | |||||
| std::unique_ptr<AudioProcessorParameter> ownedBypassParameter, ownedProgramParameter; | |||||
| Array<const AudioProcessorParameterGroup*> parameterGroups; | Array<const AudioProcessorParameterGroup*> parameterGroups; | ||||
| JuceAudioProcessor() = delete; | JuceAudioProcessor() = delete; | ||||
| @@ -379,8 +398,7 @@ class JuceVST3EditController : public Vst::EditController, | |||||
| public Vst::IMidiMapping, | public Vst::IMidiMapping, | ||||
| public Vst::IUnitInfo, | public Vst::IUnitInfo, | ||||
| public Vst::ChannelContext::IInfoListener, | public Vst::ChannelContext::IInfoListener, | ||||
| public AudioProcessorListener, | |||||
| private AudioProcessorParameter::Listener | |||||
| public AudioProcessorListener | |||||
| { | { | ||||
| public: | public: | ||||
| JuceVST3EditController (Vst::IHostApplication* host) | JuceVST3EditController (Vst::IHostApplication* host) | ||||
| @@ -594,12 +612,16 @@ public: | |||||
| bool setNormalized (Vst::ParamValue v) override | bool setNormalized (Vst::ParamValue v) override | ||||
| { | { | ||||
| if (! isPositiveAndBelow ((int) toPlain (v), owner.getNumPrograms()) | |||||
| || v == valueNormalized) | |||||
| auto programValue = roundToInt (toPlain (v)); | |||||
| if (! isPositiveAndBelow (programValue, owner.getNumPrograms()) | |||||
| || programValue == owner.getCurrentProgram()) | |||||
| return false; | return false; | ||||
| valueNormalized = v; | valueNormalized = v; | ||||
| owner.setCurrentProgram (programValue); | |||||
| changed(); | changed(); | ||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -678,17 +700,8 @@ public: | |||||
| tresult PLUGIN_API setComponentState (IBStream* stream) override | tresult PLUGIN_API setComponentState (IBStream* stream) override | ||||
| { | { | ||||
| // Cubase and Nuendo need to inform the host of the current parameter values | // Cubase and Nuendo need to inform the host of the current parameter values | ||||
| if (auto* pluginInstance = getPluginInstance()) | |||||
| { | |||||
| for (auto vstParamId : audioProcessor->vstParamIDs) | |||||
| setParamNormalized (vstParamId, audioProcessor->getParamForVSTParamID (vstParamId)->getValue()); | |||||
| auto numPrograms = pluginInstance->getNumPrograms(); | |||||
| if (numPrograms > 1) | |||||
| setParamNormalized (JuceAudioProcessor::paramPreset, static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram()) | |||||
| / static_cast<Vst::ParamValue> (numPrograms - 1)); | |||||
| } | |||||
| for (auto vstParamId : audioProcessor->vstParamIDs) | |||||
| setParamNormalized (vstParamId, audioProcessor->getParamForVSTParamID (vstParamId)->getValue()); | |||||
| if (auto* handler = getComponentHandler()) | if (auto* handler = getComponentHandler()) | ||||
| handler->restartComponent (Vst::kParamValuesChanged); | handler->restartComponent (Vst::kParamValuesChanged); | ||||
| @@ -906,7 +919,7 @@ public: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void paramChanged (Vst::ParamID vstParamId, float newValue) | |||||
| void paramChanged (Vst::ParamID vstParamId, double newValue) | |||||
| { | { | ||||
| if (inParameterChangedCallback.get()) | if (inParameterChangedCallback.get()) | ||||
| { | { | ||||
| @@ -915,8 +928,8 @@ public: | |||||
| } | } | ||||
| // NB: Cubase has problems if performEdit is called without setParamNormalized | // NB: Cubase has problems if performEdit is called without setParamNormalized | ||||
| EditController::setParamNormalized (vstParamId, (double) newValue); | |||||
| performEdit (vstParamId, (double) newValue); | |||||
| EditController::setParamNormalized (vstParamId, newValue); | |||||
| performEdit (vstParamId, newValue); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -939,17 +952,17 @@ public: | |||||
| if (auto* pluginInstance = getPluginInstance()) | if (auto* pluginInstance = getPluginInstance()) | ||||
| { | { | ||||
| auto numPrograms = pluginInstance->getNumPrograms(); | |||||
| if (numPrograms > 1) | |||||
| if (audioProcessor->getProgramParameter() != nullptr) | |||||
| { | { | ||||
| auto paramValue = static_cast<Vst::ParamValue> (pluginInstance->getCurrentProgram()) | |||||
| / static_cast<Vst::ParamValue> (numPrograms - 1); | |||||
| auto currentProgram = pluginInstance->getCurrentProgram(); | |||||
| auto paramValue = roundToInt (EditController::normalizedParamToPlain (JuceAudioProcessor::paramPreset, | |||||
| EditController::getParamNormalized (JuceAudioProcessor::paramPreset))); | |||||
| if (paramValue != EditController::getParamNormalized (JuceAudioProcessor::paramPreset)) | |||||
| if (currentProgram != paramValue) | |||||
| { | { | ||||
| beginEdit (JuceAudioProcessor::paramPreset); | beginEdit (JuceAudioProcessor::paramPreset); | ||||
| paramChanged (JuceAudioProcessor::paramPreset, (float) paramValue); | |||||
| paramChanged (JuceAudioProcessor::paramPreset, | |||||
| EditController::plainParamToNormalized (JuceAudioProcessor::paramPreset, currentProgram)); | |||||
| endEdit (JuceAudioProcessor::paramPreset); | endEdit (JuceAudioProcessor::paramPreset); | ||||
| flags |= Vst::kParamValuesChanged; | flags |= Vst::kParamValuesChanged; | ||||
| @@ -969,19 +982,6 @@ public: | |||||
| componentHandler->restartComponent (flags); | componentHandler->restartComponent (flags); | ||||
| } | } | ||||
| void parameterValueChanged (int, float newValue) override | |||||
| { | |||||
| // this can only come from the bypass parameter | |||||
| paramChanged (audioProcessor->bypassParamID, newValue); | |||||
| } | |||||
| void parameterGestureChanged (int, bool gestureIsStarting) override | |||||
| { | |||||
| // this can only come from the bypass parameter | |||||
| if (gestureIsStarting) beginEdit (audioProcessor->bypassParamID); | |||||
| else endEdit (audioProcessor->bypassParamID); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| AudioProcessor* getPluginInstance() const noexcept | AudioProcessor* getPluginInstance() const noexcept | ||||
| { | { | ||||
| @@ -1008,6 +1008,37 @@ private: | |||||
| MidiController parameterToMidiController[(int) numMIDIChannels * (int) Vst::kCountCtrlNumber]; | MidiController parameterToMidiController[(int) numMIDIChannels * (int) Vst::kCountCtrlNumber]; | ||||
| Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; | Vst::ParamID midiControllerToParameter[numMIDIChannels][Vst::kCountCtrlNumber]; | ||||
| //============================================================================== | |||||
| struct OwnedParameterListener : public AudioProcessorParameter::Listener | |||||
| { | |||||
| OwnedParameterListener (JuceVST3EditController& editController, | |||||
| AudioProcessorParameter& juceParameter, | |||||
| Vst::ParamID paramID) | |||||
| : owner (editController), | |||||
| vstParamID (paramID) | |||||
| { | |||||
| juceParameter.addListener (this); | |||||
| } | |||||
| void parameterValueChanged (int, float newValue) override | |||||
| { | |||||
| owner.paramChanged (vstParamID, newValue); | |||||
| } | |||||
| void parameterGestureChanged (int, bool gestureIsStarting) override | |||||
| { | |||||
| if (gestureIsStarting) | |||||
| owner.beginEdit (vstParamID); | |||||
| else | |||||
| owner.endEdit (vstParamID); | |||||
| } | |||||
| JuceVST3EditController& owner; | |||||
| Vst::ParamID vstParamID; | |||||
| }; | |||||
| std::vector<std::unique_ptr<OwnedParameterListener>> ownedParameterListeners; | |||||
| //============================================================================== | //============================================================================== | ||||
| std::atomic<bool> vst3IsPlaying { false }, | std::atomic<bool> vst3IsPlaying { false }, | ||||
| inSetupProcessing { false }; | inSetupProcessing { false }; | ||||
| @@ -1024,10 +1055,11 @@ private: | |||||
| { | { | ||||
| pluginInstance->addListener (this); | pluginInstance->addListener (this); | ||||
| // as the bypass is not part of the regular parameters | |||||
| // we need to listen for it explicitly | |||||
| // as the bypass is not part of the regular parameters we need to listen for it explicitly | |||||
| if (! audioProcessor->bypassIsRegularParameter) | if (! audioProcessor->bypassIsRegularParameter) | ||||
| audioProcessor->getBypassParameter()->addListener (this); | |||||
| ownedParameterListeners.push_back (std::make_unique<OwnedParameterListener> (*this, | |||||
| *audioProcessor->getBypassParameter(), | |||||
| audioProcessor->bypassParamID)); | |||||
| if (parameters.getParameterCount() <= 0) | if (parameters.getParameterCount() <= 0) | ||||
| { | { | ||||
| @@ -1036,6 +1068,10 @@ private: | |||||
| for (int i = 0; i < n; ++i) | for (int i = 0; i < n; ++i) | ||||
| { | { | ||||
| auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); | auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); | ||||
| if (vstParamID == JuceAudioProcessor::paramPreset) | |||||
| continue; | |||||
| auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); | auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); | ||||
| auto* parameterGroup = pluginInstance->getParameterTree().getGroupsForParameter (juceParam).getLast(); | auto* parameterGroup = pluginInstance->getParameterTree().getGroupsForParameter (juceParam).getLast(); | ||||
| auto unitID = JuceAudioProcessor::getUnitID (parameterGroup); | auto unitID = JuceAudioProcessor::getUnitID (parameterGroup); | ||||
| @@ -1044,8 +1080,14 @@ private: | |||||
| (vstParamID == audioProcessor->bypassParamID))); | (vstParamID == audioProcessor->bypassParamID))); | ||||
| } | } | ||||
| if (pluginInstance->getNumPrograms() > 1) | |||||
| if (auto* programParam = audioProcessor->getProgramParameter()) | |||||
| { | |||||
| ownedParameterListeners.push_back (std::make_unique<OwnedParameterListener> (*this, | |||||
| *programParam, | |||||
| JuceAudioProcessor::paramPreset)); | |||||
| parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); | parameters.addParameter (new ProgramChangeParameter (*pluginInstance)); | ||||
| } | |||||
| } | } | ||||
| #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | ||||
| @@ -2580,20 +2622,11 @@ public: | |||||
| { | { | ||||
| auto vstParamID = paramQueue->getParameterId(); | auto vstParamID = paramQueue->getParameterId(); | ||||
| if (vstParamID == JuceAudioProcessor::paramPreset) | |||||
| { | |||||
| auto numPrograms = pluginInstance->getNumPrograms(); | |||||
| auto programValue = roundToInt (value * (jmax (0, numPrograms - 1))); | |||||
| if (numPrograms > 1 && isPositiveAndBelow (programValue, numPrograms) | |||||
| && programValue != pluginInstance->getCurrentProgram()) | |||||
| pluginInstance->setCurrentProgram (programValue); | |||||
| } | |||||
| #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | ||||
| else if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||||
| if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||||
| addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value); | addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value); | ||||
| #endif | |||||
| else | else | ||||
| #endif | |||||
| { | { | ||||
| auto floatValue = static_cast<float> (value); | auto floatValue = static_cast<float> (value); | ||||