How to save non-parameter values in a plugin as presets etc


#1

Hi All,

Is anyone able to give me a rough idea of the best approach for saving state in an AudioProcessor for non-parameter values. Basically for members of the AudioProcessor which I do not wish to be automatable ?

I’m guessing the best way is somehow through a value tree.

I’d like to know how to do this and also save these values as a plugin preset of some description.

Any help greatly appreciated.

Cheers


#2

Would something like this work for you:

class JuceDemoPluginAudioProcessor  : public AudioProcessor
{
public:
    //==============================================================================
    JuceDemoPluginAudioProcessor()
    {
        pluginState.state.setProperty (myCoolParameterID, var(5), nullptr);
        myCoolParameter.referTo (pluginState.state.getPropertyAsValue (myCoolParameterID, nullptr));
    }
    .
    .
    .
    
    void someFunction()
    {
        myCoolParameter.setValue (7);
    }
    
    // this is a parameter which should not be visible in the DAW
    Value myCoolParameter;
    
    // Our public & hidden parameters: includes cool parameter above
    AudioProcessorValueTreeState pluginState;
    
    void getStateInformation (MemoryBlock& destData)
    {
        MemoryOutputStream stream (destData, true);
        pluginState.state.writeToStream (stream);
    }
    
    void setStateInformation (const void* data, int sizeInBytes)
    {
        MemoryInputStream stream (data, sizeInBytes, false);
        pluginState.state.readFromStream (stream);
    }
    
    //
    static Identifier myCoolParameterID;
    .
    .
    .

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceDemoPluginAudioProcessor)
};
        
Identifier JuceDemoPluginAudioProcessor::myCoolParameter ("myCoolParameter");

#3

Awesome. Thanks a bunch @fabian

Does this approach mean the host/DAW will save the state information in whatever project file it uses ? So if I shut down the host and load up the project file again the state will be remembered ?

I’m just a little unsure as to how this behaviour works.

I’m hoping i can also write the state to some file location to handle recalling presets etc ? (Showing my general ignorance here I expect!)

(Sorry I’m currently at work in .Net land so cannot test this out till later).

Thanks for the help


#4

Yup exactly!

You should be reporting your presets via the falling callbacks:

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

With these, the DAW should manage saving the state to various presets for you.


#5

Brilliant. :+1:


#6

@fabian

Just out of interest what would be the best way to update GUI components related to these non-parameter values in the call to setStateInformation() when a preset is loaded ?

Is it safe to send some sort of change message or broadcast and event from the setStateInformation() call ?

I realise I cannot refer to the GUI components in this method as they may not yet exist. On the GUI/Editor load I can check these values by reading the AudioProcessorValueTreeState::state but need to be able to update the controls on a preset/program load etc.

I’d prefer to do this without a timer polling the non-parameter values as they will only ever be updated by the GUI components or the setStateInformation() call (obviously no automation or anything like that going on)

EDIT: Apologies the forum is full of me mulling over parameter stuff today :confused:


#7

You can just attach a listener to the Values. See Value::Listener.


#8

Is it possible to then set a Value object without notifying its listeners ?

Say if the component attached to the non-parameter value is a Slider.

Seems like I’d then be calling the Slider’s Value::Listener callback every time the slider is moved and updates the Value (The value would be a non-parameter float) ? Is this a normal approach ?


#9

Fabian - was your example thread-safe? i.e. that value object can be accessed from the audio thread?

Also will it still be connected after pluginState.state.readFromStream() completes?? Surely that replaces the valuetree and loses the connection?


#10

Yeah that example is very old is not quite right anymore. The correct approach is to use the AudioProcessorValueTreeState::SliderAttachment or similar classes when connecting your parameters to the GUI. To load/save the state, I think this code is better:

void getStateInformation (juce::MemoryBlock& destData) override
{
    ScopedPointer<XmlElement> xml (processorState.state.createXml());

    if (xml != nullptr)
        copyXmlToBinary (*xml, destData);
}

void setStateInformation (const void* data, int sizeInBytes) override
{
    ScopedPointer<XmlElement> xml (getXmlFromBinary (data, sizeInBytes));

    if (xml != nullptr)
        processorState.state = ValueTree::fromXml (*xml);
}