I implemented my own parameter system on top of Juce 3 that handled non-linear and linear mappings, text to value and value to text and booleans and enums etc for the various plugin formats. I’m updating to Juce 7 and can see that progress has been made here, but there are still a few missing bits:
Right now AudioProcessorParameter has isBoolean, and looks to be correctly handled.
The only problem I can see is there doesn’t any distinction between an Integer, and an Enum. An Integer I consider as a regular counting number that you would not want to select from combo box / popup menu, for example the number of samples delay for a plugin for a plugin that aligns audio. An Enum I consider to be something I’d like to pick from a combo box / popup menu, for example a filter type of low, band, high, notch etc.
It looks like right now anything that returns true from isDiscrete() is considered an Enum, which means that possibly thousands of strings will be created inside functions like:
StringArray AudioProcessorParameter::getAllValueStrings() const
if (isDiscrete() && valueStrings.isEmpty())
auto maxIndex = getNumSteps() - 1;
for (int i = 0; i < getNumSteps(); ++i)
valueStrings.add (getText ((float) i / (float) maxIndex, 1024));
Currently the function isDiscrete() is meant to indicate that the parameter is exposed as being quantised to the host, which would be true for an integral parameter, so there is another function needed to indicate that a list of strings is needed:
Hey Andy, there’s a whole bunch of classes now in JUCE that derive from AudioProcessorParameter that handle ints, floats bools and choices. And for the numbers that includes ranged parameters supporting skew and custom handling using lambda fuctions. There are also attachment classes for linking to corresponding UI controls.
If it’s not worth moving from your existing code then you could probably do worse than look at the source for AudioParameterChoice to see what’s going on.
Thanks for the reply’s trying to help, but when I post about something it’s because actual changes are needed in Juce, not because of some easy layering on top of Juce which I already did around 10 years ago. I’m sick of having to constantly alter Juce to make it work properly every time I update.
Let me open the hood a little more so you guys can understand what I’m actually talking about. For example here is the inside the audio unit wrapper:
parameterGroups = juceFilter->getParameterTree().getSubgroups (true);
for (auto* param : juceParameters)
OwnedArray<const __CFString>* stringValues = nullptr;
auto initialValue = param->getValue();
bool paramIsLegacy = dynamic_cast<LegacyAudioParameter*> (param) != nullptr;
if (param->isDiscrete() && (! forceUseLegacyParamIDs))
const auto numSteps = param->getNumSteps();
stringValues = new OwnedArray<const __CFString>();
const auto maxValue = getMaximumParameterValue (param);
for (int i = 0; i < numSteps; ++i)
auto value = (float) i / maxValue;
stringValues->add (CFStringCreateCopy (nullptr, (getTextValue (value).toCFString())));
if ((bypassParam = juceFilter->getBypassParameter()) != nullptr)
This code rebuilds the list of strings based on not checking in on the StringArray virtual function overload to determine if should build an enum list or not, it instead uses the isDiscrete() flag, which based on the comments in code should be called something like isQuantizedInHost():
/** Returns whether the parameter uses discrete values, based on the result of
getNumSteps, or allows the host to select values continuously.
This information may or may not be used, depending on the host. If you
want the host to display stepped automation values, rather than a continuous
interpolation between successive values, override this method to return true.
virtual bool isDiscrete() const;
I’m not seeing anywhere here that isDiscrete → isEnum, but that’s how it’s being used, incorrectly. To remind you of the actual issue: the case I’m talking about is a discrete Integer vs discrete Enum, they are not the same. The integer does not need a combo box / popup menu for each and every number, but something that is an Enum does.
If you look at the implementation of AudioParameterInt, you’ll see that it doesn’t override isDiscrete(), so it returns false. I don’t know about AU, but in VST3 it’s not possible to distinguish a list of numbers from any other list. You just have ParameterInfo::stepCount:
Anything with stepCount > 0 shows a list in Cubase, which is filled by calling getParamStringByValue on all possible values. Returning true from isDiscrete() has the effect of setting stepCount, so what you’re actually telling the host is “this is a list of values”. If you don’t want that, you have to return false, and set a normalisable range with an interval of 1, like AudioParameterInt does. You can’t have stepped automation lines without lists of values. There are a number of plugins out there which seem to have this confused, to this unfortunate effect:
Thanks kamedin for understanding what I’m talking about. The quantised dB values are a great example of this going wrong, thanks for posting this - it’s a perfect example of how the flawed comment in the Juce code and oddly named isDiscrete function have lead to issues for plugin developers.
It sounds like the best workaround for now is just to rename isDiscrete, to isEnum, as this is what it’s doing. I’m fine with quantising a normalised float to whatever range int internally if DAWs are too challenged to support this. I doubt the person wanted quantised dB values would have returned true to isEnum(), and so this mess would have been avoided.
We understood what you were talking about, that’s how we were able to offer you the right solution using modern best practices. Yet you insist on reinventing wheels.
What you keep calling an enum parameter is what JUCE calls juce::AudioParameterChoice. If you want a parameter that uses numeric, whole numbers JUCE also has juce::AudioParameterInt, as @Nitsuj70 pointed out.
There’s really no reason to create your own derivatives of juce::AudioProcessorParameter unless for some reason you need something other than Bool, Choice, Float, or Int - but I doubt any host supports anything other than those.
There are many reasons to derive from AudioProcessorParameter’s. My main reason is they didn’t exist in Juce 2 when I wrote my all my classes. It’s great that Juce has finally caught up with most of what I’ve been doing for a long time. Trying to extract parts of what I wrote around 15 years ago to co-exist with what has more recently been added to Juce just to use the Juce classes is actually more work than just using the underlying AudioProcessorParameter classes with a thin wrapper - as long as that class has clearly documented and named methods!
Additionally, it looks like slapping in a AudioParameterChoice would also break my current plugins, because the mapping used does not have equal range at the endpoints. I’ll start a new thread to point out the issues with the current default implementation.