Plugin presets

Hello,
I was wandering what is the best way to handle the manipulation of plug-in presets.

My goal is to have 4 buttons for CRUD (Create - Read - Update - Delete) on plug-in presets.

After adding a plug-in on a track, how can I get its presets? Do I only look in the directory that stores them?

Also, how can I create my own parameter presets?

There’s no built in preset management in Tracktion Engine, you can deal with that however you like.
The only thing you need to do in the Engine is set the Plugin’s state via the base class AutomatableEditItem::restorePluginStateFromValueTree (const juce::ValueTree&).

You get the current preset by just copying its state. You might need to call AutomatableEditItem::flushPluginStateToValueTree if its an ExternalPlugin or a subclass that doesn’t automatically update the state.

Hope that helps.

1 Like

Yes this helped me.
Just one question :
I’ve an External parameter (VST3) and when i call plugin->flushPluginStateToValueTree(...) and save the plugin->state to a file ( file.preset ) and then i load this file and call plugin->restorePluginStateFromValueTree (ValueTree::fromXml(file.toXmlString()) all parameters is setted to 0.0.
Any hints for this problem ?

The file i saved has this form :

<PRESET name="p1" tags="Plugin" path="C:\***\***\***\***\***\***\VSTPlugin"
        filename="preset_file.preset">
  <PLUGIN type="vst" uniqueId="65593902" uid="577a572f" filename="C:\***\VSTPlugin.vst3"
          name="VSTPlugin" manufacturer="VSTPlugin"
          windowLocked="1" id="65588" enabled="1"
state="1015.VMjLg39....O+fWarAhckI2bo8la8HRLt.iHfTlai8FYo41Y8HRUTYTK3HxO9.BOVMEUy.Ea0cVZtMEcgQWY9vSRC8Vav8lak4Fc9XyM23hUMczXWEjKt3hYt3hKt.kKt3hKt3BS5gEcyQjKtfDToYTR5AkaA4hKtfjYhsVVE0jKH4RPt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKt3hKAQUcUMTRSgjcF4hKtXWdOMCLFElYXcUV30zUZUGMV8DZDk1R1gjPHsFMwfUcQYkVzMlUOgFUEUkQvHjSncSZOYlcCM1Y2YDRoUUahYWRxDVaIcEVyASZHYGRBgTLUwlX4sVLgQGLogDdyHDSnMyPOYWRxDVaIcEVy0TaOciKsIVciwlXmAiQHsVVrk0ZMYzX3UEaisVRsgUczX0SnQTZKYGRBgzZYwVVq0jQigWUrM1ZI0FVmASLgACMFMFNHIDSzgzPLIiYS0DMLkVSxPTdLgGQC4TLpkFRlQEaYwVUwfkdIcUVwTEahgVSWoUazXEVxUjUgUWUsEldvjFR2MiPLglKRkEaYYUVoEUahsVVWkEdIYTVqcmUXQCLogjcyHDSn4hTYwVVVkUZQ0lXqk0UYgWRwH1aucUV3fjPLQGQ40DLhMjS2gzTMglKRkEaYYUVoEUahsVVWkEdIYTX0M1UOglKoszchMUSxX1TLgGUogjYTwVVrUULXoWRWkULUwlXnclUZ01YV8DZtj1R3wzTMkmZCwTLLkGS4A0PMECUCwTdHIDRqkEaYsVSFM1b3XTVmcmQi8FNrEVdAcUVqEkUOglKosTdHMUSzPTZMYGRowjcXkFS44xTLECRBgzZYwVVq0jQioVUFIldmY0Sn4RZKkGQo0jdtjVS3QUZHYFUrkEaUECV5EkUZwVVVMVdUY0SnQTZKYGRBgjcIISXskzUXMGMVg0bUY0SnAEUYwVQVMlbQ0FR0MyPOUmKsIVciwlXmASLhkic4sjdEYTX43hKt3hKt3hKt3hKt3FUUMTUDQEdqw1XmE0UYQTQFM1YAAkKAgDUjYWQwHVdAAkKAwjKtLlKt3hKt3hKt3hYRUUSTEETIckVwTjQisVTTgkdEYDOujzPu0Fbu4VYtQmO77hUSQ0LPwVcmklaSQWXzUlO.."
          base64:layout="99.LETVOUEU..P.BjjSPUEUSA..ADfPUME.AHPZtQVY3AP.ED......rEVduUGc.DPAEvDHRA..OUEUPUEUSA..ADfPUME.AHPZtQVY3AP.ED......rEVduUGc.DPAEvDHRA..">>
    <MACROPARAMETERS id="65589"/>
    <MODIFIERASSIGNMENTS/>
  </PLUGIN>
</PRESET>

You’ll need to pull out your PLUGIN node as ExternalPlugin doesn’t know anything about your PRESET node.

Sorry i did not write it.
When i load the file.preset i call presetVt.getChildWithName(ā€œPLUGINā€) and so to the plugin->restorePluginStateFromValueTree(...) i pass the PLUGIN vt and not the PRESET

Can you step through ExternalPlugin::restorePluginStateFromValueTree, it should get the plugin state out of the state property:

    if (v.hasProperty (IDs::state))
    {
        s = v.getProperty (IDs::state).toString();

Then get to the section at the bottom:

pluginInstance->setStateInformation

If not, where does it fail?

Yes this part of code is covered , it gets the state and then call the setStateInformation via the callBlock(…) method for each chunk.
it seems like that when i call the plugin->flushPluginStateToValueTree(...) the current aut param values are not stored or something like that…

In the specific the plugin->flushPluginStateToValueTree(...) calls this method :

void AutomatableEditItem::saveChangedParametersToState()
{
    juce::MemoryOutputStream stream;

    for (auto ap : automatableParams)
    {
        if (ap->getCurrentValue() != ap->getCurrentExplicitValue())
        {
            stream.writeString (ap->paramID);
            stream.writeFloat (ap->getCurrentExplicitValue());
        }
    }

    stream.flush();
    auto um = &edit.getUndoManager();

    if (stream.getDataSize() > 0)
        elementState.setProperty (IDs::parameters, stream.getMemoryBlock(), um);
    else
        elementState.removeProperty (IDs::parameters, um);
}

and this compairson if (ap->getCurrentValue() != ap->getCurrentExplicitValue()) is always false… maybe this could be the problem ?

This section is only if you’re using Modifiers or Macros. Are you using those?

After called the restorePluginStateFromValueTree(...) a strage thing happens…
When i call autParam->getCurrentValueAsString() it returns the correct value ( 7 Hz for example ) while calling autParam->getCurrentValue() it returns always 0… how does it possibile ?

This is the stack calls :

The value passed in the currentValueChanged(AutomatableParameter&ap , flaot value ) is always 0.
I use this callback to set up sliders and other stuffs…

anything yet about this issue?

Can you step in to getCurrentValueAsString? Is it calling AutomatableParameter::getCurrentValueAsString or ExternalAutomatableParameter::getCurrentValueAsString?

Did you see my comment here?

I need to know the answer from this in order to advise further.

Sorry dave,

I’m not using any macros or modifiers.

Actually the ExternalAutomatableParameter::getCurrentValueAsString is called

juce::String getCurrentValueAsString() override
    {
        if (auto p = getParam())
            return p->getCurrentValueAsText();

        return {};
    }
String AudioProcessorParameter::getCurrentValueAsText() const
{
    return getText (getValue(), 1024);
}

Ok, that’s what I thought, it’s calling directly in to the plugin, not converting the ā€œcurrent valueā€ to a string.

I think the question that needs answering is why this:

void AutomatableParameter::setParameter (float value, juce::NotificationType nt)
{
    currentParameterValue = value;
    setParameterValue (value, false);

fails.

E.g. why does the call to setParameterValue not end up in your plugin setting the parameter to the same value as currentParameterValue? Are you doing some internal scaling or something?

No, I am not doing anything like that.

The problem seems to be upstream, let me explain :
After calling restorePluginFromState(…) there is this stack of calls :

Now the problem is that the value passed as a parameter turns out to be always 0 , as you can see from the picture , while the current value is correct

That value is passed by this method from the ExternalAutomatableParameter

void valueChangedByPlugin()
    {
        if (auto p = getParam())
            setParameter (p->getValue(), juce::sendNotification);
    }

that calls getValue() from VST3Parameter :

float getValue() const override
        {
            return pluginInstance.cachedParamValues.get (vstParamIndex);
        }

That has all values setted to 0…

Then is sounds like pluginInstance.cachedParamValues.get (vstParamIndex); is incorrectly all 0s?
Shouldn’t they be the last values set during the set-state operation?

It sounds like the juce::VST3PluginInstance::VST3Parameter isn’t being updated at the correct time (i.e. when setStateInformation is called on the plugin instance?

Can you perhaps step through juce::VST3PluginInstance::setStateInformation and see why either resetParameters() isn’t being called or why the values are all 0 if it is being called?

I’m at ADC this week so can’t really look in to it myself I’m afraid.

The resetParameters() method is called and the value of the parameters is correct !

But then this method is called :

void refreshParameterList() override
    {
        AudioProcessorParameterGroup newParameterTree;

        // We're going to add parameter groups to the tree recursively in the same order as the
        // first parameters contained within them.
        std::map<Vst::UnitID, Vst::UnitInfo> infoMap;
        std::map<Vst::UnitID, AudioProcessorParameterGroup*> groupMap;
        groupMap[Vst::kRootUnitId] = &newParameterTree;

        if (unitInfo != nullptr)
        {
            const auto numUnits = unitInfo->getUnitCount();

            for (int i = 1; i < numUnits; ++i)
            {
                Vst::UnitInfo ui{};
                unitInfo->getUnitInfo (i, ui);
                infoMap[ui.id] = std::move (ui);
            }
        }

        {
            auto allIds = getAllParamIDs (*editController);
            inputParameterChanges ->initialise (allIds);
            outputParameterChanges->initialise (allIds);
            cachedParamValues = CachedParamValues { std::move (allIds) };
        }

and here cachedParamValues = CachedParamValues { std::move (allIds) }; the cachedParamValues is setted to all zero and then these values are passed to the parameterChanged(…) callback.

The parameter in the native plugin UI update correctly their values… but when the callback is called ( with wich i can update my sandBox ) they are always 0.
I’ve added even an async updater but the problem is still present.

I think this sounds like a JUCE bug then. I don’t think refreshParameterList() should be clearing the parameter values if they already exist. @reuk, is this new behaviour since the introduction of the VST3 parameter cache?

From reading the conversation, it sounds like we’d expect something like this to work:

std::vector<float> values;

for (auto* param : processor->getParameters())
    values.push_back (param->getValue());

processor->refreshParameterList();

for (auto* param : processor->getParameters())
    jassert (param->getValue() == values[param->getParameterIndex()]);

Have I understood the problem?

If so, it looks like this did indeed work before CachedParamValues was added, so this seems to be a regression. (This might be a good Pluginval test!)

Having said that, I don’t really understand why refreshParameters() is implemented at all for VST3 plugins. The VST3 format disallows adding/removing parameters at runtime, so this only seems useful if the parameter unitIDs may change at runtime (i.e. the tree structure changes), but it’s not clear to me whether this is allowed by the spec. There’s no ā€˜unit IDs changed’ flag in the VST3 RestartFlags, so I suspect this is not allowed.

@dave96 do you happen to know of any VST3 plug-ins for which refreshParameterList should actually change the parameter values/layout? If not, I’m leaning towards turning refreshParameterList into a no-op for VST3 plug-ins.

Yes, I think that’s the test case that should pass (and yes, would make a good addition to pluginval :wink:).

I think we call refreshParameters as perhaps that was required for changed names (as can happen with macro parameters). If in the VST3 impl that happens without refreshing the params I think you’re right and it could be a no-op.

1 Like

The new refreshParameterList behaviour was added here:

1 Like