1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioProcessorParameter: Add getVersionHint function

This commit is contained in:
reuk 2022-01-24 15:07:34 +00:00
parent 5186ac71e2
commit 7068e70758
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
17 changed files with 118 additions and 39 deletions

View file

@ -1492,8 +1492,6 @@ void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioPro
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
//==============================================================================
AudioProcessorParameter::AudioProcessorParameter() noexcept {}
AudioProcessorParameter::~AudioProcessorParameter()
{
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING

View file

@ -39,7 +39,58 @@ class AudioProcessor;
class JUCE_API AudioProcessorParameter
{
public:
AudioProcessorParameter() noexcept;
AudioProcessorParameter() noexcept = default;
/** The version hint supplied to this constructor is used in Audio Unit plugins to aid ordering
parameter identifiers when JUCE_FORCE_USE_LEGACY_PARAM_IDS is not enabled.
When adding a parameter that is not present in a previous version of the Audio Unit, you
must ensure that the version hint supplied is a number higher than that of any parameter in
any previous plugin version.
For example, in the first release of a plugin, every parameter was created with "1" as a
version hint. If you add some parameters in the second release of the plugin, all of the
new parameters should have "2" as a version hint. Additional parameters added in subsequent
plugin versions should have "3", "4", and so forth, increasing monotonically.
Note that adding or removing parameters with a version hint that is lower than the maximum
version hint of all parameters will break saved automation in some hosts, so be careful!
A version hint of "0" will be treated as though the version hint has not been set
explicitly. When targeting the AU format, the version hint may be checked at runtime in
debug builds to ensure that it has been set.
Rationale:
According to <a href="https://developer.apple.com/documentation/audiotoolbox/audiounitparameter?language=objc">Apple's Documentation</a>:
> An audio unit parameter is uniquely identified by the combination of its scope, element, and ID.
However, Logic Pro and GarageBand have a known limitation that causes them to use parameter
indices instead of IDs to identify parameters. The effect of this is that adding parameters
to a later version of a plugin can break automation saved with an earlier version of the
plugin if the indices of existing parameters are changed. It is *always* unsafe to remove
parameters from an Audio Unit plugin that will be used in one of these hosts, because
removing a parameter will always modify the indices of following parameters.
In order to work around this limitation, parameters in AUv2 plugins are sorted first by
their version hint, and then by the hash of their string identifier. As long as the
parameters from later versions of the plugin always have a version hint that is higher than
the parameters from earlier versions of the plugin, recall of automation data will work as
expected in Logic and GarageBand.
Note that we can't just use the JUCE parameter index directly in order to preserve ordering.
This would require all new parameters to be added at the end of the parameter list, which
would make it impossible to add parameters to existing parameter groups. It would also make
it awkward to structure code sensibly, undoing all of the benefits of string-based parameter
identifiers.
At time of writing, AUv3 plugins seem to be affected by the same issue, but there does not
appear to be any API to control parameter indices in this format. Therefore, when building
AUv3 plugins you must not add or remove parameters in subsequent plugin versions if you
wish to support Logic and GarageBand.
*/
explicit AudioProcessorParameter (int versionHint)
: version (versionHint) {}
/** Destructor. */
virtual ~AudioProcessorParameter();
@ -224,6 +275,10 @@ public:
*/
virtual StringArray getAllValueStrings() const;
//==============================================================================
/** @see AudioProcessorParameter(int) */
int getVersionHint() const { return version; }
//==============================================================================
/**
A base class for listeners that want to know about changes to an
@ -291,6 +346,7 @@ private:
friend class LegacyAudioParameter;
AudioProcessor* processor = nullptr;
int parameterIndex = -1;
int version = 0;
CriticalSection listenerLock;
Array<Listener*> listeners;
mutable StringArray valueStrings;

View file

@ -34,6 +34,8 @@ namespace juce
*/
struct HostedAudioProcessorParameter : public AudioProcessorParameter
{
using AudioProcessorParameter::AudioProcessorParameter;
/** Returns an ID that is unique to this parameter.
Parameter indices are unstable across plugin versions, which means that the

View file

@ -26,11 +26,14 @@
namespace juce
{
AudioParameterBool::AudioParameterBool (const String& idToUse, const String& nameToUse,
bool def, const String& labelToUse,
AudioParameterBool::AudioParameterBool (const String& idToUse,
const String& nameToUse,
bool def,
const String& labelToUse,
std::function<String (bool, int)> stringFromBool,
std::function<bool (const String&)> boolFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse),
std::function<bool (const String&)> boolFromString,
int versionHintToUse)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse),
value (def ? 1.0f : 0.0f),
defaultValue (value),
stringFromBoolFunction (stringFromBool),

View file

@ -48,11 +48,13 @@ public:
@param boolFromString An optional lambda function that parses a string and
converts it into a bool value. Some hosts use this
to allow users to type in parameter values.
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
AudioParameterBool (const String& parameterID, const String& parameterName, bool defaultValue,
const String& parameterLabel = String(),
std::function<String (bool value, int maximumStringLength)> stringFromBool = nullptr,
std::function<bool (const String& text)> boolFromString = nullptr);
std::function<bool (const String& text)> boolFromString = nullptr,
int versionHint = 0);
/** Destructor. */
~AudioParameterBool() override;

View file

@ -29,8 +29,10 @@ namespace juce
AudioParameterChoice::AudioParameterChoice (const String& idToUse, const String& nameToUse,
const StringArray& c, int def, const String& labelToUse,
std::function<String (int, int)> stringFromIndex,
std::function<int (const String&)> indexFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse), choices (c),
std::function<int (const String&)> indexFromString,
int versionHintToUse)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse),
choices (c),
range ([this]
{
NormalisableRange<float> rangeWithInterval { 0.0f, (float) choices.size() - 1.0f,

View file

@ -50,13 +50,15 @@ public:
@param indexFromString An optional lambda function that parses a string and
converts it into a choice index. Some hosts use this
to allow users to type in parameter values.
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
AudioParameterChoice (const String& parameterID, const String& parameterName,
const StringArray& choices,
int defaultItemIndex,
const String& parameterLabel = String(),
std::function<String (int index, int maximumStringLength)> stringFromIndex = nullptr,
std::function<int (const String& text)> indexFromString = nullptr);
std::function<int (const String& text)> indexFromString = nullptr,
int versionHint = 0);
/** Destructor. */
~AudioParameterChoice() override;

View file

@ -30,8 +30,9 @@ AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& n
NormalisableRange<float> r, float def,
const String& labelToUse, Category categoryToUse,
std::function<String (float, int)> stringFromValue,
std::function<float (const String&)> valueFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, categoryToUse),
std::function<float (const String&)> valueFromString,
int versionHintToUse)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, categoryToUse, versionHintToUse),
range (r), value (def), defaultValue (def),
stringFromValueFunction (stringFromValue),
valueFromStringFunction (valueFromString)

View file

@ -51,6 +51,7 @@ public:
@param valueFromString An optional lambda function that parses a string and
converts it into a non-normalised value. Some hosts use
this to allow users to type in parameter values.
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
AudioParameterFloat (const String& parameterID,
const String& parameterName,
@ -59,7 +60,8 @@ public:
const String& parameterLabel = String(),
Category parameterCategory = AudioProcessorParameter::genericParameter,
std::function<String (float value, int maximumStringLength)> stringFromValue = nullptr,
std::function<float (const String& text)> valueFromString = nullptr);
std::function<float (const String& text)> valueFromString = nullptr,
int versionHint = 0);
/** Creates a AudioParameterFloat with an ID, name, and range.
On creation, its value is set to the default value.

View file

@ -30,8 +30,9 @@ AudioParameterInt::AudioParameterInt (const String& idToUse, const String& nameT
int minValue, int maxValue, int def,
const String& labelToUse,
std::function<String (int, int)> stringFromInt,
std::function<int (const String&)> intFromString)
: RangedAudioParameter (idToUse, nameToUse, labelToUse),
std::function<int (const String&)> intFromString,
int versionHintToUse)
: RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse),
range ([minValue, maxValue]
{
NormalisableRange<float> rangeWithInterval { (float) minValue, (float) maxValue,

View file

@ -51,13 +51,15 @@ public:
@param intFromString An optional lambda function that parses a string
and converts it into an int. Some hosts use this
to allow users to type in parameter values.
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
AudioParameterInt (const String& parameterID, const String& parameterName,
int minValue, int maxValue,
int defaultValue,
const String& parameterLabel = String(),
std::function<String (int value, int maximumStringLength)> stringFromInt = nullptr,
std::function<int (const String& text)> intFromString = nullptr);
std::function<int (const String& text)> intFromString = nullptr,
int versionHint = 0);
/** Destructor. */
~AudioParameterInt() override;

View file

@ -29,9 +29,15 @@ namespace juce
AudioProcessorParameterWithID::AudioProcessorParameterWithID (const String& idToUse,
const String& nameToUse,
const String& labelToUse,
AudioProcessorParameter::Category categoryToUse)
: paramID (idToUse), name (nameToUse), label (labelToUse), category (categoryToUse) {}
AudioProcessorParameterWithID::~AudioProcessorParameterWithID() {}
AudioProcessorParameter::Category categoryToUse,
int versionHintToUse)
: HostedAudioProcessorParameter (versionHintToUse),
paramID (idToUse),
name (nameToUse),
label (labelToUse),
category (categoryToUse)
{
}
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); }
String AudioProcessorParameterWithID::getLabel() const { return label; }

View file

@ -38,14 +38,18 @@ class JUCE_API AudioProcessorParameterWithID : public HostedAudioProcessorPara
public:
/** The creation of this object requires providing a name and ID which will be
constant for its lifetime.
@param parameterID Used to uniquely identify the parameter
@param parameterName The user-facing name of the parameter
@param parameterLabel An optional label for the parameter's value
@param parameterCategory The semantics of this parameter
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
AudioProcessorParameterWithID (const String& parameterID,
const String& parameterName,
const String& parameterLabel = {},
Category parameterCategory = AudioProcessorParameter::genericParameter);
/** Destructor. */
~AudioProcessorParameterWithID() override;
Category parameterCategory = AudioProcessorParameter::genericParameter,
int versionHint = 0);
/** Provides access to the parameter's ID string. */
const String paramID;

View file

@ -38,7 +38,8 @@ AudioProcessorValueTreeState::Parameter::Parameter (const String& parameterID,
bool isAutomatableParameter,
bool isDiscrete,
AudioProcessorParameter::Category parameterCategory,
bool isBoolean)
bool isBoolean,
int versionHintToUse)
: AudioParameterFloat (parameterID,
parameterName,
valueRange,
@ -47,7 +48,8 @@ AudioProcessorValueTreeState::Parameter::Parameter (const String& parameterID,
parameterCategory,
valueToTextFunction == nullptr ? std::function<String (float v, int)>()
: [valueToTextFunction] (float v, int) { return valueToTextFunction (v); },
std::move (textToValueFunction)),
std::move (textToValueFunction),
versionHintToUse),
unsnappedDefault (valueRange.convertTo0to1 (defaultParameterValue)),
metaParameter (isMetaParameter),
automatable (isAutomatableParameter),

View file

@ -409,7 +409,8 @@ public:
bool isAutomatableParameter = true,
bool isDiscrete = false,
AudioProcessorParameter::Category parameterCategory = AudioProcessorParameter::genericParameter,
bool isBoolean = false);
bool isBoolean = false,
int versionHint = 0);
float getDefaultValue() const override;
int getNumSteps() const override;

View file

@ -26,14 +26,6 @@
namespace juce
{
RangedAudioParameter::RangedAudioParameter (const String& parameterID,
const String& parameterName,
const String& parameterLabel,
Category parameterCategory)
: AudioProcessorParameterWithID (parameterID, parameterName, parameterLabel, parameterCategory)
{
}
int RangedAudioParameter::getNumSteps() const
{
const auto& range = getNormalisableRange();

View file

@ -38,11 +38,14 @@ class JUCE_API RangedAudioParameter : public AudioProcessorParameterWithID
public:
/** The creation of this object requires providing a name and ID which will be
constant for its lifetime.
@param parameterID Used to uniquely identify the parameter
@param parameterName The user-facing name of the parameter
@param parameterLabel An optional label for the parameter's value
@param parameterCategory The semantics of this parameter
@param versionHint See AudioProcessorParameter::getVersionHint()
*/
RangedAudioParameter (const String& parameterID,
const String& parameterName,
const String& parameterLabel = {},
Category parameterCategory = AudioProcessorParameter::genericParameter);
using AudioProcessorParameterWithID::AudioProcessorParameterWithID;
/** Returns the range of values that the parameter can take. */
virtual const NormalisableRange<float>& getNormalisableRange() const = 0;