More fun with VST3 and Cubase/Nuendo


#1

I've had reports (verified here) that when sessions are restored, all of my plugins are set to minimum values--no matter how they were saved.  I'd already worked around the issue with beginParameterChangeGesture and endParameterChangeGesture, so this looked like something else.  I've spent several hours instrumenting my way through the problem, and here's what I think is going on.

I found that processParameterChanges in the VST3 wrapper (called from process) was setting all parameters to 0.0, even when they had been previously restored to their proper values at session load.  This happens the very first time that process is called.  It appears that the IParameterChanges structure has been initialized to all zeros and that process is called before the structure has been populated with the true values using getParameter(id).

I don't know whether this is a bug in juce (a missing init call or something) or a race condition in Cubase/Nuendo, but it happens every single time.  I have a workaround that I don't like very much.  It involves a new function in the wrapper:

  bool checkForLegitimateParameterChanges (Vst::IParameterChanges& paramChanges)
  {
    bool Legit = true;

    int ParameterCount = pluginInstance->getNumParameters();
    const Steinberg::int32 numParamsChanged = paramChanges.getParameterCount();

    // If these match, then it looks like it might be a startup situation where all
    // parameters are getting bashed.  Let's look for nonsense.

    // I'm interpreting nonsense as a set of all-zero values
    // If the param counts DON'T match, then it might be legit.
    if (numParamsChanged == (Steinberg::int32)ParameterCount)
    {
      Legit = false;
      for (Steinberg::int32 i = 0; i < numParamsChanged; ++i)
      {
        if (Vst::IParamValueQueue* paramQueue = paramChanges.getParameterData (i))
        {
          const Steinberg::int32 numPoints = paramQueue->getPointCount();

          Steinberg::int32 offsetSamples;
          double value = 0.0;

          if (paramQueue->getPoint (numPoints - 1,  offsetSamples, value) == kResultTrue)
          {
            // any value greater than zero will be used as a confirmation that the parameter
            // values have been fetched and this structure has been initted.
            // We'll bomb out early.
            if (0.0f < value)
            {
              Legit = true;
              break;
            }
          }
        }
      }
    }
    return Legit;
  }

 

The first lines of processParameterChanges then look like this:

    void processParameterChanges (Vst::IParameterChanges& paramChanges)
    {
      if (!checkForLegitimateParameterChanges(paramChanges))
        return;

This is based on the statistical unlikelihood of anyone setting all parameters to zero.  That's true enough for me, but I wouldn't apply it as a general rule.  I've thought about adding a method to the audioProcessor class that would verify that parameter values had been fetched before allowing them to be written from the wrapper.  If there isn't a better solution, I'll go with it.  But at least what I have here is sufficient to prove the basic point.  Is there some fetch value function that needs to be implemented in juce?


#2

I did not look into this yet,  but I have observed the same (plugins set to minimum values on project restore) and would love to see that fixed...