AudioProcessorValueTreeState with different classes

Hi there, I am new to JUCE so I am unsure now if how I’ve set up my project has been wrong from the beginning. Basically I just found out about the AudioProcessorValueTreeState and have to change some things (I was passing references everywhere and it was getting a bit messy) I was following TheAudioProgrammer tutorial from 4 years ago but cannot think how to do it with different classes. I have different classes for my different components (which I don’t know if that is the standard) for example for the reverb or the oscillator. I understand how to make the APVTS work with only the Pluginprocessor and Plugineditor but when I am working with different classes should I be passing the reference to the processor as a parameter to the different classes? Or pass the reference to the APVTS in the processor? I was trying that before and kept getting errors (Copilot didn’t seem to know what to do either and kept trying to solve everything by using forward declarations instead of #include). Any help will be very appreciated, thank you very much!

It’s not uncommon to have child components also take a reference to the processor in their constructor. You essentially just keep passing it down the chain through constructors of your component hierarchy.

1 Like

Pass the processor around and use it in multiple ProcessorEditors’ if you need to, setting Attachments to the relevant parameters for the specific editor currently in focus …

1 Like

Thank you Fandusss and Ibisum! It is working now, thought it would be better to pass a reference just to the APVTS of the processor instead of the processor as that’s the only thing I’ll need?

I think code gurus will agree that you should only expose as little as you can to get the functionality you need. Others, like me, are lazy and prefer to just pass the entire processor in case your needs change in the future. Refactoring entire constructor chains can be a pain.

I think if you are writing code that a lot of people touch, being correct about things is a good thing. If you are a solo dev, you can be a bit more dangerous to make things more flexible.

In my experience, hooking up the processor or APVTs directly to UI components is a tempting anti-pattern that will shoot you in the foot once things get more complex and you want to make bigger changes. I’ve been moving away from doing that.

I recommend making components independent of any application logic, just with the necessary APIs to get stuff in and out. Build the component hierarchy and give components IDs to find them later.

Similarly, build a hierarchy of controller objects that basically connect UI components with application logic. That’s where attachments go, for example. Also any listeners, datasources and what have you. Just give it the top-level component and the processor, and the constructors will pull out the components they need and make the necessary connection between the processor and the UI.

Here’s some handy utility code I use for that:

juce::Component* recursiveFindChildWithID(juce::Component* root, juce::StringRef targetID)
{
    // First try to find direct children
    Component* result = root->findChildWithID(targetID);
    
    if(result)
        return result;
    
    // If not successful, visit child hierarchy recursively
    for(Component* c : root->getChildren())
    {
        result = recursiveFindChildWithID(c, targetID);
        if(result)
            break;
    }
    
    return result;
}

template <class C>
C* recursiveFindChildAsType(juce::Component* root, juce::StringRef targetID)
{
    auto result = recursiveFindChildWithID(root, targetID);
    
    if(result)
        return dynamic_cast<C*>(result);
    else
        return nullptr;
}

The PluginEditor is the top-level thingy that puts both together. It has the top-level UI component, and after setting that up creates the top-level controller, stringing it all together.

It’s more work, but much cleaner to maintain in the long run. Especially useful to untangle multiple components that work with the same kind of underlying logic. You can also start with a dummy controller that just mocks app logic to get the UI working before hooking it up to the real thing.

2 Likes

Good advice - one question, do you only do the recursiveFileChildAsType() once, during the constructor of your Controller class, to ‘bind’ the controller with the component its interested in?

Yes, only during construction.

You can think of it in web terms. The UI components are basically HTML/CSS and describe the visual structure and presentation. The tree is kind of a DOM. The controllers are basically the JS that controls behavior. It pulls (queries) components out of the DOM initially and hooks them up with callbacks and updating functions. Once that’s done, all the bits and pieces do their thang.

Yeah nice… I must admit I’ve been resistant to doing this, mostly because of the Controller class design, which I don’t really have formulated properly in my own app context, yet. But its good to know you’ve taken the approach and find it productive. I wonder if there are any good examples of this out there, for example in the awesome-juce archive …

This is incredibly helpful, thank you very much!