Updating multiple 'linked' controls

Hi,

I’m trying to update multiple ‘linked’ controls so they show/have the same value when one of them is changed. I have 2 sliders that can be linked so both should be updated when the other one is dragged.
I’m not sure how to do this correctly in iPlug2. What I do now is check for param changes in OnParamChangeUI. Then I set the linked control param to the same value as the other one:

SetParameterValue(kParamSlider1, value);
refSlider1->SetDirty(false); // only update ui, don't push

This seems to almost work. :slight_smile: Only, when automating the parameter in StudioOne 4 / Win, the automation randomly turns off like when changing a control manually that is being automated by the host.

What is the correct way of doing this?

Thanks,
PJ

1 Like

in iPlug2 controls linked to the same parameter (with same paramIdx set in CTOR) should both get updated when one changes, without you having to do anything special.

Not sure that answers your question.

There is a reasonably complex setup in this test/example which might be worth looking at:

Thanks!

In my case, the option to link the 2 controls is optional. So it cannot set both controls to the same paramIdx in the CTOR because it depends on the ‘link’ control in the plugin.
I see that in the example there’s a similar case so I checked setting both params in Control->SetActionFunction. This works, but not for automation that is coming from the host.

Kind regards,
PJ

you can use IControl::SetParamIdx() to change the parameter that a control is linked to, including kNoParameter (-1)

after doing that you’d need to call IEditorDelegate::SendCurrentParamValuesFromDelegate(), to update the control values

1 Like

Thanks, I ended up using SetParamIdx and SendParameterValueFromDelegate to update the specific control. Seems to work fine now!

I’m transitioning from wdl-ol to iplug2 and I’m trying to do exactly what you described, but I’m having some trouble. It’d be really helpful if you could show that part of the code!

Sorry for the noobness

If anyone is as helpless as me and reads this, you can do it without the SetParamIdx, like this:

  case kLVol:
  mLVol = GetParam(kLVol)->Value() / 100.;
    if (mSync) {
      GetParam(kRVol)->Set(GetParam(kLVol)->Value());
      mRVol = mLVol;
      SendCurrentParamValuesFromDelegate();
    }

Where mSync defines wether knobs are synced or not. LVol is the parameter being altered and RVol is copying LVol.

1 Like

@olilarkin I’ve tried copying your MetaParamTest example with a slightly different concept.

Using an kActiveParam to drive which param a knob reads from.

And it stubbornly refuses to work. Here is what I’m trying to do:

    pGraphics->AttachControl(new IVKnobControl(eqControlsPanel.SubRectVertical(3, 0), kParamFreq1, "Freq", knobStyle, true, false, -150.f, 150.f, -150.f), 0, "EQ");
    pGraphics->AttachControl(new IVKnobControl(eqControlsPanel.SubRectVertical(3, 1), kParamGain1, "Gain", knobStyle, true, false, -150.f, 150.f, 0.f), 1, "EQ");
    pGraphics->AttachControl(new IVKnobControl(eqControlsPanel.SubRectVertical(3, 2), kParamQ1, "Q", knobStyle, true, false, -150.f, 150.f, -150.f), 2, "EQ");
    pGraphics->ForControlInGroup("EQ", [&](IControl* pControl)
    {
      pControl->SetActionFunction([&](IControl* pCaller)
      {
        const int activeBandId = GetParam(kActiveBand)->Int();
        const int baseParamId = pCaller->GetTag() + 1;
        const int newParamId = baseParamId + 5 * activeBandId;
        pCaller->SetParamIdx(newParamId);
        SendParameterValueFromDelegate(newParamId, GetParam(newParamId)->GetNormalized(), true);
#if 0
        SendCurrentParamValuesFromDelegate();
#endif
#if 0
        double x = GetParam(newParamId)->GetNormalized();
        SetParameterValue(newParamId, x);
        pCaller->SetValueFromDelegate(x);
#endif
      });
    });

You can see I’ve tried a few things, and in no case does changing the knob param using SetParamIdx actually change the knob and update properly. It becomes broken from that point and the knobs stop working properly.

What am I doing wrong?

I am usually using two things, param value and GUI display:

SetParameterValue(kSize, x);
if (GetUI()) GetUI()->GetControlWithTag(kCtrlSize)->SetValueFromDelegate(x);

not sure about conditional logic and math in your action function.

1 Like

I have one control called activeBandId which is an enum holding the currently selected band in an EQ GUI. When it changes I want a single frequency, Q and gain knob to switch to those associated values. But it doesn’t work. The knob, after calling SetParamIdx become irrevocable broken and stops updating. Ive tried all the calls I could find and gave up.

If you only change the knob value it works, but calling SetParamIdx to dynamically update what the knob points to doesn’t seem to.

So I instead had to do something I didn’t want to. I copied and modified the knob class to pass an initializer list of the activeBandId,freq,freq2,freq3,… etc. parameters.

Then in all the functions that call either GetValue or SetValue I read the paramId as an enum, then read the 1+activeBandId value to retrieve the correct frequency value. And that works.

But it would be a lot easier to just use the standard knob class and call SetParamIdx to change what parameter it points to.

But I just cannot get it to work no matter what I’ve tried.

The issue seems to be that the update of the band parameter can’t be detected in order to update the knobs and even if it could, the knobs become broken after calling SetParamIdx anyway.

Using action functions doesn’t work since the default knob can only track a single parameter and therefore can’t react to a different parameter.

I was thinking about updating the knobs in OnParamChangeUI but I’ve not tried that yet since putting it there feels wrong to me.

I found a solution I’m super happy with and it looks like this:

void TemplateProject::OnParamChangeUI(int paramIdx, EParamSource source)
{
  #if IPLUG_EDITOR
  if (auto pGraphics = GetUI())
  {
    if (paramIdx == kActiveBand)
    {
      const int activeBandId = GetParam(kActiveBand)->Int();
      IVKnobControl* knobFreq = (IVKnobControl*)pGraphics->GetControlWithTag(0);
      IVKnobControl* knobGain = (IVKnobControl*)pGraphics->GetControlWithTag(1);
      IVKnobControl* knobQ = (IVKnobControl*)pGraphics->GetControlWithTag(2);
      const int freqId = kParamFreq1 + 5 * activeBandId;
      const int gainId = kParamGain1 + 5 * activeBandId;
      const int qId = kParamQ1 + 5 * activeBandId;
      knobFreq->SetParamIdx(freqId);
      knobGain->SetParamIdx(gainId);
      knobQ->SetParamIdx(qId);
      SendParameterValueFromDelegate(freqId, GetParam(freqId)->GetNormalized(), true);
      SendParameterValueFromDelegate(gainId, GetParam(gainId)->GetNormalized(), true);
      SendParameterValueFromDelegate(qId, GetParam(qId)->GetNormalized(), true);
    }
  }
  #endif
}

This does precisely what I describe above, doesn’t require any custom knob implementation, and reliably updates precisely as I wanted it to. VERY COOL!

Nice, but I am a simple guy, I would just use.
pGraphics->HideControl(i, x);
to achieve the same functionality :slight_smile:

@JohnCrosby FYI this is what I’m using this for: How to handle non-DSP parameters that influence others