Create AudioProcessorValueTreeState attachments for own GUI class

Hey there,

I’m writing a plugin which is supposed to send 2-dimensional panning-information to another computer via OSC.
Therefore I’ve written a 2D-slider GUI class which is basically a rectangle in which you can move around a knob (a surround panner if you want to).
Everything works fine so far as a stand-alone but I’m having difficulties with creating a connection between my 2D-slider and a parameter in my AudioProcessorValueTreeState to be able to use it as a real plugin that saves the parameters and is able to read and write automations in the daw.
Since I don’t use a native JUCE GUI class such as Slider or Button there is no pre-defined Attachment class for my 2D-slider, which I could create inside my PluginEditor.
What would be the most effective way to solve my problem?
I’ve already tried to create a ParameterAttachment object inside my 2D-slider class. But then I don’t know how to connect it to my AudioProcessorValueTreeState as ParameterAttachments seem to only connect to a certain parameter but not to an AudioProcessorValueTreeState.

I’m sure that I’m missing out something obvious.
It’d be great if somebody more experienced could help me!

Cheers,
Christian

The ParameterAttachment is the right one to use.
It connects to a parameter, which is what the other attachments do as well, they just pick the right parameter through the APVTS. You can call getParameter on the APVTS or directly on the AudioProcessor and use that to connect your ParameterAttachment.

And you can use two ParameterAttachments, one for each dimension.

Hope that helps

1 Like

Thanks a lot Daniel!
One more thing:
ParameterAttachments take a callback function of type std::function<void(float)> as an argument.
I suppose I have to create these functions as member functions of my 2D-slider class for every variable that needs to be controlled (in my case x and y axis). These function will then be called whenever the parameter has changed.

I’m a bit confused by the std::function<void(float)> stuff…
How does the spelling work for the act of passing a function into the constructor of an object?
I tried it like this:

std::function< void ( const TwoDimensionalSlider, float )> xParam = &TwoDimensionalSlider::xParameterChanged;

std::function< void ( const TwoDimensionalSlider, float )> zParam = &TwoDimensionalSlider::zParameterChanged;

xAttachment = new ParameterAttachment(m_parent->apvts.getParameter("xValue"), xParam( this , m_parent->apvts.getParameter("xValue")->getValue()), nullptr );

zAttachment = new ParameterAttachment(m_parent->apvts.getParameter("zValue"), zParam( this , m_parent->apvts.getParameter("zValue")->getValue()), nullptr);

And then in the header file:

    std::unique_ptr<ParameterAttachment> xAttachment;

    std::unique_ptr<ParameterAttachment> zAttachment;

    void xParameterChanged ( float value) const {value = thumbPositionX;}

    void zParameterChanged ( float value) const {value = thumbPositionZ;}

m_parent is a pointer to my plugin processor.

This is the error I’m getting:
No matching function for call to object of type 'std::function<void (const TwoDimensionalSlider, float)>'

the std::function (also called lambda) is a convenient way to wrap a little code snippet with context (aka capture).

For your use case I would write something like that:

class XYpad : public juce::Component
{
public:
    XYpad ()
    {
        addAndMakeVisible (knob);
    }
    connectXtoParameter (juce::RangedAudioParameter& p)
    {
        xAttachment = std::make_unique<juce::ParameterAttachment>(p, 
            [this](float newValue) { value = newValue; resized(); },
            nullptr);
    }
    void resized() override
    {
        knob.setBounds (getWidth() - 10 * value, 5, 10, 10);
    }
private:
    class Knob : public juce::Component
    {
    public:
        Knob (XYpad& pad) : owner (pad) {}
        void mouseDown (const juce::MouseEvent&) override
        {
            if (owner.xAttachment.get() != nullptr) owner.xAttachment->beginGesture();
        }
        void mouseUp (const juce::MouseEvent&) override
        {
            if (owner.xAttachment.get() != nullptr) owner.xAttachment->endGesture();
        }
        void mouseDrag (const juce::MouseEvent& event) override
        {
            if (owner.xAttachment.get() != nullptr)
            {
                owner.xAttachment.setValueAsPartOfGesture (event.x / (owner.getWidth() - 10));
            }
        }
        void paint (juce::Graphics& g) override
        {
            g.setColour (juce::Colours::red);
            g.fillEllipse (getLocalBounds().toFloat());
        }
    private:
        XYpad& owner;
    }
    float value = 0.0f;
    std::unique_ptr<juce::ParameterAttachment> xAttachment;
    Knob knob { *this };
};

The missing bit is found in connectXToParameter(). You will probably want to add the same for Y and do some other modifications to adapt it to your usecase.

Got a bit longer, but it should have all what’s needed… untested code though!

1 Like

Thank you! You’re the man!!