Couple of beginer questions - Adding parameters & Read out

1- What are the basic steps to add a parameter so it could work in the host? I believe we have to use getStateInformation, settStateInformation. Anything else?
I did check out some examples and they seem a bit different and confusing so a straight forward instruction could clarify things for me.

2 - This is a dumb question, but when you want to read out a processing data, like midi notes etc, how exactly do you update the read out label in the editor?

  1. Take a look in examples/PlugInSamples and perhaps examples/PlugInSamples/GainPliugin first as it’s super simple.
  2. For simple data you can store it in a member variable then use a regular Timer to update your GUI. But for streams of information this is a bit more involved as you need to use a buffer (FIFO) to do this.

AFAIK no. The automated parameters are stored in the host, because it’s value is obviously dependant on the playing position. In the get/setStateInformation you save things, that are not visible to the host.

Using AudioProcessorValueTreeState works very well, it handles parameters and you can store all other information in the public member “state”. Then in the get/setStateInformation you simply write the complete ValueTree into the memoryblock.
And you get undo/redo functionality, if you want.

Examples audio plugin demo and gain, use AudioParameterFloat and addParameter.
but AudioProcessorValueTreeState looks interesting. Is this a different way of doing it and Is there any examples?
Say i create a parameter,

AudioParameterFloat* Parameter1;
…
addParameter (Parameter1 = new AudioParameterFloat (“Param1”, “Param1”, 0.0f, 1.0f, 0.0f));

How can i set and change its value? For example on reset button or a selector.
Like this: “Parameter1->setValueNotifyingHost(0.0f);” ?
What should i do in set and getstateinformation section?

AudioProcessorValueTreeState wraps AudioProcessorParameters, so you can use either in the constructor addParameter (new AudioProcessorParameterFloat()); or treeState.createAndAddParameter (...); but you cannot mix them.

exactly, the host will check if this change is ok with it’s automation settings and call in turn setValue() of your processor parameter (e.g. if it’s set to read, it might not accept the change).

If you use AudioProcessorValueTreeState, you have the additional feature to register the processor as listener of a parameter and implement a callback, whenever the host changes a parameter: AudioProcessorValueTreeState::Listener

In get/setStateInformation you don’t need to do anything, if you only have automated parameters. If you have e.g. a tabbed component in your editor and want to restore, which tab was selected, you write that information in getStateInformation into the project file and restore it, when setStateInformation is called.

I usually have an AudioProcessorValueTreeState member called mState, set in the constructor (after the createProcessorParameter calls) an ValueTree like

mState.state = ValueTree ("MySetup");

and save and restore like this:

void MyAudioProcessor::getStateInformation (MemoryBlock& destData)
{        
    MemoryOutputStream stream(destData, false);
    mState.state.writeToStream (stream);
}

void MyAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    ValueTree tree = ValueTree::readFromData (data, sizeInBytes);
    if (tree.isValid()) {
        mState.state = tree;
        // synchronize here the state with the actual values
        // or even better use the editor as ValueTree::Listener on mState.state
    }
}

Now you can use the state to save whatever you want like:
mState.state.setProperty (“MyGuiValue”, 5, nullptr);

But again, you don’t need this for parameter values.

1 Like

Thanks so much Daniel. I should practice using TreeState class but for now i figured out how to make things work the old way. similar to gain example:

getStateinformation:
MemoryOutputStream (destData, true).writeFloat (*Parameter1);
settStateinformation:
Parameter1->setValueNotifyingHost (MemoryInputStream (data, static_cast<size_t> (sizeInBytes), false).readFloat());

My apologies, I completely forgot, that some hosts don’t save automated values. I once discovered, when I saved my values as you do it here, that they became redundant with the host’s information.
Using AudioProcessorValueTreeState takes all these problems away, so I didnt think of that.
I use atm only one host (ProTools), so I didn’t care about simple hosts - my bad.

How about my second question?
How can i update a UI element inside AudioProcessor ? Say i have a label that i want its text to change only if a condition is true. I don’t need to use a timer and waste resources.
We access AudioProcessor objects from editor (processor.X =0) using something like MyProjectAudioProcessor& processor;
can we do the opposite for editor too?

You can just add a check in the timerCallback() so it only updates the UI element when something changes? It’s probably not the best way, but it might work for you.

I have a question about the AudioProcessorValueTreeState: To make it work with my code I’d ideally like to retrieve the parameter ID by iterating over a list of parameters in my editor constructor. There doesn’t seem to be an obvious way to do this? I want the number of parameters to dictate the number of sliders added to the editor, in a similar way to the “Adding plug-in parameters” tutorial. But it appears the attachment class wants the parameterID passed in when I connect it to the slider.

My current code looks like this:

const OwnedArray<AudioProcessorParameter>& params = processor.getParameters();

for (i=0; i<params.size(); i++)
{
    if (const AudioParameterFloat* param = dynamic_cast<AudioParameterFloat*> (params[i]))
    {
        if (!param->name.contains("Type"))
        {
            ScopedPointer<Slider> aSlider;
            ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> sliderAttachment;

            paramSliders.add(aSlider = new Slider());
            themeSlider(aSlider);
            sliderAttachments.add(sliderAttachment = new AudioProcessorValueTreeState::SliderAttachment(valueTreeState, , *aSlider));
        }
        else
        {
            ScopedPointer<ComboBox> filtCombo;
            ScopedPointer<AudioProcessorValueTreeState::ComboBoxAttachment> comboAttachment;

            filterTypeCombos.add(filtCombo = new ComboBox(param->name));
            themeCombo(filtCombo);
            comboAttachments.add(comboAttachment = new AudioProcessorValueTreeState::ComboBoxAttachment(valueTreeState, param->paramID, *filtCombo));
        }
       }
    }

This doesn’t work as the conditional check never seems to return true in this case with AudioProcessorValueTreeState. This seemed to work fine when I used the old AudioParameterFloat tutorial method of creating the parameters.

I think if I could get the param ID from the AudioProcessorValueTreeState I could do something along the lines of:

for (auto* param: params)
{
        if (!param->getName.contains("Type"))
        {
            ScopedPointer<Slider> aSlider;
            ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> sliderAttachment;

            paramSliders.add(aSlider = new Slider());
            themeSlider(aSlider);
            sliderAttachments.add(sliderAttachment = new AudioProcessorValueTreeState::SliderAttachment(valueTreeState, , *aSlider));
        }
        else
        {
            ScopedPointer<ComboBox> filtCombo;
            ScopedPointer<AudioProcessorValueTreeState::ComboBoxAttachment> comboAttachment;

            filterTypeCombos.add(filtCombo = new ComboBox(param->name));
            themeCombo(filtCombo);
            comboAttachments.add(comboAttachment = new AudioProcessorValueTreeState::ComboBoxAttachment(valueTreeState, param->paramID, *filtCombo));
        }
}

Does anyone have any advice on using the ValueTreeState in this way? I don’t want to have to write out lines of code to set every parameter ID manually in the editor for each slider…

This is failing because AudioProcessorValueTreeState does not use AudioParameterFloat internally - it uses AudioProcessorParameterWithID. Just cast to AudioProcessorParameterWithID instead and your first code should work.

1 Like