# AudioProcessorValueTreeState with child processors' parameters

**URL:** <https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722>\
**Category:** Audio Plugins\
**Created:** [April 6, 2019, 8:40pm UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722 "2019-04-06T20:40:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![rlg](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/rlg/32/7362_2.png) [@rlg](https://forum.juce.com/u/rlg)\
**Post date:** [April 6, 2019, 8:40pm UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/1 "2019-04-06T20:40:27Z")

</div>

I’m trying to wrap my head around using AudioProcessorValueTreeState in a plugin using child processors. Ideally, I’d like to get a parent processor with an AudioProcessorValueTreeState reflecting parameters present in any child processors, with the child processors providing the parent constructor their parameters themselves.

I can have child processors with their own AudioProcessorValueTreeStates, but those aren’t seen by the plugin host as they don’t show up in the parent’s parameters.

I can have the child processors provide parameters to the parent’s APVTS in the parent’s constructor, as in the example below, but there’s some cumbersome acrobatics involved in getting the parameter linked to the appropriate child variables – the parameter listener will only provide the [0 1] range, so I appear to need to keep each parameter’s NormalisableRange somewhere in the child…

It would be great to just pass a pointer to the parent APVTS to the child, but the child needs to be initialized in order to provide its parameters to the parent constructor, so the APVTS doesn’t exist when the child is being initialized.

Can an APVTS add a child’s entire APVTS to it? Or extract and/or move the child APVTS as a ParameterLayout in the parent APVTS constructor?

If I’m missing something simple or obvious, what is it? Anybody else facing the problem of surfacing child parameters to the parent processor? It seems like this isn’t an esoteric use case – providing host-visible parameters linked to child processors in a Graph or ProcessorChain or whatever. Any thoughts greatly appreciated.

```cpp
class BaseProcessor : public AudioProcessor 
{
// ... default shared AudioProcessor setup methods ...
};

class GainProcessor : public BaseProcessor, public AudioProcessorParameter::Listener
{
public:
    GainProcessor() {}
    std::vector<std::unique_ptr<RangedAudioParameter>> createParameters() {
        std::vector<std::unique_ptr<RangedAudioParameter>> params;
        auto gainParam = std::make_unique<AudioParameterFloat> ("gain", "Gain", 0.0f, 2.0f, 1.0f);
        gainParam->addListener(this);
        gainRange = gainParam.get()->getNormalisableRange();
        params.push_back(std::move(gainParam));
        return params;
    }
    
    void parameterValueChanged (int parameterIndex, float newValue) override {
        gainParamValue = gainRange.convertFrom0to1(newValue);
    }
    void parameterGestureChanged (int, bool) override {}
    
    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        buffer.applyGain(gainParamValue);
    }

private:
    float gainParamValue = 1.0f;
    NormalisableRange<float> gainRange;
};

class ParentProcessor : public BaseProcessor {
public:
    //==============================================================================
    ParentProcessor()
        : gainProcessor()
        , avts (*this, nullptr, "PARAMETERS", createParameters() )
    {}
    
    AudioProcessorValueTreeState::ParameterLayout createParameters()
    {
        std::vector<std::unique_ptr<RangedAudioParameter>> params;
        auto gainParams = gainProcessor.createParameters();
        for (auto& p : gainParams)
            params.push_back(std::move(p));
        return { params.begin(), params.end() };
    }
    
    virtual void prepareToPlay (double sampleRate, int samplesPerBlock) override
    {
        gainProcessor.prepareToPlay(sampleRate, samplesPerBlock);
    }

    virtual void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
            buffer.clear (i, 0, buffer.getNumSamples());
        gainProcessor.processBlock(buffer, midiMessages);
    }

    GainProcessor gainProcessor;
    AudioProcessorValueTreeState avts;
};

```

---

<div class="post-metadata">

**Author:** ![JuceUser1980](https://avatars.discourse-cdn.com/v4/letter/j/ecae2f/32.png) [@JuceUser1980](https://forum.juce.com/u/JuceUser1980)\
**Post date:** [April 7, 2019, 5:05am UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/2 "2019-04-07T05:05:43Z")

</div>

One thing that occurs to me is to attach multiple apvts as children of one parent ValueTree, and pass that around. I’m a newbie myself and don’t know how to get at the parameters of each child apvts from the mother node. I have verified that a child apvts does continue to function normally after being added to a parent ValueTree. Can someone expand on this?

---

<div class="post-metadata">

**Author:** ![daniel](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/daniel/32/790_2.png) [@daniel](https://forum.juce.com/u/daniel)\
**Post date:** [April 7, 2019, 10:20am UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/3 "2019-04-07T10:20:29Z")

</div>

Assuming, you wrote those AudioProcessors to be hosted in your plugin…

I solved this with a static method in the main processor, that collects the parameters of the sub processors into a vector.

Easy variant is to give a reference to the vector to each sub processor like

```auto
static void SubProcessor::createParameterLayout (AudioProcessorValueTreeState::ParameterLayout& layout)
{
    auto drive = std::make_unique<AudioParameterFloat>(DistortionProcessor::paramDrive, 
                                                       NEEDS_TRANS ("Overdrive"),
                                                       NormalisableRange<float> (1.0, 11.0), 1.0);
    layout.add (std::move (drive));
}

static AudioProcessorValueTreeState::ParameterLayout MainProcessor::createParameterLayout()
{
    AudioProcessorValueTreeState::ParameterLayout layout;
    SubProcessor::createParameterLayout (layout);
    return layout;
}

```

More elegant is to use AudioProcessorParameterGroups for each sub processor:

```auto
static std::unique_ptr<AudioProcessorParameterGroup> SubProcessor::createProcessorParameters ()
{
    auto drive = std::make_unique<AudioParameterFloat>(DistortionProcessor::paramDrive, 
                                                       NEEDS_TRANS ("Overdrive"),
                                                       NormalisableRange<float> (1.0, 11.0), 1.0);
    layout.push_back (std::move (drive));
}

static AudioProcessorValueTreeState::ParameterLayout MainProcessor::createParameterLayout()
{
    std::vector<std::unique_ptr<AudioProcessorParameterGroup>> params;

    params.push_back (SubProcessor::createProcessorParameters());
    return { params.begin(), params.end() };
}

```

---

<div class="post-metadata">

**Author:** ![rlg](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/rlg/32/7362_2.png) [@rlg](https://forum.juce.com/u/rlg)\
**Post date:** [April 7, 2019, 12:48pm UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/4 "2019-04-07T12:48:12Z")

</div>

daniel, many thanks – making the child processor’s createProcessorParameters() method static is the key. Now I can create the APVTS with necessary child processor parameters, and initialize the child processor afterwards, passing a pointer to the generated APVTS to the child processor constructor.

To bring it back to my original example, the code now looks like this:

```cpp
class BaseProcessor : public AudioProcessor 
{
// ... default shared AudioProcessor setup methods ...
};

class GainProcessor : public BaseProcessor
{
public:
    GainProcessor(AudioProcessorValueTreeState& s) : avts(s) {}
    
    static std::unique_ptr<AudioProcessorParameterGroup> createProcessorParameters ()
    {
        std::unique_ptr<AudioProcessorParameterGroup> layout (std::make_unique<AudioProcessorParameterGroup>("groupGain", "Gain", "|"));
        auto gain = std::make_unique<AudioParameterFloat> ("gain", "Gain", 0.0f, 2.0f, 1.0f);
        layout.get()->addChild(std::move(gain));
        return layout;
    }
    
    void prepareToPlay (double sampleRate, int samplesPerBlock) override {
        gainParamValue = avts.getRawParameterValue("gain");
    }
    
    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        buffer.applyGain(*gainParamValue);
    }

private:
    AudioProcessorValueTreeState& avts;
    float* gainParamValue;
};

class ParentProcessor : public BaseProcessor {
public:
    //==============================================================================
    ParentProcessor()
        : avts (*this, nullptr, "PARAMETERS", createParameters() )
        , gainProcessor(avts)
    {}
    
    AudioProcessorValueTreeState::ParameterLayout createParameters()
    {
        std::vector<std::unique_ptr<AudioProcessorParameterGroup>> params;
        params.push_back (GainProcessor::createProcessorParameters());
        return { params.begin(), params.end() };
    }
  
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
    {
        gainProcessor.prepareToPlay(sampleRate, samplesPerBlock);
    }

    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
            buffer.clear (i, 0, buffer.getNumSamples());
        gainProcessor.processBlock(buffer, midiMessages);
    }

    AudioProcessorValueTreeState avts;
    GainProcessor gainProcessor;
};

```

---

<div class="post-metadata">

**Author:** ![daniel](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/daniel/32/790_2.png) [@daniel](https://forum.juce.com/u/daniel)\
**Post date:** [April 7, 2019, 1:34pm UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/5 "2019-04-07T13:34:10Z")

</div>

N.B. since they share the necessary information via the AudioProcessorValueTreeState, there is actually no need for a common base class for your sub processors. IMHO this ties it unnecessarily to the one setup, but if it was me, I would want to recycle those sub processors.

---

<div class="post-metadata">

**Author:** ![ryanavery](https://avatars.discourse-cdn.com/v4/letter/r/c68b51/32.png) [@ryanavery](https://forum.juce.com/u/ryanavery)\
**Post date:** [April 30, 2019, 8:38pm UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/6 "2019-04-30T20:38:27Z")

</div>

I like this solution, and am using a similar technique myself. However, one problem with this is that it makes instantiating GainProcessor on its own (for unit testing) almost impossible.

After all, GainProcessor requires an initialized AudioProcessorValueTreeState to be instantiated, but to create a new AudioProcessorValueTreeState, it needs to attach to an AudioProcessor. A chicken-and-the-egg scenario.

I haven’t been able to solve this yet. If anyone has any suggestions, I would love to hear it!

---

<div class="post-metadata">

**Author:** ![rlg](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/rlg/32/7362_2.png) [@rlg](https://forum.juce.com/u/rlg)\
**Post date:** [May 1, 2019, 1:11am UTC](https://forum.juce.com/t/audioprocessorvaluetreestate-with-child-processors-parameters/32722/7 "2019-05-01T01:11:34Z")

</div>

Yes, this chicken and egg situation has also kept me scratching my head. I’ve now moved on to the following, with the above example updated below:

```cpp
class BaseProcessor : public AudioProcessor
{
// ... generic AudioProcessor setup methods
};

class GainProcessor : public BaseProcessor
{
public:
    GainProcessor(bool c = false)
        : isChild(c)
        , localAvts(*this, nullptr, "PARAMETERS", createProcessorParameters())
    {
        if (!isChild) 
            initializeAvts(localAvts);
    }

    void initializeAvts(AudioProcessorValueTreeState& s)
    {
        avts = &s;
    }
    
    std::unique_ptr<AudioProcessorParameterGroup> createProcessorParameters ()
    {
        std::unique_ptr<AudioProcessorParameterGroup> layout (std::make_unique<AudioProcessorParameterGroup>("groupGain", "Gain", "|"));
        auto gain = std::make_unique<AudioParameterFloat> ("gain", "Gain", 0.0f, 2.0f, 1.0f);
        layout.get()->addChild(std::move(gain));
        return layout;
    }
    
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
    {
        gainParamValue = avts->getRawParameterValue("gain");
    }
    
    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        buffer.applyGain(*gainParamValue);
    }

private:
    bool isChild;
    AudioProcessorValueTreeState* avts;
    AudioProcessorValueTreeState localAvts;
    float* gainParamValue;
};

class ParentProcessor : public BaseProcessor {
public:
    //==============================================================================
    ParentProcessor()
        : gainProcessor(true)
        , parentAvts (*this, nullptr, "PARAMETERS", createParameters())
    {
        gainProcessor.initializeAvts(parentAvts);
    }
    
    AudioProcessorValueTreeState::ParameterLayout createParameters()
    {
        std::vector<std::unique_ptr<AudioProcessorParameterGroup>> params;
        params.push_back (gainProcessor.createProcessorParameters());
        return { params.begin(), params.end() };
    }
  
    void prepareToPlay (double sampleRate, int samplesPerBlock) override
    {
        gainProcessor.prepareToPlay(sampleRate, samplesPerBlock);
    }

    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
    {
        for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
            buffer.clear (i, 0, buffer.getNumSamples());
        gainProcessor.processBlock(buffer, midiMessages);
    }

    GainProcessor gainProcessor;
    AudioProcessorValueTreeState parentAvts;
};

```

GainProcessor can be initialized by itself or via the parent processor, and works with its AVPTS\* variable `avts`, which gets assigned with the `initializeAvts()` method.

In the standalone case, GainProcessor’s constructor initializes `localAvts`, then calls `initializeAvts()`.

In the parent/child case, `GainProcessor` gets initialized first, then `ParentProcessor` initializes its own `parentAvts` with the `gainProcessor` instance’s `createProcessorParameters()` (not a static method anymore) then passes `parentAvts` to `GainProcessor`.

This is all pretty convoluted and clumsy, but at least it works, and can be extended to multiple child processors. If anyone has a more simple or elegant solution, I’d love to know.
