AudioParameterFloat taking abstract NormalisableRange parent class ptr instead of a NormalisableRange object

Right now you are stuck with the ValueRemapFunctions, which are really annoying whenever your additional Range behaviour needs additional member variables or subclassing for some other reason.

Example:
Instead of subclassing the (right now not existing) abstract normalisableRange parent class, I need to generate a normalisableRange from my other range implementation:

//in my norm range class

juce::NormalisableRange<FloatType> getNormRange(float start, float end, float interval)
    {
        auto fromNorm = [this] (float start, float end, float value) {
            return this->xToY (value);
        };
        auto toNorm = [this] (float start, float end, float value) {
            return this->yToX (value);
        };

        juce::NormalisableRange<float> range (start, end,{ fromNorm }, { toNorm });
        range.interval = interval;
        return range;
    }

The problem is, that now my other object must be alive during the life time of the normalisableRange object.
Creating a new object of my range class in every ValueRemapFunction would be way too expensive for per-sample host automation.
Using static would maybe work, but then I have to instantiate my class three times instead of one time + I want to avoid static as much as possible when dealing with audio plugins.

Any thoughts on this? Am I missing something?

You don’t need statics or additional members for your mapping functions.
Since they are std::functions you can capture a member of your processor by reference. If this leads to a great UX is a different question, but it should solve your use case.

Neglecting that JUCE doesn’t implement per-sample automation anyway, by making it abstract you are adding the overhead of a virtual call. However I don’t know what is worse: a virtual call or calling a std::function…

2 Likes

I feel like these are workarounds and the abstract class would be the OO way of doing it

I learned OO with Smalltalk ST-80. so I feel like you. However, in performance critical places it is better to minimise the use of OO.
But like I mentioned above, std::function might be worse.

I have no conclusive opinion if an abstract NormalisableRange is a good idea or not.

i think there are some pretty good examples of parameter types where these functions depend on other parameter states and stuff. for example:

  1. a delay’s or lfo’s rate parameter that depends on the state of the temposync switch to define if it paints its values in hz or beats.

  2. a panning parameter that paints L and R at -1 and 1, but only if the plugin is not in mid/side mode rn, in which case it’s M and S ofc.

  3. a pitch parameter where inputting a frequency value (400hz) evaluates to different pitches according to the state of the tuning parameters (master tune, base pitch, xen scale etc)

so what i usually do in these cases:

either i discard the idea entirely because it turns out not being useful anyway (like with the rate parameters that could also just be 2 individual rate parameters where only one is visible in the gui),
or i put state into the processor as a member or reference other parameters in those functions (like the panning one).