VST3 parameter name changes don't work

There is a problem in the VST3 wrapper, that prevents parameter name changes to be propagated to the host. The VST parameter table is created in setupParameters, but they are never updated. Any plugin that changes their parameter names after load will not work properly with this.

2 Likes

I have temporarily fixed the problem by changing two files, that I should not touch:

juce_VST3_Wrapper.cpp overriding the getInfo methods in the Param class, which are defined in Vst::Parameter:

	const Steinberg::Vst::ParameterInfo& getInfo () const override
	{
		toString128 (const_cast<Param*>(this)->info.title, owner.getParameterName (paramIndex));
		toString128 (const_cast<Param*>(this)->info.shortTitle, owner.getParameterName (paramIndex, 8));
		toString128 (const_cast<Param*>(this)->info.units, owner.getParameterLabel (paramIndex));
		return info;
	}

	Steinberg::Vst::ParameterInfo& getInfo () override
	{
		toString128 (info.title, owner.getParameterName (paramIndex));
		toString128 (info.shortTitle, owner.getParameterName (paramIndex, 8));
		toString128 (info.units, owner.getParameterLabel (paramIndex));
		return info;
	}

and adding the information that parameter titles changed, when notifying the host about plugin changes.

        componentHandler->restartComponent (Vst::kLatencyChanged | Vst::kParamValuesChanged | Vst::kParamTitlesChanged);

and vstparameters.h making getInfo virtual, so I can override it

virtual const ParameterInfo& getInfo () const {return info;}
virtual ParameterInfo& getInfo () {return info;}

Steinberg might be so kind to update their SDK with this change and ROLI to consider this fix to be included in JUCE.

Thanks

Weā€™re not going to add anything that requires modifying the VST3 SDK. Whilst a copy of the SDK is now included inside the JUCE repo, JUCE users are also free to use their own. Anything that changes the interface is going to cause problems.

One way of achieving the same result without modifying the SDK would be to use ParameterInfo& getInfo() or write to the info member directly. The trouble with this is that there isnā€™t a mechanism in JUCE for indicating that the parameter data has changedā€¦

I discussed this Yvan Grabit and he confirmed that this change will make it into the VST3 SDK.

I am sure there are other ways to fix it, but this clearly is a problem in JUCE right now. Parameter name changes are not picked up by the JUCE VST3 Wrapper, like it does in the VST2 and AU wrappers. I consider this an issue that needs addressing, as name changes is the only way to update the parameter array exposed to the host. I could not find a way to fix it without changing JUCE and the VST3 SDK. So any fix, I did not see is more then welcome.

Hi,
Can this be added to the JUCE code now with an if define for the latest VST SDK? A lot of programmers appear to be discussing dynamic plugins, which implies the need for more dynamic parameters.
Thank you

1 Like

Hi Tom, would it be possible, to revisit this topic ? Apparently, Steinberg has updated the VST3 SDK so that this fix should be possible now.

Yep, itā€™s on our TODO list. However, the next few weeks are pretty busy for us as we gear up for the ADC conference, so no promises on any timelines.

May I ask again, if this fix can be considered for inclusion ?

1 Like

Yes, i am also highly interested in this. I found that i had to change the code of wkundrus a little, which made it work for me with VST3 and Reaper:

        const Steinberg::Vst::ParameterInfo& getInfo () const override
    {
        toString128 (const_cast<Param*>(this)->info.title, param.getName (128));
        toString128 (const_cast<Param*>(this)->info.shortTitle, param.getName (8));
        toString128 (const_cast<Param*>(this)->info.units, param.getLabel());
        return info;
    }
    
    Steinberg::Vst::ParameterInfo& getInfo () override
    {
        toString128 (info.title, param.getName (128));
        toString128 (info.shortTitle, param.getName (8));
        toString128 (info.units, param.getLabel());
        return info;
    }
1 Like

Iā€™ve stumbled into this problem as well. I see that JUCE has updated the VST3 SDK now, so I was able to implement this without any further hacks.

While I was at it, I also wanted to be able to change parameter group names dynamically, so I added that. This resulted in the only breaking change of the patch, unconstifying the pointers returned by AudioProcessorParameterGroup::getSubGroups.

With this, I can now have fully dynamic parameters in hosts that A) support updateHostDisplay() and B) hide parameters/groups with empty names. Tested with VST3 in Reaper (as well as under AU in Logic).

I also took the liberty of updating some comments which seemed unclear to me as I was working through this. What do you think, @ed95 and @t0m?

dyn-params.patch (13.3 KB)

1 Like

Actually, the above patch doesnā€™t build for AU because of something I overlooked. Iā€™ll fix it and repost tomorrow.

Edit: Yeah, I had to roll back the ā€˜non-const subgroupsā€™ changes, they didnā€™t compile for certain configurations. I discovered that you get already get non-const pointers to subgroups through the begin() and end() iterators of AudioProcessorParameterGroup. So this version of the patch contains NO breaking changes at all. Please consider for inclusion!

dyn-params.patch (10.6 KB)

1 Like

I am a bit upset that this severe issue does not gain any attention by Roli. Several people have successfully fixed the issue with the above patches. Why is this fix not included by now? Each time an update comes along, I need to reapply this patch.

All of the patches so far rely upon modifying const data via const_cast, which introduces undefined behaviour.

I think that a correct fix might require using a mechanism in JUCE for indicating that the parameter data has changed, then updating the info struct in the wrapper before the const version of getInfo is called.

The change is triggered by the owner of the API and the host ist notified about the change. I do not see the problem to be honest. So why is the data const ?

The info struct is const during the call to getInfo() const because of the signature of that method - a const method should not modify its own members.

The real question ist, why this does not get attention. Is it so unusual today that a plugin manages dynamic parameter lists ?

1 Like
3 Likes

Hello, iā€™m a brand new Juce user and i am trying to change the name & the range (in runtime) of my vst 3 parameters.
For this, i changed the juce_VST3_Wrapper as indicated by t0m on github
and added an include on my audioprocessor file.
#include <juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp>

But doing this i got plenty compilations errors like(C2872,C2065,ā€¦)

Do you have an hint of how i can update my parameters made available to the host?
Thanks for your help

Since this patch has been added I tried to update my plugins to report changed parameter names to the host for vst3. While this does work, there is a big issue with UpdateHostDisplay() and void audioProcessorChanged () that gets called by it in the wrapper. The problem is this line:

componentHandler->restartComponent (Vst::kLatencyChanged | Vst::kParamValuesChanged | Vst::kParamTitlesChanged);|

It means every time I change a parameter name, the host thinks latency changes and latency changes lead to audio ā€œglitchesā€ in many host. The line above is just too broad. It really should not sem kLatencyChanged unless the latency has changed and the same goes for the two other flags. In general the UpdateHostDisplay() is just a bad mechanism as it is too broad. IMHO it should be split into multiple calls or have a flag-mask to specify what parts of the host display should be updated. Itā€™s a similar story in AU and I think it comes from the old VST where there was just one call. The newer plugin standards would allow for more fine control here and it would be nice if JUCE could reflect that. At the very least the VST3 wrapper should avoid sending latencyChanged if it hasnā€™t changed. Iā€™ll prep a PR now.

EDIT: restartComponent() is probably a bad idea in general. Surely there must be a way in VST3 to change a parameter name without needing to tell the host to restart the whole plugin. From the VST3 docs:

/** Instructs host to restart the component. This should be called in the UI-Thread context!
**\param** flags is a combination of RestartFlags */
**virtual** tresult PLUGIN_API restartComponent (int32 flags) = 0;

Thereā€™s an IMPORTANT NOTE in the JUCE docs about audioProcessorChanged(ā€¦)

/** Called to indicate that something else in the plugin has changed, like its
    program, number of parameters, etc.

    IMPORTANT NOTE: This will be called synchronously, and many audio processors will
    call it during their audio callback. This means that not only has your handler code
    got to be completely thread-safe, but it's also got to be VERY fast, and avoid
    blocking. If you need to handle this event on your message thread, use this callback
    to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to later on the
    message thread.
*/
virtual void audioProcessorChanged (AudioProcessor* processor) = 0;

Currently for VST3, UpdateHostDisplay() calls audioProcessorChanged() in AudioProcessor which calls audioProcessorChanged() in the VST3 wrapper which then calls restartComponent(). These are all direct calls. This means currently itā€™s a bad idea to call UpdateHostDisplay() for VST3 while audio is running - which might be always.

I also looked into crafting a PR to fix this, but it would involve changes in AudioProcessorā€¦ so I wonā€™t even try to get it right. For now, Iā€™ll just disable reporting parameter changes for VST3 in my plugins.

2 Likes

I looked at the VST3 SDK a bit more. It does seem like restartComponent is indeed the only way to update parameter names and the VST3SDK comment is probably wrong about calling it on the UI Thread only. However the fact that the JUCE VST3 sends Vst::kLatencyChanged on every UpdateHostDisplay() still is a big problem. It should be more smart than that.

In my opinion, updateHostDisplay() shouldnā€™t even do anything about the latency as plugin latency is more than something the host displays - it does affect processing and many hosts arenā€™t able to properly deal with changes while audio is running, therefore Vst::kLatencyChanged should be used as little as possible. Some host cope fine with this, but only because they check themselves whether latency indeed did change after receiving the flag.