Control (View) with multiple parameters?

A user interface toolkit mainly for audio plug-ins (VST, AudioUnit, etc).
Post Reply
thirty
Posts: 9
Joined: Tue Feb 06, 2018 9:38 am

Control (View) with multiple parameters?

Post by thirty »

Let's say I want to create a ADSR visualization control which controls (read&write) 4 parameters - kAttack, kDecay etc.
How to do it as clean as possible since default CControls supports one tag?

Arne Scheffler
Posts: 414
Joined: Mon Jun 20, 2016 7:53 am

Re: Control (View) with multiple parameters?

Post by Arne Scheffler »

You cannot use a CControl for a this kind of view. You have to use a CView as base and write the code which handles the multiple parameters yourself.

Cheers,
Arne

bb20
Posts: 4
Joined: Fri Sep 18, 2020 2:24 pm

Re: Control (View) with multiple parameters?

Post by bb20 »

I am stuck on the same thing: a custom element that can control two parameters.

I derive a custom view class MyView from CView. In the construtor I pass a pointer to the VST3Editor:

Code: Select all

class MyView : public CView
{
public:
     AAView (const CRect& size, Steinberg::Vst::EditController* listener = nullptr);
     // ...
private:
     Steinberg::Vst::EditController* editController;
     // ...
}
In the UiDescriptionEditor I place a CView and set the custom-view-name to "MyView".
This triggers a call of Controller::createCustomView() and I can construct MyView:

Code: Select all

VSTGUI::CView* Controller::createCustomView (UTF8StringPtr name,
                                              const UIAttributes& attributes,
                                               const IUIDescription* description,
                                               VST3Editor* editor)
{
     if (UTF8StringView (name) == "MyView")
    {
          myView = new MyView(CRect(), this);
          return myView;
    }
    return nullptr;
}
                                                      
MyView can then inform the Editor about value changes for different parameters:

Code: Select all

// in MyView::onMouseDown():
editController->beginEdit(tag);

// in MyView::onMouseMoved():
editController->performEdit (tag, value);
editController->setParamNormalized (tag, value);

// in MyView::onMouseUp:
editController->endEdit(tag);
Do I understand it right: performEdit() informs the host and setParamNormalized() updates the editor?

For the other way round (MyView receives parameter changes) I guess I have to add MyView as listener to the two parameters.
In the sdk I see only code that is related Parameter Listener with CControls.

How can this be done? Can someone point me to the right direction?

Arne Scheffler
Posts: 414
Joined: Mon Jun 20, 2016 7:53 am

Re: Control (View) with multiple parameters?

Post by Arne Scheffler »

That's all correct. To get informed about parameter changes from the edit controller you can have a look at the PadController class at vstgui/plugin-bindings/vst3padcontroller.cpp how it is done there.

Cheers,
Arne

bb20
Posts: 4
Joined: Fri Sep 18, 2020 2:24 pm

Re: Control (View) with multiple parameters?

Post by bb20 »

Arne, thanks to your tip I got it working now!

Here's the code with some comments:

MyView.h

Code: Select all

namespace VSTGUI {

class MyView : public CView
             , public Steinberg::FObject
             , public DelegationController
{
public:
    MyView (Steinberg::Vst::EditController* editController = nullptr);
    ~MyView ();

    // --- CView methods ---
    
    void draw (CDrawContext *pContext) override;
    
    CMouseEventResult onMouseDown (CPoint& where, const CButtonState& buttons) override;
    CMouseEventResult onMouseUp (CPoint& where, const CButtonState& buttons) override;
    CMouseEventResult onMouseMoved (CPoint& where, const CButtonState& buttons) override;
    
    CLASS_METHODS (MyView, CView)

private:
    
    // --- IDependent (FObject) methods ---
    
    void PLUGIN_API update (Steinberg::FUnknown* changedUnknown, Steinberg::int32 message) override;

    // --- attributes ---
    
    Steinberg::Vst::EditController* editController;
     
    Steinberg::Vst::Parameter* parameter1;
    Steinberg::Vst::Parameter* parameter2;
    
    float mValue1;
    float mValue2;
};
}
MyView.cpp

Code: Select all

namespace VSTGUI {

MyView::MyView (Steinberg::Vst::EditController* editController)
: DelegationController (nullptr)
, CView (CRect(0,0,0,0))
, editController(editController)
, mValue1(0)
, mValue2(0)
{
    // retrieve the parameter that we are interested in from controller
    parameter1 = editController->getParameterObject (kParamId1);
    parameter2 = editController->getParameterObject (kParamId2);
    
    // listen to these parameters (parameter changes will trigger update() )
    if (parameter1)
        parameter1->addDependent (this);
    if (parameter2)
         parameter2->addDependent (this);
}

MyView::~MyView ()
{
    if (parameter1)
        parameter1->removeDependent (this);
    if (parameter2)
        parameter2->removeDependent (this);
}

// ...

CMouseEventResult MyView::onMouseDown (CPoint& where, const CButtonState& buttons)
{
    // ...
    // editController->beginEdit(tag);
    // ...
}


CMouseEventResult MyView::onMouseMoved (CPoint& where, const CButtonState& buttons)
{
    // ...
    // editController->performEdit (tag, value);
    // editController->setParamNormalized (tag, value);
    // ...
}

CMouseEventResult MyView::onMouseUp (CPoint& where, const CButtonState& buttons)
{
     // ...
    // editController->endEdit(tag);
    // ...
    
}

void PLUGIN_API MyView::update (Steinberg::FUnknown* changedUnknown,
                                      Steinberg::int32 message)
{
    // if a parameter value is changed (by host or by another control) this
    // method is called
    
    auto* p = Steinberg::FCast<Steinberg::Vst::Parameter> (changedUnknown);
    if (p)
    {
        if (message == kChanged)
        {
            // update local parameter value and draw()
            switch (p->getInfo().id) {
                 case kParamId1:
                     mValue1 = p->getNormalized ();
                     setDirty (true);
                    break;
                 case kParamId2:
                     mValue2 = p->getNormalized ();
                     setDirty (true);
                    break;
                 default:
                     break;
             }
         } else if (message == kWillDestroy)
        {
            // stop listening to parameter changes
            if (parameter1)
                parameter1->removeDependent (this);
            if (parameter2)
                parameter2->removeDependent (this);
            parameter1 = nullptr;
            parameter2 = nullptr;
        }
    }
}
}

Post Reply