AVPTS Listeners in Tabbed Component Problem

I have managed to get Sliders onto the main PluginEditor and got them attached to an APVTS and got the Listeners working, all good!

The problem is that my project requires Tabs because there’s no way everything will fit on one screen. I created a new Class for each Tab and I managed to get that working with a basic slider but when it comes to getting all the APVTS stuff working I have hit a brick wall!

If I leave the APVTS Attachment and the ParameterListener in the main PluginEditor Constructor then I cannot access the slider that is now defined in the new Tab Class! If I move all the APVTS Attachment and the ParameterListener stuff over to the new Tab Class then I get a problem trying to access the audioProcessor.apvts from the new Tab Class!!!

I know these are all basic C++ problems but I don’t know C++ very well, so could someone please be so kind as to start by telling me the correct approach?

If I have a bunch of sliders on a Tab, defined in a new Class for that Tab, how do I get the APVTS Attachments and Listeners working, should all that be moved into the new Class or left in the PluginEditor Constructor?

The new Class for the Tab is fairly basic:

class VoicePage : public juce::Component
{
public:

juce::Slider filterCutoffSlider;

VoicePage()
{
    addAndMakeVisible(filterCutoffSlider);

    filterCutoffSlider.setSliderStyle(juce::Slider::SliderStyle::Rotary);
    filterCutoffSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, 44, 20);
    filterCutoffSlider.setRange(0, 127, 1);

}
void resized() override
{
    filterCutoffSlider.setBounds(10, 10, 100, 100);
}

private:

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VoicePage)

};

The PluginEditor Constructor just has this:

TabbedComponentAudioProcessorEditor::TabbedComponentAudioProcessorEditor (TabbedComponentAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
//Tabs.setTitle(“Demo tabs”);
const auto tabColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId).darker(0.1f);
Tabs.addTab(“VOICE”, tabColour, new VoicePage(), true);
Tabs.addTab(“GLOBAL”, tabColour, new GlobalPage(), true);
addAndMakeVisible(Tabs);

filterCutoffSliderAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(audioProcessor.apvts, "FILTER_CUTOFF", filterCutoffSlider);
audioProcessor.apvts.addParameterListener("FILTER_CUTOFF", this);

setSize (400, 300);

}

If I move all the APVTS Attachment and the ParameterListener stuff over to the new Tab Class then I get a problem trying to access the audioProcessor.apvts from the new Tab Class!!!

How about passing a reference of APVTS to the VoicePage? For example:

Thanks but I cannot get that to work!

I’m still unclear, should I:

a) Leave all the Slider Attachments and audioProcessor.apvts.addParameterListener in the AudioProcessorEditor and try and access the Sliders in the Tabs?

b) Move all the Slider Attachments and apvts Parameter Listeners into the Tab Class and try and access the APVTS Parameters in the AudioProcessor from there?

I would suggest approach (b).

Thanks again, and I’m really sorry to be having to ask these basic questions but passing my reference to the AudioProcessor into my new Component Class just isn’t working:

Here’s the relevant Header bits:

class VoicePage : public juce::Component,
                  public juce::Slider::Listener,
                  public juce::AudioProcessorValueTreeState::Listener
{
public:
    VoicePage(TabbedComponentAudioProcessor&);
    ~VoicePage() override;

    //=============
    void resized();

private:
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    TabbedComponentAudioProcessor& audioProcessor;

Here’s the Constructor:

VoicePage::VoicePage(TabbedComponentAudioProcessor& p) : audioProcessor (p)
{
    addAndMakeVisible(filterCutoffSlider);
    filterCutoffSlider.setSliderStyle(juce::Slider::SliderStyle::Rotary);
    filterCutoffSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, 44, 20);
    filterCutoffSlider.setRange(0, 127, 1);
    filterCutoffSlider.setValue(64);
    //filterCutoffSlider.setSkewFactorFromMidPoint(20);

    addAndMakeVisible(filterCutoffLabel);
    filterCutoffLabel.setText("Filter Cutoff", juce::dontSendNotification);
    filterCutoffLabel.attachToComponent(&filterCutoffSlider, false);
    filterCutoffLabel.setJustificationType(juce::Justification::centredTop);

    filterCutoffSlider.addListener(this);

    addAndMakeVisible(requestButton);
    requestButton.setButtonText("Request Current Prog");
    requestButton.onClick = [this] { requestCurrentProg(); };

    // AVPTS Stuff
    //////////////
    filterCutoffSliderAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(audioProcessor.apvts, "FILTER_CUTOFF", filterCutoffSlider);
    audioProcessor.apvts.addParameterListener("FILTER_CUTOFF", this);
}

Visual Studio seems happy with that but I get this error from the compiler:

‘VoicePage::VoicePage’: no appropriate default constructor available

I tried following the way it’s done in the PluginEditor but something is not right and I’m losing the will to live :frowning:

The constructor takes a parameter, but you are probably declaring a usage of it (as a member of another class?) without providing that parameter. Where is the code which declares an object of VoicePage?

Yes, audioProcessor is also declared in the private: section of the PluginEditor Class, but since they were both in the private sections of their classes I used the same name???

The VoicePage Class is added as a Tab in the PluginEditor:

TabbedComponentAudioProcessorEditor::TabbedComponentAudioProcessorEditor (TabbedComponentAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    //Tabs.setTitle("Demo tabs");
    const auto tabColour = getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId).darker(0.1f);
    Tabs.addTab("VOICE", tabColour, new VoicePage(), true);
    Tabs.addTab("GLOBAL", tabColour, new GlobalPage(), true);
    addAndMakeVisible(Tabs);

    setSize (400, 300);
}

I found my omission, I forgot to pass audioProcessor into the new Tab, it should be:

Tabs.addTab("VOICE", tabColour, new VoicePage(audioProcessor), true);

It’s working now! Whether this is the right approach or not I do not know, it’s very hard to find information.

There’s a Tutorial on how to use AVPTS (and YouTube Videos) and a Tutorial on how to use TabbedComponent but there isn’t one on how to do both and access the APVTS from Tabs on a TabbedComponent!

Thanks everyone for your help, my C++ skills are sadly lacking but I’ll get there.

I don’t use the AVPTS class, but I believe this is the right approach. Ie. Since you need to give a child component access to it, passing as a reference is a good solution. Using a reference, instead of a pointer makes ownership clear.