Is this a bug? Or I don’t understand something

Hi All!
I found one mismatch in the custom visual component.
This is about getting the normalized value of the parameter associated with the component.
You can get the value literally like this:

`GetValue();`

However, this method is also capable of obtaining the normalized value of the associated parameter:

`GetParam()->GetNormalized();`

When the component is running, for some unknown reason, the values obtained by these methods are different.
I tried to check it like this:

`if (GetParam())
    assert(GetParam()->GetNormalized() == GetValue());`

What is causing the exception.
It turned out that the GetValue() method returns an incorrect value. Or am I misunderstanding something?

l think the method to get the param’s value is just Value() not GetValue()? :thinking:

Maybe I’m using an outdated version of the framework?
However, in my version, for the IControl class there is such a method declaration:

/** Get the control’s value
* @return Value of the control, normalized in the range 0-1
* @param valIdx The index of the value to set, which should be between 0 and NVals() */
double GetValue(int valIdx = 0) const;

I also noticed that the parameter set via SetValue () is looped for each preset when the plugin is loaded into the host. And therefore, the value of the last preset in the list is loaded into the control, and in the parameters - the required value saved by the host, but for some reason it is not loaded into the control. Maybe it should be forced in some way?

As it turned out, in the IPlugAPIBase::SendParameterValueFromAPI method, parameters from the host are written to a queue, which is then sent to all controls by timer. Since FL Studio sequentially poisons all the preset parameters into the plug-in, in the end they are delayed by the timer, and already knock down the necessary control settings. I tried sending parameters directly, without a timer, by changing the implementation of the IPlugAPIBase :: SendParameterValueFromAPI method like this:

void IPlugAPIBase::SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
{
  //TODO: Can we assume that no host is stupid enough to try and set parameters on multiple threads at the same time?
  // If that is the case then we need a MPSPC queue not SPSC
  if (normalized)
    value = GetParam(paramIdx)->FromNormalized(value);
  SendParameterValueFromDelegate(paramIdx, value, false); 
  //mParamChangeFromProcessor.Push(ParamTuple { paramIdx, value } );
}

After this fix, the issues went away. I do not know if this is a good solution, but in the classic version I did not find buffering of incoming data to the queue and delayed sending them to the plugin by timer.

Could you describe how I can recreate this issue in FLStudio with e.g. IPlugEffect? I don’t quite understand

An example that would could use to recreate would be really helpful here. You aren’t necessarily guaranteed when controls and parameters will be aligned in terms of value, but once all changes are done you should eventually be aligned - if that’s not happening then the behaviour is incorrect.

So what we need to understand here is whether there’s an issue or whether it’s to do with your expectations about WHEN things are done that needs to be explained better. In iplug1 things worked differently, raising the possibility of some other issues with threading that are being dealt with in what we hope is a better way in iplug2, but we do need to makes sure it is implemented correctly.

The example “IPlugEffect” works great. However, if you add several different presets to it, then the problem described above will arise. Moreover, the problem is seen only in the VST2 version. As I wrote above, it is solved as follows:

void IPlugAPIBase::SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
{
  //TODO: Can we assume that no host is stupid enough to try and set parameters on multiple threads at the same time?
  // If that is the case then we need a MPSPC queue not SPSC
  if (normalized)
    value = GetParam(paramIdx)->FromNormalized(value);
  SendParameterValueFromDelegate(paramIdx, value, false); 
  //mParamChangeFromProcessor.Push(ParamTuple { paramIdx, value } );
}

I just added 3 presets to IPlugEffect and tried the VST2 in FLStudio. Everything seems to work as expected.

Try this:
IPlugEffect.h

#pragma once

#include "IPlug_include_in_plug_hdr.h"

const int kNumPrograms = 10;

enum EParams
{
  kGain = 0,
  kVol,
  kNumParams
};

using namespace iplug;
using namespace igraphics;

class IPlugEffect final : public Plugin
{
public:
  IPlugEffect(const InstanceInfo& info);

#if IPLUG_DSP // http://bit.ly/2S64BDd
  void ProcessBlock(sample** inputs, sample** outputs, int nFrames) override;
#endif
};

IPlugEffect.cpp

#include "IPlugEffect.h"
#include "IPlug_include_in_plug_src.h"
#include "IControls.h"

IPlugEffect::IPlugEffect(const InstanceInfo& info)
: Plugin(info, MakeConfig(kNumParams, kNumPrograms))
{
  GetParam(kGain)->InitDouble("Gain", 0., 0., 100.0, 0.01, "%");
  GetParam(kVol)->InitDouble("Volume", 50., 0., 100.0, 0.01, "%");

  MakeDefaultPreset("Default");
  MakePreset("1", 20.0, 34.62720);
  MakePreset("2", 30.0, 4.62720);
  MakePreset("3", 40.0, 24.62720);
  MakePreset("4", 50.0, 14.62720);
  MakePreset("5", 60.0, 55.62720);
  MakePreset("6", 70.0, 27.62720);
  MakePreset("7", 76.0, 27.62720);
  MakePreset("8", 80.0, 85.62720);
  MakePreset("9", 88.0, 56.62720);
  MakePreset("10", 90.0, 77.62720);


#if IPLUG_EDITOR // http://bit.ly/2S64BDd
  mMakeGraphicsFunc = [&]() {
    return MakeGraphics(*this, PLUG_WIDTH, PLUG_HEIGHT, PLUG_FPS, 1.);
  };
  
  mLayoutFunc = [&](IGraphics* pGraphics) {
    pGraphics->AttachCornerResizer(EUIResizerMode::Scale, false);
    pGraphics->AttachPanelBackground(COLOR_GRAY);
    pGraphics->LoadFont("Roboto-Regular", ROBOTO_FN);

    const IRECT b = pGraphics->GetBounds();
    pGraphics->AttachControl(new ITextControl(b.GetMidVPadded(50), "Hello iPlug 2!", IText(50)));
    pGraphics->AttachControl(new IVKnobControl(b.GetCentredInside(100).GetVShifted(-100), kGain));
    pGraphics->AttachControl(new IVKnobControl(b.GetCentredInside(100).GetVShifted(100), kVol));
  };
#endif
}

#if IPLUG_DSP
void IPlugEffect::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
  const double gain = GetParam(kGain)->Value() / 100.;
  const int nChans = NOutChansConnected();
  
  for (int s = 0; s < nFrames; s++) {
    for (int c = 0; c < nChans; c++) {
      outputs[c][s] = inputs[c][s] * gain;
    }
  }
}
#endif

When testing, bring the second knob to 0% and save the file. When loading the file, it can be seen that the risk handles are not in the zero position.

bug6

OK I see now thanks for the example

My solution to this problem:

   void IPlugAPIBase::SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
    {
      //TODO: Can we assume that no host is stupid enough to try and set parameters on multiple threads at the same time?
      // If that is the case then we need a MPSPC queue not SPSC
      if (normalized)
        value = GetParam(paramIdx)->FromNormalized(value);
    #ifdef VST2_API
      SendParameterValueFromDelegate(paramIdx, value, false); 
    #else
      mParamChangeFromProcessor.Push(ParamTuple { paramIdx, value } );
    #endif
    }