Slider - change attached parameter dynamically

Ok, so after spending some time figuring out how to fix a segfault due to my MidiChannel parameter trying to modify some not-existing-yet attachments, I can at last confirm that the technique is working fine.

Thanks @mathieudemange for pointing me in the right direction, and thanks @Rincewind for the extra hint, setting the std::unique_ptr<SomeGuiAttachment> to nullptr before creating the new attachment is indeed required for the technique to work.

For the record, I ended up using something like this (inspired by this post from @daniel) :

// abstract base attachment wrapper class
class DynamicGuiAttachment {
protected:
    juce::AudioProcessorValueTreeState& apvts;
    juce::String parameterId;

    juce::String getFullParameterId(juce::String groupId) {
        return groupId + parameterGroupSeparator + parameterId;
    }

public:
    DynamicGuiAttachment(
        juce::AudioProcessorValueTreeState& vts,
        juce::String paramId
    ) : apvts(vts), parameterId(paramId) {}

    virtual void attachToGroup(juce::String groupId) = 0;
};

// implementation for slider (one must exist for each type of GUI control)
class DynamicSliderAttachment : public DynamicGuiAttachment {
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> attachment;
    juce::Slider& slider;

public:
    DynamicSliderAttachment(
        juce::AudioProcessorValueTreeState& vts,
        juce::String paramId,
        juce::Slider& sldr
    ) : DynamicGuiAttachment(vts, paramId), slider(sldr) {}

    void attachToGroup(juce::String groupId) override {
        attachment = nullptr; // MANDATORY !!!
        attachment.reset(new SliderAttachment(
            apvts, getFullParameterId(groupId), slider
        ));
    }
};

class MyWrapperComponent {
    std::map<std::string, std::unique_ptr<DynamicGuiAttachment>> attachments;
    juce::Slider mySlider;

public:
    MyWrapperComponent(PluginProcessor& processor) {
        addAndMakeVisible(mySlider);
        attachments["myParameter"] = std::make_unique<DynamicSliderAttachment>(
            processor.getValueTreeState(), "myParameter", mySlider
        );
        // etc ...
    }

    // groupId is inferred from the currently selected channel
    void setActiveParameterGroup(juce::String groupId) {
        for (const auto& [key, attachment] : attachments) {
            attachment->attachToGroup(groupId);
        }
    }
};

I should precise that I’m using an AudioProcessorParameterGroup containing the same set of AudioParameterFloat’s for each channel in the AudioProcessorValueTreeState, and a "global" group with a "MidiChannel" parameter to control which channel group the GUI controls are attached to.

Hope this might help someone …
Cheers !

1 Like