Idea for Saving/Restoring Processor Param Objects

Since there is considerable push for using the AudioProcessorParameter class, why not provide a means of saving and restoring these parameters via XML, for use in AudioProcessor's getStateInformation()/setStateInformation() ?

The following implementation is quite specific to the code I've been working on, but it could no doubt be rehashed for everyone to use with a bit of TLC. The caveat is that I have to use an explicit method that provides an Identifier for my custom processors and parameter base-classes (instead of using the parameter name or processor name, in case anyone happens to use TRANS()).

void CustomProcessor::getStateInformation (MemoryBlock& destData)
{
   ValueTree valueTree (getIdentifier());

    {
       ScopedLock sl (getCallbackLock());

        const OwnedArray<AudioProcessorParameter>& parameters = getParameters();

        for (int i = 0; i < parameters.size(); ++i)
            if (CustomAudioProcessorParameter* const p = dynamic_cast<CustomAudioProcessorParameter*> (parameters.getUnchecked (i)))
                valueTree.setProperty (p->getIdentifier(), p->getValue(), nullptr);
    }

   ScopedPointer<XmlElement> state (valueTree.createXml());

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

void CustomProcessor::setStateInformation (const void* data, const int sizeInBytes)
{
   ScopedPointer<XmlElement> state (getXmlFromBinary (data, sizeInBytes));

    if (state != nullptr)
    {
       ValueTree valueTree (ValueTree::fromXml (*state));

        if (valueTree.getType() == getIdentifier())
        {
            ScopedLock sl (getCallbackLock());

            const OwnedArray<AudioProcessorParameter>& parameters = getParameters();

            for (int i = 0; i < valueTree.getNumProperties(); ++i)
            {
                for (int f = 0; f < parameters.size(); ++f)
                    if (CustomAudioProcessorParameter* const p = dynamic_cast<CustomAudioProcessorParameter*> (parameters.getUnchecked (i)))
                        if (p->getIdentifier() == valueTree.getPropertyName (i))
                            p->setValue (valueTree.getProperty (valueTree.getPropertyName (i)));
            }
        }
    }

    updateHostDisplay();
}

This is something we are currently looking at, together with adding full support for parameter labels and ranges. In any case, only AudioProcessors using the AudioProcessorParameter class are likely to benefit from any new features.

I like this a lot because it reduces the amount of code needed and it automatically works when a parameter is added, removed, or modified. The only issue I see is that parameter names need to be carefully constructed so they form valid XML attributes. This could be solved by adding another field for the attribute name or adding a simple algorithm to replace invalid characters in the name, etc.  Even simpler would be to auotmatically generate attribute names based on the parameter index (param1, param2, etc.), although this has the drawback of breaking if the indexes ever change, and making the XML less readable.

Voice of experience: It is not advisable to use either the visible parameter name or the parameter index to do save/restore.  Works great until you have to rename or add/remove a parameter (changing indices).  Then you're gonna have a bad time.

This is exactly why AAX uses a parameter ID which is separate from the user-visible name.

jrlanglois also appears to be using this approach (separate identifier).

In the implementation of said identifier, I strongly advise using an explictly set identifier rather than an auto-generated one based on name or index.  The latter is prone to set up the unpleasant situation where you've renamed a control or re-ordered them in a way that changes the identifier, but you don't realize that it has done so until testers (or worse, end-users) complain that settings don't restore right. It's easier to miss than you might think, especially when it only affects a single control.  Much better to use an explicitly set identifier which will always be the same for a given parameter until the developer intentionally changes it in the source.

 

 

 

I agree.

Yes, definitely. One of the good things about the new parameter objects is that sub-classes can use IDs. In the ValueTree-based helper classes that we've been using here at ROLI (and which we'll release publicly at some point), we save the parameters in a big ValueTree using IDs for them.

Works great until you have to rename or add/remove a parameter (changing indices)

iiuc when changing indices it will screw up automation in many hosts which I believe work by parameter indices

1 Like