How can I change the name of parameters dynamically?

According to multiple threads, it should be possible to change the names of plugin parameters dynamically (I’ve learned from my research that it is not possible to change the number of parameters). The problem is I can’t actually figure out the code way to do this.

I have a juce::AudioProcessorValueTreeState called parameters in my processor. I can call getParameter, which gets me a RangedAudioParameter * pointer, but the name of these objects seems to be a const that can’t be changed.

Am I missing something? How are people actually changing parameter names. Any actually code would be greatly appreciated.

not sure if you can really just change the name, since, as you already mentioned, it’s const. but you can totally just define any behaviour in the lambda that draws the value to the screen as string in the daw. say you have an lfo’s rate parameter and a switch for free/tempo sync (i suppose the most prominent example for this problem?), you’d just put a reference to the switch’s parameter into the string lambda of the rate-parameter and say if it’s value > .5 it will draw the temposync-related values, else the free-running ones.

edit: however, considering that you are open to question your design-decision: while testing different delays and other stuff with such parameters, i did find that most plugins implement it like that. so i also decided to implement it like that in my current project and did so successfully. but after a while i realized that it might not have been worth the hassle. the idea was that if the rate-parameter can be free or sync, people would only need one automation lane to switch between free and sync dynamically. the alternative is that there are always individual free- and sync-rate parameters and only one of them is visible. but i realized that 1. no one ever automates the free/sync switch anyway and 2. if anyone really automated the free/sync switch they’d probably want to immediatly change the new rate-value that results from that anyway, so it’s not bad that it requires a 3rd automation lane to do so. that’s why i now think maybe not only code-readability but even user workflow might profit from not going the sophisticated route.

The wrappers will query AudioProcessorParameter::getName to find the parameter name to display in the host parameter list. To notify the host that it should rescan the parameter names, you can call

const auto details = AudioProcessorListener::ChangeDetails{}.withParameterInfoChanged (true);
processor.updateHostDisplay (details);

As long as AudioProcessorParameter::getName returns new values, the parameter names should then update in the host.

You correctly pointed out that AudioProcessorParameterWithID::name is const, so it’s not possible to change it directly. Instead, you would need to create a parameter type that overrides getName. This type doesn’t need to derive directly from AudioProcessorParameter - it could derive from AudioProcessorValueTreeState::Parameter, AudioParameterFloat etc.

1 Like

Ah, I see. So the solution is maybe to subclass the parameter type I need and change it so that getName() returns a different value as needed. Thanks!

Instead of subclassing all the parameters you can template the parameters:

template<typename Parameter>
class Renameable : public Parameter
{
public:
    template<typename... Args>
    Renameable (Args&&... args) : Parameter (std::forward<Args>(args)...) {}

    juce::String getName (int maximumStringLength) const override 
    { 
        return name.substring (0, maximumStringLength); 
    }

    void setName (const juce::String& newName) 
    { 
        name = newName; 
    }

    void setNameNotifyingHost (const juce::String& newName, juce::AudioProcessor& processor) 
    { 
        const auto details = AudioProcessorListener::ChangeDetails{}.withParameterInfoChanged (true);

        setName (newName);
        processor.updateHostDisplay (details);
    }

private:
    juce::String name;
};

// use it:
Renameable<juce::AudioParameterFloat> gain { /* ... */ };
Renameable<juce::AudioParameterBool> option { /* ... */  };

That avoids writing multiple subclasses

11 Likes

Thanks so much. This worked for me!

You should also think about protecting the name field from multi-threaded access. The juce::String class is not threadsafe and we just came across a rare crash that is caused by the host trying to access parameter names while we’re updating them on a different thread. I think a simple ReadWriteLock will be enough to fix the issue without much of a performance hit.

if you are prepared to accept a hard limit on the length of the string, you can store it in a fixed array (that is never reallocated. This is immune to crashing. You might get the occasional glitchy string (while one thread is updating it and the other is reading it). Just be sure to reserve one byte at the end that is always null, so you never get into the situation where the string is not null-terminated (e.g. while you are overwriting a short string with a longer one).

1 Like