AudioProcessorValueTreeState Improvements

ok, I’m quite sleepy today, so it might be my mistake, but I will hit the assertion in the constructor with tom’s code :

   GainProcessor() : parameters (*this, nullptr, "PARAMS", createParameterLayout())
   {
        jassert (gainParam != nullptr);
   }

   AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
   {
        std::vector<std::unique_ptr<RangedAudioParameter>> params;
        auto param = std::make_unique<AudioParameterFloat> ("gain", "Gain", 0.0f, 1.0f, 0.5f);
        gainParam = param.get();
        params.push_back (std::move (param));
        return { params.begin(), params.end() };
    }

AudioProcessorValueTreeState parameters;
AudioParameterFloat* gainParam = nullptr;
< a PIP project to reproduce >
/*******************************************************************************
 The block below describes the properties of this PIP. A PIP is a short snippet
 of code that can be read by the Projucer and used to generate a JUCE project.

 BEGIN_JUCE_PIP_METADATA

 name:             GainPlugin
 version:          1.0.0
 vendor:           JUCE
 website:          http://juce.com
 description:      Gain audio plugin.

 dependencies:     juce_audio_basics, juce_audio_devices, juce_audio_formats,
                   juce_audio_plugin_client, juce_audio_processors,
                   juce_audio_utils, juce_core, juce_data_structures,
                   juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
 exporters:        xcode_mac, vs2019

 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1

 type:             AudioProcessor
 mainClass:        GainProcessor

 useLocalCopy:     1

 END_JUCE_PIP_METADATA

*******************************************************************************/

#pragma once

//==============================================================================
class GainProcessor  : public AudioProcessor
{
public:
    //==============================================================================
    GainProcessor()
    : AudioProcessor (BusesProperties().withInput  ("Input",  AudioChannelSet::stereo())
                                       .withOutput ("Output", AudioChannelSet::stereo()))
    , parameters (*this, nullptr, "PARAMS", createParameterLayout())
    {
        jassert (gainParam != nullptr);
    }

    AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
    {
        std::vector<std::unique_ptr<RangedAudioParameter>> params;
        auto param = std::make_unique<AudioParameterFloat> ("gain", "Gain", 0.0f, 1.0f, 0.5f);
        gainParam = param.get();
        params.push_back (std::move (param));
        return { params.begin(), params.end() };
    }

    //==============================================================================
    void prepareToPlay (double, int) override {}
    void releaseResources() override {}
    void processBlock (AudioBuffer<float>& buffer, MidiBuffer&) override {}

    //==============================================================================
    AudioProcessorEditor* createEditor() override          { return nullptr; }
    bool hasEditor() const override                        { return false;   }

    //==============================================================================
    const String getName() const override                  { return "Gain PlugIn"; }
    bool acceptsMidi() const override                      { return false; }
    bool producesMidi() const override                     { return false; }
    double getTailLengthSeconds() const override           { return 0; }

    //==============================================================================
    int getNumPrograms() override                          { return 1; }
    int getCurrentProgram() override                       { return 0; }
    void setCurrentProgram (int) override                  {}
    const String getProgramName (int) override             { return {}; }
    void changeProgramName (int, const String&) override   {}

    //==============================================================================
    void getStateInformation (MemoryBlock& destData) override {}
    void setStateInformation (const void* data, int sizeInBytes) override {}

    //==============================================================================
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override
    {
        const auto& mainInLayout  = layouts.getChannelSet (true,  0);
        const auto& mainOutLayout = layouts.getChannelSet (false, 0);

        return (mainInLayout == mainOutLayout && (! mainInLayout.isDisabled()));
    }

private:
    //==============================================================================
    AudioProcessorValueTreeState parameters;
    AudioParameterFloat* gainParam = nullptr;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainProcessor)
};

What’s going on here?

createParameterLayout() is called before the construction of your GainProcessor has finished. So the member gainParam is set to nullptr after the parameters were constructed.

1 Like

oh, yes :slight_smile:
sorry for the noise guys, I really need a sleep I think.

RangedAudioParameter* gainParam = nullptr;
AudioProcessorValueTreeState parameters { *this, nullptr, "PARAMS", createParameterLayout() };

While that has more chance to deliver the expected results, I would really avoid accessing members before at least all members are initialised.
Instead I would set the gainParam in the constructor body. And the createParameterLayout() I would make static, so it cannot access members.
I am not deep enough in the current literature to claim it has to be that way, just gut feeling.
Maybe somebody can chime in, if I am paranoid or if that is best practise.

You may need a sleep, but I also made that mistake in the example code. I’ll update it now.