Automation issue in Cubase/Nuendo

I am experiencing an issue with automation in Cubase/Nuendo. It is very similar to another issue that has been fixed so I want to make sure it is understood that this is a different problem. The symptom is that when recording automation in Cubase/Nuendo, the parameter value returns to zero when I release the mouse. The "correct" behavior is for the the parameter to return to the previous value, just as it was before I start moving the slider to record the automation. This issue has been reported by several other users and had been fixed here.

https://github.com/soundradix/JUCE/commit/2e9e66cbc8c65e889be5232ffae83c0ca78f9c7e

I have brought in this change and it mostly works. However, it appears that when a plugin is initially loaded, there is no way to inform the host of the initial parameter values, so if I try to record automation immediately after loading a plugin, I still see the errant behavior. Basically, the host doesn't know the previous value because it hasn't been reported. The behavior returns to normal after the plugin notifies the host of its value, which occurs after any of the following three events:

  1. The user modifies the value of the plugin
  2. The session is closed and re-opened
  3. The user loads a preset into the plugin

I tried to fix this by calling "setValueNotifyingHost" in the constructor of my AudioProcessor-derived class, right after the parameters are added via the "addParameter" method. However, this does not work either because it is too early in the initialization sequence and the host hasn't yet been added to the list of audio processor "listeners". So under this specific condition, it appears that the plugin doesn't get a chance to report its parameter values to the host before automation begins.

 

Just a hunch - not tested at all - but what if you tried your fix in AudioProcessor::PrepareToPlay()?

 

That might work. I'll give it a try and let you know.

Thanks!

Just tried it and it works perfectly. No more automation problems! Thanks a bunch!

Hi,

I've encountered the same issue. It looks like JUCE's VST3 wrapper has to initialize automation values for all parameters, at least in Cubase. I've noticed this when recording automation for a parameter while a track is playing; after stopping and seeking back to the beginning of the track, the parameter value will be reset to its minimum value.

I've also worked around this by calling setParameterNotifyingHost() for all parameters my plug-in's prepareToPlay() method, but I guess this fix (or similar) should go into the VST3 wrapper's preparePlugin() method.

JUCE's VST2 wrapper doesn't seem to have this issue (in Cubase at least).

Cheers,

Dan

+1

Thank you for reporting this bug. I found the following fix to be more reliabe. Simply add the following function to JuceVST3EditController in modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp line 233:

tresult PLUGIN_API setComponentState (IBStream*) override
{
    // Cubase and Nuendo need to inform the host of the current parameter values
    if (AudioProcessor* const pluginInstance = getPluginInstance())
    {
       for (int i = 0; i < pluginInstance->getNumParameters(); ++i)
           setParamNormalized ((Vst::ParamID) i, (double) pluginInstance->getParameter (i));
    }

    return kResultFalse;
}

Please tell me if this works for you and we will include the above fix.

Seems to work fine. I''ve removed my "PrepareToPlay" code and will be doing some more testing.

Seems to work well here as well.

Thanks Fabian!

OK! This is on latest tip now!

I've not the documentation in front of me, but it seems odd to return kResultFalse instead of kResultTrue. Did you mean to return kResultTrue within the if-block?

Also, note that this LOC in the VST3 host code will assume the call failed because you return kResultFalse:

https://github.com/julianstorer/JUCE/blob/master/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp#L2320

Yes, thanks for spotting, kResultFalse is definietly wrong, but I believe kResultTrue is also not correct. My intention was to write kNotImplemented which I think is the correct thing to do here. See for example the default implementation of setComponentState in the base class (vsteditcontroller.cpp) which also returns kNotImplemented, i.e. prior to my recent change, JUCE would effectively return kNotImplemented by not overriding setComponentState.

I basically use the setComponentState function as a convenient point in the plug-in initialization process to init the parameters but tell the host that basically setComponentState is not impleneted, i.e. the host may safely assume that the supplied stream wasn't used to restore the state of the plug-in and the host can use it's own restore code. Maybe calling the base class instead of returning kNotImplemented or an extra comment would make this clearer if someone happens to stumble over this part of the code.

I've tried many other positions of placing the parameter initialization code, but all other positions broke something else (especially if your plug-in is using presets/programs). This is the only place where it seems to work. I've been using the current solution for years in a seperate, not JUCE-based project and it seems to work there, but, of course, I would be happy for suggestions of a less whacky workaround.

Hi,

We've done some tests so far about automation behavior and here are our results from VST3 Hosts:

- Cubase / Nuendo (We've tested mostly Cubase 8.0.10): Automation seems to be working as expected.

- Cakewalk SONAR 21.00.01: Automation works ok.

- Presonus Studio One 2.6.5: Automation works partial.  UI doesn't update upon playing back. (no redraw callback)
* note: tested against other plug-ins and many of also other manufacturers sturggle to work as internal effects.

- Tracktion v5.4.2x32: Automation writes ok. UI doesn't update upon playing back.

- Samplitude X2: Automation seems to be broken.
* note: tested against HOFA 4U Meter Fader & MS-Pan v1.1.0. seems to be writing automation correctly. UI doesn't update upon playing back.

 

 

We (HOFA) are also using JUCE but have our own implementation for VST3 wrapping. I had a quick look about the differences between our wrapper and JUCE’s wrapper. They are quite different (we inherit from SingleComponentEffect) but I noticed that JUCE doesn’t have setParamNormalized() implemented. Normally that should work, as the default implementation seems to change the parameter. Nonetheless, after adding the following code below audioProcessorParameterChanged(), it is at least possible to read automation in Samplitude with the JUCE demo plugin. The automation needs to be written using Samplitude.

tresult PLUGIN_API setParamNormalized (Steinberg::uint32 tag, double value) { audioProcessor->get()->setParameter((int) tag, (float) value); return EditController::setParamNormalized (tag, value); }

It still isn’t possible to write automation using the plugin’s controls and I’m neither sure how to fix this nor why reading automation works with the above code (didn’t dig very deep). Hopefully this helps getting it to work.

Chris