| @@ -231,6 +231,11 @@ public: | |||
| return getParamForVSTParamID (bypassParamID); | |||
| } | |||
| AudioProcessorParameter* getProgramParameter() const noexcept | |||
| { | |||
| return getParamForVSTParamID (JuceAudioProcessor::paramPreset); | |||
| } | |||
| static Vst::UnitID getUnitID (const AudioProcessorParameterGroup* group) | |||
| { | |||
| if (group == nullptr || group->getParent() == nullptr) | |||
| @@ -336,6 +341,20 @@ private: | |||
| vstParamIDs.add (vstParamID); | |||
| 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) | |||
| @@ -363,7 +382,7 @@ private: | |||
| //============================================================================== | |||
| LegacyAudioParametersWrapper juceParameters; | |||
| HashMap<int32, AudioProcessorParameter*> paramMap; | |||
| std::unique_ptr<AudioProcessorParameter> ownedBypassParameter; | |||
| std::unique_ptr<AudioProcessorParameter> ownedBypassParameter, ownedProgramParameter; | |||
| Array<const AudioProcessorParameterGroup*> parameterGroups; | |||
| JuceAudioProcessor() = delete; | |||
| @@ -379,8 +398,7 @@ class JuceVST3EditController : public Vst::EditController, | |||
| public Vst::IMidiMapping, | |||
| public Vst::IUnitInfo, | |||
| public Vst::ChannelContext::IInfoListener, | |||
| public AudioProcessorListener, | |||
| private AudioProcessorParameter::Listener | |||
| public AudioProcessorListener | |||
| { | |||
| public: | |||
| JuceVST3EditController (Vst::IHostApplication* host) | |||
| @@ -594,12 +612,16 @@ public: | |||
| 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; | |||
| valueNormalized = v; | |||
| owner.setCurrentProgram (programValue); | |||
| changed(); | |||
| return true; | |||
| } | |||
| @@ -678,17 +700,8 @@ public: | |||
| tresult PLUGIN_API setComponentState (IBStream* stream) override | |||
| { | |||
| // 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()) | |||
| 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()) | |||
| { | |||
| @@ -915,8 +928,8 @@ public: | |||
| } | |||
| // 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()) | |||
| { | |||
| 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); | |||
| paramChanged (JuceAudioProcessor::paramPreset, (float) paramValue); | |||
| paramChanged (JuceAudioProcessor::paramPreset, | |||
| EditController::plainParamToNormalized (JuceAudioProcessor::paramPreset, currentProgram)); | |||
| endEdit (JuceAudioProcessor::paramPreset); | |||
| flags |= Vst::kParamValuesChanged; | |||
| @@ -969,19 +982,6 @@ public: | |||
| 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 | |||
| { | |||
| @@ -1008,6 +1008,37 @@ private: | |||
| MidiController parameterToMidiController[(int) numMIDIChannels * (int) 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 }, | |||
| inSetupProcessing { false }; | |||
| @@ -1024,10 +1055,11 @@ private: | |||
| { | |||
| 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) | |||
| audioProcessor->getBypassParameter()->addListener (this); | |||
| ownedParameterListeners.push_back (std::make_unique<OwnedParameterListener> (*this, | |||
| *audioProcessor->getBypassParameter(), | |||
| audioProcessor->bypassParamID)); | |||
| if (parameters.getParameterCount() <= 0) | |||
| { | |||
| @@ -1036,6 +1068,10 @@ private: | |||
| for (int i = 0; i < n; ++i) | |||
| { | |||
| auto vstParamID = audioProcessor->getVSTParamIDForIndex (i); | |||
| if (vstParamID == JuceAudioProcessor::paramPreset) | |||
| continue; | |||
| auto* juceParam = audioProcessor->getParamForVSTParamID (vstParamID); | |||
| auto* parameterGroup = pluginInstance->getParameterTree().getGroupsForParameter (juceParam).getLast(); | |||
| auto unitID = JuceAudioProcessor::getUnitID (parameterGroup); | |||
| @@ -1044,8 +1080,14 @@ private: | |||
| (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)); | |||
| } | |||
| } | |||
| #if JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS | |||
| @@ -2580,20 +2622,11 @@ public: | |||
| { | |||
| 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 | |||
| else if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||
| if (juceVST3EditController != nullptr && juceVST3EditController->isMidiControllerParamID (vstParamID)) | |||
| addParameterChangeToMidiBuffer (offsetSamples, vstParamID, value); | |||
| #endif | |||
| else | |||
| #endif | |||
| { | |||
| auto floatValue = static_cast<float> (value); | |||