tldr: In JUCE-based VST3s with an internal preset browser, preset changes don’t update parameter values in the host unless you notify it manually — but doing so may trigger unwanted automation recording.
There are basically two ways the host can be notified that some parameters have changed:
- automation recordable : the parameter is explicitly modified by the user, for example by dragging a slider.
- not recorded by automation: the parameter is modified indirectly, for example the user switches presets in the internal preset browser of the plugin. In that case all parameters are modified, but you do not want the host to auto-create a thousand automation tracks if you have a thousand parameters. However you still want to ping the host in some way so that it updates its cached values of the parameter.
Why does this matter ? Consider Komplete Kontrol, or the JUCE AudioPluginHost. They both display the current value of parameters (for Komplete Kontrol: in its gui and on the Komplete keyboard screen, for AudioPluginHost: in the “show all parameters” window). Open any VST3 which has a preset browser. When you drag a slider inside the VST3, its value in the host changes accordingly. If you change preset in the vst, you expect the values in the host to change accordingly. This is only the case for JUCE plugins if you explicitey call AudioProcessorParameter::setValueNotifyingHost on all your params. Otherwise the values displayed by KK or AudioPluginHost are simply not the correct ones.
But now, if you call setValueNotifyingHost on preset changes, and load your plugin in a VST3 host such as Reason, and change presets while Reason is recording, it will create an automation track for ALL your parameters.
So there are situations where you want to inform the host that a parameter has changed, without telling it that the parameter is actively being edited by the user.
As far as I understand VST3 (which is not much , unfortunately), you have:
beginEdit / performEdit / endEdit for notifying the host of recordable automation events.
setParamNormalized to just inform the host of the param value change
Problem is JuceVST3EditController::paramChanged always calls both.
If I change it this way, so that performEdit is only called when inside a beginGesture / endGesture it is much better:
int gesture_cnt = 0;
void beginGesture (Vst::ParamID vstParamId)
{
if (! inSetState && MessageManager::getInstance()->isThisTheMessageThread()) {
beginEdit (vstParamId);
++gesture_cnt;
}
}
void endGesture (Vst::ParamID vstParamId)
{
if (! inSetState && MessageManager::getInstance()->isThisTheMessageThread()) {
endEdit (vstParamId);
--gesture_cnt;
}
}
void paramChanged (Steinberg::int32 parameterIndex, Vst::ParamID vstParamId, double newValue)
{
if (inParameterChangedCallback || inSetState)
return;
if (MessageManager::getInstance()->isThisTheMessageThread())
{
EditController::setParamNormalized (vstParamId, newValue);
if (gesture_cnt) { // notify the host of aa recordable param change only if there was a beginGesture before..
performEdit (vstParamId, newValue);
}
}
else
{
audioProcessor->setParameterValue (parameterIndex, (float) newValue);
}
}
But that is not a great hack. It would probably be better to have a way to call AudioProcessor::updateHostDisplay with some argument that would trigger a refresh of the param values seen by the host.
