From afe51998483af6b7277af5fffda971883fd442ea Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 1 Feb 2022 22:24:46 +0000 Subject: [PATCH] AudioProcessorParameter: Add new ParameterID and Attributes types --- BREAKING-CHANGES.txt | 28 +++ .../AAX/juce_AAX_Wrapper.cpp | 2 +- .../VST3/juce_VST3_Wrapper.cpp | 2 +- .../utilities/juce_AudioParameterBool.cpp | 69 +++----- .../utilities/juce_AudioParameterBool.h | 50 +++++- .../utilities/juce_AudioParameterChoice.cpp | 26 ++- .../utilities/juce_AudioParameterChoice.h | 51 +++++- .../utilities/juce_AudioParameterFloat.cpp | 25 +-- .../utilities/juce_AudioParameterFloat.h | 56 +++++- .../utilities/juce_AudioParameterInt.cpp | 45 +++-- .../utilities/juce_AudioParameterInt.h | 58 ++++++- .../juce_AudioProcessorParameterWithID.cpp | 17 +- .../juce_AudioProcessorParameterWithID.h | 123 +++++++++++++- .../juce_AudioProcessorValueTreeState.cpp | 159 ++++++++++-------- .../juce_AudioProcessorValueTreeState.h | 116 +++++++++++-- .../utilities/juce_RangedAudioParameter.h | 70 +++++++- modules/juce_core/misc/juce_Functional.h | 16 ++ 17 files changed, 692 insertions(+), 221 deletions(-) diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index 29aa4155ed..396e056563 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -1,6 +1,34 @@ JUCE breaking changes ===================== +develop +======= + +Change +------ +Constructors of AudioParameterBool, AudioParameterChoice, AudioParameterFloat, +AudioParameterInt, and AudioProcessorParameterWithID have been deprecated and +replaced with new constructors taking an 'Attributes' argument. + +Possible Issues +--------------- +The compiler may issue a deprecation warning upon encountering usages of the +old constructors. + +Workaround +---------- +Update code to pass an 'Attributes' instance instead. Example usages of the new +constructors are given in the constructor documentation, and in the plugin +example projects. + +Rationale +--------- +Parameter types have many different properties. Setting a non-default property +using the old constructors required explicitly setting other normally-defaulted +properties, which was redundant. The new Attributes types allow non-default +properties to be set in isolation. + + Version 6.1.6 ============= diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index 67f29bee0b..ff22efdb82 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -1601,7 +1601,7 @@ namespace AAXClasses if (bypassParameter == nullptr) { - ownedBypassParameter.reset (new AudioParameterBool (cDefaultMasterBypassID, "Master Bypass", false, {}, {}, {})); + ownedBypassParameter.reset (new AudioParameterBool (cDefaultMasterBypassID, "Master Bypass", false)); bypassParameter = ownedBypassParameter.get(); } diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 623ca9aef2..8ab3be7e59 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -526,7 +526,7 @@ private: if (bypassParameter == nullptr) { vst3WrapperProvidedBypassParam = true; - ownedBypassParameter.reset (new AudioParameterBool ("byps", "Bypass", false, {}, {}, {})); + ownedBypassParameter.reset (new AudioParameterBool ("byps", "Bypass", false)); bypassParameter = ownedBypassParameter.get(); } diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterBool.cpp b/modules/juce_audio_processors/utilities/juce_AudioParameterBool.cpp index 81592455bb..eb7168f96a 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterBool.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterBool.cpp @@ -26,49 +26,36 @@ namespace juce { -AudioParameterBool::AudioParameterBool (const String& idToUse, +AudioParameterBool::AudioParameterBool (const ParameterID& idToUse, const String& nameToUse, bool def, - const String& labelToUse, - std::function stringFromBool, - std::function boolFromString, - int versionHintToUse) - : RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse), - value (def ? 1.0f : 0.0f), - defaultValue (value), - stringFromBoolFunction (stringFromBool), - boolFromStringFunction (boolFromString) + const AudioParameterBoolAttributes& attributes) + : RangedAudioParameter (idToUse, nameToUse, attributes.getAudioProcessorParameterWithIDAttributes()), + value (def ? 1.0f : 0.0f), + valueDefault (def), + stringFromBoolFunction (attributes.getStringFromValueFunction() != nullptr + ? attributes.getStringFromValueFunction() + : [] (bool v, int) { return v ? TRANS("On") : TRANS("Off"); }), + boolFromStringFunction (attributes.getValueFromStringFunction() != nullptr + ? attributes.getValueFromStringFunction() + : [] (const String& text) + { + static const StringArray onStrings { TRANS ("on"), TRANS ("yes"), TRANS ("true") }; + static const StringArray offStrings { TRANS ("off"), TRANS ("no"), TRANS ("false") }; + + String lowercaseText (text.toLowerCase()); + + for (auto& testText : onStrings) + if (lowercaseText == testText) + return true; + + for (auto& testText : offStrings) + if (lowercaseText == testText) + return false; + + return text.getIntValue() != 0; + }) { - if (stringFromBoolFunction == nullptr) - stringFromBoolFunction = [] (bool v, int) { return v ? TRANS("On") : TRANS("Off"); }; - - if (boolFromStringFunction == nullptr) - { - StringArray onStrings; - onStrings.add (TRANS("on")); - onStrings.add (TRANS("yes")); - onStrings.add (TRANS("true")); - - StringArray offStrings; - offStrings.add (TRANS("off")); - offStrings.add (TRANS("no")); - offStrings.add (TRANS("false")); - - boolFromStringFunction = [onStrings, offStrings] (const String& text) - { - String lowercaseText (text.toLowerCase()); - - for (auto& testText : onStrings) - if (lowercaseText == testText) - return true; - - for (auto& testText : offStrings) - if (lowercaseText == testText) - return false; - - return text.getIntValue() != 0; - }; - } } AudioParameterBool::~AudioParameterBool() @@ -81,7 +68,7 @@ AudioParameterBool::~AudioParameterBool() float AudioParameterBool::getValue() const { return value; } void AudioParameterBool::setValue (float newValue) { value = newValue; valueChanged (get()); } -float AudioParameterBool::getDefaultValue() const { return defaultValue; } +float AudioParameterBool::getDefaultValue() const { return valueDefault; } int AudioParameterBool::getNumSteps() const { return 2; } bool AudioParameterBool::isDiscrete() const { return true; } bool AudioParameterBool::isBoolean() const { return true; } diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h b/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h index 18351798cb..54b2de5c63 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h @@ -26,6 +26,13 @@ namespace juce { +/** Properties of an AudioParameterBool. + + @see AudioParameterBool(), RangedAudioParameterAttributes() +*/ +class AudioParameterBoolAttributes : public RangedAudioParameterAttributes {}; + +//============================================================================== /** Provides a class of AudioProcessorParameter that can be used as a boolean value. @@ -36,6 +43,28 @@ namespace juce class JUCE_API AudioParameterBool : public RangedAudioParameter { public: + /** Creates a AudioParameterBool with the specified parameters. + + Note that the attributes argument is optional and only needs to be + supplied if you want to change options from their default values. + + Example usage: + @code + auto attributes = AudioParameterBoolAttributes().withStringFromValueFunction ([] (auto x, auto) { return x ? "On" : "Off"; }) + .withLabel ("enabled"); + auto param = std::make_unique ("paramID", "Parameter Name", false, attributes); + @endcode + + @param parameterID The parameter ID to use + @param parameterName The parameter name to use + @param defaultValue The default value + @param attributes Optional characteristics + */ + AudioParameterBool (const ParameterID& parameterID, + const String& parameterName, + bool defaultValue, + const AudioParameterBoolAttributes& attributes = {}); + /** Creates a AudioParameterBool with the specified parameters. @param parameterID The parameter ID to use @@ -48,13 +77,22 @@ 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(), + [[deprecated ("Prefer the signature taking an Attributes argument")]] + AudioParameterBool (const ParameterID& parameterID, + const String& parameterName, + bool defaultValue, + const String& parameterLabel, std::function stringFromBool = nullptr, - std::function boolFromString = nullptr, - int versionHint = 0); + std::function boolFromString = nullptr) + : AudioParameterBool (parameterID, + parameterName, + defaultValue, + AudioParameterBoolAttributes().withLabel (parameterLabel) + .withStringFromValueFunction (std::move (stringFromBool)) + .withValueFromStringFunction (std::move (boolFromString))) + { + } /** Destructor. */ ~AudioParameterBool() override; @@ -90,7 +128,7 @@ private: const NormalisableRange range { 0.0f, 1.0f, 1.0f }; std::atomic value; - const float defaultValue; + const float valueDefault; std::function stringFromBoolFunction; std::function boolFromStringFunction; diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.cpp b/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.cpp index fc7cae08ef..4534ec948b 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.cpp @@ -26,12 +26,12 @@ namespace juce { -AudioParameterChoice::AudioParameterChoice (const String& idToUse, const String& nameToUse, - const StringArray& c, int def, const String& labelToUse, - std::function stringFromIndex, - std::function indexFromString, - int versionHintToUse) - : RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse), +AudioParameterChoice::AudioParameterChoice (const ParameterID& idToUse, + const String& nameToUse, + const StringArray& c, + int def, + const AudioParameterChoiceAttributes& attributes) + : RangedAudioParameter (idToUse, nameToUse, attributes.getAudioProcessorParameterWithIDAttributes()), choices (c), range ([this] { @@ -44,16 +44,14 @@ AudioParameterChoice::AudioParameterChoice (const String& idToUse, const String& }()), value ((float) def), defaultValue (convertTo0to1 ((float) def)), - stringFromIndexFunction (stringFromIndex), - indexFromStringFunction (indexFromString) + stringFromIndexFunction (attributes.getStringFromValueFunction() != nullptr + ? attributes.getStringFromValueFunction() + : [this] (int index, int) { return choices [index]; }), + indexFromStringFunction (attributes.getValueFromStringFunction() != nullptr + ? attributes.getValueFromStringFunction() + : [this] (const String& text) { return choices.indexOf (text); }) { jassert (choices.size() > 1); // you must supply an actual set of items to choose from! - - if (stringFromIndexFunction == nullptr) - stringFromIndexFunction = [this] (int index, int) { return choices [index]; }; - - if (indexFromStringFunction == nullptr) - indexFromStringFunction = [this] (const String& text) { return choices.indexOf (text); }; } AudioParameterChoice::~AudioParameterChoice() diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h b/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h index 7e833a8ed8..134923fd5b 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h @@ -26,6 +26,13 @@ namespace juce { +/** Properties of an AudioParameterChoice. + + @see AudioParameterChoice(), RangedAudioParameterAttributes() +*/ +class AudioParameterChoiceAttributes : public RangedAudioParameterAttributes {}; + +//============================================================================== /** Provides a class of AudioProcessorParameter that can be used to select an indexed, named choice from a list. @@ -39,10 +46,33 @@ class JUCE_API AudioParameterChoice : public RangedAudioParameter public: /** Creates a AudioParameterChoice with the specified parameters. + Note that the attributes argument is optional and only needs to be + supplied if you want to change options from their default values. + + Example usage: + @code + auto attributes = AudioParameterChoiceAttributes().withLabel ("selected"); + auto param = std::make_unique ("paramID", "Parameter Name", StringArray { "a", "b", "c" }, 0, attributes); + @endcode + @param parameterID The parameter ID to use @param parameterName The parameter name to use @param choices The set of choices to use @param defaultItemIndex The index of the default choice + @param attributes Optional characteristics + */ + AudioParameterChoice (const ParameterID& parameterID, + const String& parameterName, + const StringArray& choices, + int defaultItemIndex, + const AudioParameterChoiceAttributes& attributes = {}); + + /** Creates a AudioParameterChoice with the specified parameters. + + @param parameterID The parameter ID to use + @param parameterName The parameter name to use + @param choicesToUse The set of choices to use + @param defaultItemIndex The index of the default choice @param parameterLabel An optional label for the parameter's value @param stringFromIndex An optional lambda function that converts a choice index to a string with a maximum length. This may @@ -50,15 +80,24 @@ 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, + [[deprecated ("Prefer the signature taking an Attributes argument")]] + AudioParameterChoice (const ParameterID& parameterID, + const String& parameterName, + const StringArray& choicesToUse, int defaultItemIndex, - const String& parameterLabel = String(), + const String& parameterLabel, std::function stringFromIndex = nullptr, - std::function indexFromString = nullptr, - int versionHint = 0); + std::function indexFromString = nullptr) + : AudioParameterChoice (parameterID, + parameterName, + choicesToUse, + defaultItemIndex, + AudioParameterChoiceAttributes().withLabel (parameterLabel) + .withStringFromValueFunction (std::move (stringFromIndex)) + .withValueFromStringFunction (std::move (indexFromString))) + { + } /** Destructor. */ ~AudioParameterChoice() override; diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp b/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp index 91dd34176d..75ffe946ca 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.cpp @@ -26,16 +26,17 @@ namespace juce { -AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& nameToUse, - NormalisableRange r, float def, - const String& labelToUse, Category categoryToUse, - std::function stringFromValue, - std::function valueFromString, - int versionHintToUse) - : RangedAudioParameter (idToUse, nameToUse, labelToUse, categoryToUse, versionHintToUse), - range (r), value (def), defaultValue (def), - stringFromValueFunction (stringFromValue), - valueFromStringFunction (valueFromString) +AudioParameterFloat::AudioParameterFloat (const ParameterID& idToUse, + const String& nameToUse, + NormalisableRange r, + float def, + const AudioParameterFloatAttributes& attributes) + : RangedAudioParameter (idToUse, nameToUse, attributes.getAudioProcessorParameterWithIDAttributes()), + range (r), + value (def), + valueDefault (def), + stringFromValueFunction (attributes.getStringFromValueFunction()), + valueFromStringFunction (attributes.getValueFromStringFunction()) { if (stringFromValueFunction == nullptr) { @@ -71,7 +72,7 @@ AudioParameterFloat::AudioParameterFloat (const String& idToUse, const String& n valueFromStringFunction = [] (const String& text) { return text.getFloatValue(); }; } -AudioParameterFloat::AudioParameterFloat (String pid, String nm, float minValue, float maxValue, float def) +AudioParameterFloat::AudioParameterFloat (const ParameterID& pid, const String& nm, float minValue, float maxValue, float def) : AudioParameterFloat (pid, nm, { minValue, maxValue, 0.01f }, def) { } @@ -86,7 +87,7 @@ AudioParameterFloat::~AudioParameterFloat() float AudioParameterFloat::getValue() const { return convertTo0to1 (value); } void AudioParameterFloat::setValue (float newValue) { value = convertFrom0to1 (newValue); valueChanged (get()); } -float AudioParameterFloat::getDefaultValue() const { return convertTo0to1 (defaultValue); } +float AudioParameterFloat::getDefaultValue() const { return convertTo0to1 (valueDefault); } int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); } String AudioParameterFloat::getText (float v, int length) const { return stringFromValueFunction (convertFrom0to1 (v), length); } float AudioParameterFloat::getValueForText (const String& text) const { return convertTo0to1 (valueFromStringFunction (text)); } diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h b/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h index ea3971101a..9e90e48a5b 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h @@ -26,6 +26,13 @@ namespace juce { +/** Properties of an AudioParameterFloat. + + @see AudioParameterFloat(), RangedAudioParameterAttributes() +*/ +class AudioParameterFloatAttributes : public RangedAudioParameterAttributes {}; + +//============================================================================== /** A subclass of AudioProcessorParameter that provides an easy way to create a parameter which maps onto a given NormalisableRange. @@ -37,6 +44,30 @@ namespace juce class JUCE_API AudioParameterFloat : public RangedAudioParameter { public: + /** Creates a AudioParameterFloat with the specified parameters. + + Note that the attributes argument is optional and only needs to be + supplied if you want to change options from their default values. + + Example usage: + @code + auto attributes = AudioParameterFloatAttributes().withStringFromValueFunction ([] (auto x, auto) { return String (x * 100); }) + .withLabel ("%"); + auto param = std::make_unique ("paramID", "Parameter Name", NormalisableRange(), 0.5f, attributes); + @endcode + + @param parameterID The parameter ID to use + @param parameterName The parameter name to use + @param normalisableRange The NormalisableRange to use + @param defaultValue The non-normalised default value + @param attributes Optional characteristics + */ + AudioParameterFloat (const ParameterID& parameterID, + const String& parameterName, + NormalisableRange normalisableRange, + float defaultValue, + const AudioParameterFloatAttributes& attributes = {}); + /** Creates a AudioParameterFloat with the specified parameters. @param parameterID The parameter ID to use @@ -51,25 +82,34 @@ 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, + [[deprecated ("Prefer the signature taking an Attributes argument")]] + AudioParameterFloat (const ParameterID& parameterID, const String& parameterName, NormalisableRange normalisableRange, float defaultValue, - const String& parameterLabel = String(), + const String& parameterLabel, Category parameterCategory = AudioProcessorParameter::genericParameter, std::function stringFromValue = nullptr, - std::function valueFromString = nullptr, - int versionHint = 0); + std::function valueFromString = nullptr) + : AudioParameterFloat (parameterID, + parameterName, + std::move (normalisableRange), + defaultValue, + AudioParameterFloatAttributes().withLabel (parameterLabel) + .withCategory (parameterCategory) + .withStringFromValueFunction (std::move (stringFromValue)) + .withValueFromStringFunction (std::move (valueFromString))) + { + } /** Creates a AudioParameterFloat with an ID, name, and range. On creation, its value is set to the default value. For control over skew factors, you can use the other constructor and provide a NormalisableRange. */ - AudioParameterFloat (String parameterID, - String parameterName, + AudioParameterFloat (const ParameterID& parameterID, + const String& parameterName, float minValue, float maxValue, float defaultValue); @@ -108,7 +148,7 @@ private: float getValueForText (const String&) const override; std::atomic value; - const float defaultValue; + const float valueDefault; std::function stringFromValueFunction; std::function valueFromStringFunction; diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterInt.cpp b/modules/juce_audio_processors/utilities/juce_AudioParameterInt.cpp index 2a8486980a..15847080e8 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterInt.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterInt.cpp @@ -26,34 +26,29 @@ namespace juce { -AudioParameterInt::AudioParameterInt (const String& idToUse, const String& nameToUse, +AudioParameterInt::AudioParameterInt (const ParameterID& idToUse, const String& nameToUse, int minValue, int maxValue, int def, - const String& labelToUse, - std::function stringFromInt, - std::function intFromString, - int versionHintToUse) - : RangedAudioParameter (idToUse, nameToUse, labelToUse, Category::genericParameter, versionHintToUse), - range ([minValue, maxValue] - { - NormalisableRange rangeWithInterval { (float) minValue, (float) maxValue, - [] (float start, float end, float v) { return jlimit (start, end, v * (end - start) + start); }, - [] (float start, float end, float v) { return jlimit (0.0f, 1.0f, (v - start) / (end - start)); }, - [] (float start, float end, float v) { return (float) roundToInt (juce::jlimit (start, end, v)); } }; - rangeWithInterval.interval = 1.0f; - return rangeWithInterval; - }()), - value ((float) def), - defaultValue (convertTo0to1 ((float) def)), - stringFromIntFunction (stringFromInt), - intFromStringFunction (intFromString) + const AudioParameterIntAttributes& attributes) + : RangedAudioParameter (idToUse, nameToUse, attributes.getAudioProcessorParameterWithIDAttributes()), + range ([minValue, maxValue] + { + NormalisableRange rangeWithInterval { (float) minValue, (float) maxValue, + [] (float start, float end, float v) { return jlimit (start, end, v * (end - start) + start); }, + [] (float start, float end, float v) { return jlimit (0.0f, 1.0f, (v - start) / (end - start)); }, + [] (float start, float end, float v) { return (float) roundToInt (juce::jlimit (start, end, v)); } }; + rangeWithInterval.interval = 1.0f; + return rangeWithInterval; + }()), + value ((float) def), + defaultValue (convertTo0to1 ((float) def)), + stringFromIntFunction (attributes.getStringFromValueFunction() != nullptr + ? attributes.getStringFromValueFunction() + : [] (int v, int) { return String (v); }), + intFromStringFunction (attributes.getValueFromStringFunction() != nullptr + ? attributes.getValueFromStringFunction() + : [] (const String& text) { return text.getIntValue(); }) { jassert (minValue < maxValue); // must have a non-zero range of values! - - if (stringFromIntFunction == nullptr) - stringFromIntFunction = [] (int v, int) { return String (v); }; - - if (intFromStringFunction == nullptr) - intFromStringFunction = [] (const String& text) { return text.getIntValue(); }; } AudioParameterInt::~AudioParameterInt() diff --git a/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h b/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h index 2d61c50c84..77c3c1f5b4 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h +++ b/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h @@ -26,6 +26,13 @@ namespace juce { +/** Properties of an AudioParameterInt. + + @see AudioParameterInt(), RangedAudioParameterAttributes() +*/ +class AudioParameterIntAttributes : public RangedAudioParameterAttributes {}; + +//============================================================================== /** Provides a class of AudioProcessorParameter that can be used as an integer value with a given range. @@ -39,11 +46,37 @@ class JUCE_API AudioParameterInt : public RangedAudioParameter public: /** Creates a AudioParameterInt with the specified parameters. + Note that the attributes argument is optional and only needs to be + supplied if you want to change options from their default values. + + Example usage: + @code + auto attributes = AudioParameterIntAttributes().withStringFromValueFunction ([] (auto x, auto) { return String (x); }) + .withLabel ("things"); + auto param = std::make_unique ("paramID", "Parameter Name", 0, 100, 50, attributes); + @endcode + @param parameterID The parameter ID to use @param parameterName The parameter name to use @param minValue The minimum parameter value @param maxValue The maximum parameter value @param defaultValue The default value + @param attributes Optional characteristics + */ + AudioParameterInt (const ParameterID& parameterID, + const String& parameterName, + int minValue, + int maxValue, + int defaultValue, + const AudioParameterIntAttributes& attributes = {}); + + /** Creates a AudioParameterInt with the specified parameters. + + @param parameterID The parameter ID to use + @param parameterName The parameter name to use + @param minValue The minimum parameter value + @param maxValue The maximum parameter value + @param defaultValueIn The default value @param parameterLabel An optional label for the parameter's value @param stringFromInt An optional lambda function that converts a int value to a string with a maximum length. This may @@ -51,15 +84,26 @@ 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(), + [[deprecated ("Prefer the signature taking an Attributes argument")]] + AudioParameterInt (const ParameterID& parameterID, + const String& parameterName, + int minValue, + int maxValue, + int defaultValueIn, + const String& parameterLabel, std::function stringFromInt = nullptr, - std::function intFromString = nullptr, - int versionHint = 0); + std::function intFromString = nullptr) + : AudioParameterInt (parameterID, + parameterName, + minValue, + maxValue, + defaultValueIn, + AudioParameterIntAttributes().withLabel (parameterLabel) + .withStringFromValueFunction (std::move (stringFromInt)) + .withValueFromStringFunction (std::move (intFromString))) + { + } /** Destructor. */ ~AudioParameterInt() override; diff --git a/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.cpp b/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.cpp index 9540464d30..d795b9847e 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.cpp @@ -26,16 +26,17 @@ namespace juce { -AudioProcessorParameterWithID::AudioProcessorParameterWithID (const String& idToUse, +AudioProcessorParameterWithID::AudioProcessorParameterWithID (const ParameterID& idToUse, const String& nameToUse, - const String& labelToUse, - AudioProcessorParameter::Category categoryToUse, - int versionHintToUse) - : HostedAudioProcessorParameter (versionHintToUse), - paramID (idToUse), + const AudioProcessorParameterWithIDAttributes& attributes) + : HostedAudioProcessorParameter (idToUse.getVersionHint()), + paramID (idToUse.getParamID()), name (nameToUse), - label (labelToUse), - category (categoryToUse) + label (attributes.getLabel()), + category (attributes.getCategory()), + meta (attributes.getMeta()), + automatable (attributes.getAutomatable()), + inverted (attributes.getInverted()) { } diff --git a/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h b/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h index 2360d1f6c3..37aecc10d9 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h +++ b/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h @@ -26,6 +26,87 @@ namespace juce { +/** + Combines a parameter ID and a version hint. +*/ +class ParameterID +{ +public: + ParameterID() = default; + + /** Constructs an instance. + + Note that this constructor implicitly converts from Strings and string-like types. + + @param identifier A string that uniquely identifies a single parameter + @param versionHint Influences parameter ordering in Audio Unit plugins. + Used to provide backwards compatibility of Audio Unit plugins in + Logic and GarageBand. + @see AudioProcessorParameter(int) + */ + template > + ParameterID (StringLike&& identifier, int versionHint = 0) + : paramID (std::forward (identifier)), version (versionHint) {} + + /** @see AudioProcessorParameterWithID::paramID */ + auto getParamID() const { return paramID; } + + /** @see AudioProcessorParameter(int) */ + auto getVersionHint() const { return version; } + +private: + String paramID; + int version = 0; +}; + +/** + An instance of this class may be passed to the constructor of an AudioProcessorParameterWithID + to set optional characteristics of that parameter. +*/ +class AudioProcessorParameterWithIDAttributes +{ + using This = AudioProcessorParameterWithIDAttributes; + +public: + using Category = AudioProcessorParameter::Category; + + /** An optional label for the parameter's value */ + JUCE_NODISCARD auto withLabel (String x) const { return withMember (*this, &This::label, std::move (x)); } + + /** The semantics of this parameter */ + JUCE_NODISCARD auto withCategory (Category x) const { return withMember (*this, &This::category, std::move (x)); } + + /** @see AudioProcessorParameter::isMetaParameter() */ + JUCE_NODISCARD auto withMeta (bool x) const { return withMember (*this, &This::meta, std::move (x)); } + + /** @see AudioProcessorParameter::isAutomatable() */ + JUCE_NODISCARD auto withAutomatable (bool x) const { return withMember (*this, &This::automatable, std::move (x)); } + + /** @see AudioProcessorParameter::isOrientationInverted() */ + JUCE_NODISCARD auto withInverted (bool x) const { return withMember (*this, &This::inverted, std::move (x)); } + + /** An optional label for the parameter's value */ + JUCE_NODISCARD auto getLabel() const { return label; } + + /** The semantics of this parameter */ + JUCE_NODISCARD auto getCategory() const { return category; } + + /** @see AudioProcessorParameter::isMetaParameter() */ + JUCE_NODISCARD auto getMeta() const { return meta; } + + /** @see AudioProcessorParameter::isAutomatable() */ + JUCE_NODISCARD auto getAutomatable() const { return automatable; } + + /** @see AudioProcessorParameter::isOrientationInverted() */ + JUCE_NODISCARD auto getInverted() const { return inverted; } + +private: + String label; + Category category = AudioProcessorParameter::genericParameter; + bool meta = false, automatable = true, inverted = false; +}; + +//============================================================================== /** This abstract base class is used by some AudioProcessorParameter helper classes. @@ -36,6 +117,26 @@ namespace juce class JUCE_API AudioProcessorParameterWithID : public HostedAudioProcessorParameter { public: + /** The creation of this object requires providing a name and ID which will be constant for its lifetime. + + Given that AudioProcessorParameterWithID is abstract, you'll probably call this constructor + from a derived class constructor, e.g. + @code + MyParameterType (String paramID, String name, String label, bool automatable) + : AudioProcessorParameterWithID (paramID, name, AudioProcessorParameterWithIDAttributes().withLabel (label) + .withAutomatable (automatable)) + { + } + @endcode + + @param parameterID Specifies the identifier, and optionally the parameter's version hint. + @param parameterName The user-facing parameter name. + @param attributes Other parameter properties. + */ + AudioProcessorParameterWithID (const ParameterID& parameterID, + const String& parameterName, + const AudioProcessorParameterWithIDAttributes& attributes = {}); + /** The creation of this object requires providing a name and ID which will be constant for its lifetime. @@ -43,13 +144,18 @@ public: @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, + [[deprecated ("Prefer the signature taking an Attributes argument")]] + AudioProcessorParameterWithID (const ParameterID& parameterID, const String& parameterName, - const String& parameterLabel = {}, - Category parameterCategory = AudioProcessorParameter::genericParameter, - int versionHint = 0); + const String& parameterLabel, + Category parameterCategory = AudioProcessorParameter::genericParameter) + : AudioProcessorParameterWithID (parameterID, + parameterName, + AudioProcessorParameterWithIDAttributes().withLabel (parameterLabel) + .withCategory (parameterCategory)) + { + } /** Provides access to the parameter's ID string. */ const String paramID; @@ -67,9 +173,14 @@ public: String getLabel() const override; Category getCategory() const override; - String getParameterID() const override { return paramID; } + String getParameterID() const override { return paramID; } + bool isMetaParameter() const override { return meta; } + bool isAutomatable() const override { return automatable; } + bool isOrientationInverted() const override { return inverted; } private: + bool meta = false, automatable = true, inverted = false; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterWithID) }; diff --git a/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp b/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp index e6ba3f8709..23fbe951c1 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp +++ b/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp @@ -27,42 +27,26 @@ namespace juce { //============================================================================== -AudioProcessorValueTreeState::Parameter::Parameter (const String& parameterID, + +AudioProcessorValueTreeState::Parameter::Parameter (const ParameterID& parameterID, const String& parameterName, - const String& labelText, NormalisableRange valueRange, float defaultParameterValue, - std::function valueToTextFunction, - std::function textToValueFunction, - bool isMetaParameter, - bool isAutomatableParameter, - bool isDiscrete, - AudioProcessorParameter::Category parameterCategory, - bool isBoolean, - int versionHintToUse) + const AudioProcessorValueTreeStateParameterAttributes& attributes) : AudioParameterFloat (parameterID, parameterName, valueRange, defaultParameterValue, - labelText, - parameterCategory, - valueToTextFunction == nullptr ? std::function() - : [valueToTextFunction] (float v, int) { return valueToTextFunction (v); }, - std::move (textToValueFunction), - versionHintToUse), + attributes.getAudioParameterFloatAttributes()), unsnappedDefault (valueRange.convertTo0to1 (defaultParameterValue)), - metaParameter (isMetaParameter), - automatable (isAutomatableParameter), - discrete (isDiscrete), - boolean (isBoolean) + discrete (attributes.getDiscrete()), + boolean (attributes.getBoolean()) { } float AudioProcessorValueTreeState::Parameter::getDefaultValue() const { return unsnappedDefault; } int AudioProcessorValueTreeState::Parameter::getNumSteps() const { return RangedAudioParameter::getNumSteps(); } -bool AudioProcessorValueTreeState::Parameter::isMetaParameter() const { return metaParameter; } -bool AudioProcessorValueTreeState::Parameter::isAutomatable() const { return automatable; } bool AudioProcessorValueTreeState::Parameter::isDiscrete() const { return discrete; } bool AudioProcessorValueTreeState::Parameter::isBoolean() const { return boolean; } @@ -305,18 +289,21 @@ RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (const AudioProcessorParameter::Category category, bool isBooleanParameter) { + auto attributes = AudioProcessorValueTreeStateParameterAttributes() + .withLabel (labelText) + .withStringFromValueFunction ([fn = std::move (valueToTextFunction)] (float v, int) { return fn (v); }) + .withValueFromStringFunction (std::move (textToValueFunction)) + .withMeta (isMetaParameter) + .withAutomatable (isAutomatableParameter) + .withDiscrete (isDiscreteParameter) + .withCategory (category) + .withBoolean (isBooleanParameter); + return createAndAddParameter (std::make_unique (paramID, paramName, - labelText, range, defaultVal, - std::move (valueToTextFunction), - std::move (textToValueFunction), - isMetaParameter, - isAutomatableParameter, - isDiscreteParameter, - category, - isBooleanParameter)); + std::move (attributes))); } RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (std::unique_ptr param) @@ -535,7 +522,7 @@ struct ParameterAdapterTests : public UnitTest { const auto test = [&] (NormalisableRange range, float value) { - AudioParameterFloat param ({}, {}, range, value, {}); + AudioParameterFloat param ({}, {}, range, value); AudioProcessorValueTreeState::ParameterAdapter adapter (param); @@ -550,7 +537,7 @@ struct ParameterAdapterTests : public UnitTest { const auto test = [&] (NormalisableRange range, float value) { - AudioParameterFloat param ({}, {}, range, {}, {}); + AudioParameterFloat param ({}, {}, range, {}); AudioProcessorValueTreeState::ParameterAdapter adapter (param); adapter.setDenormalisedValue (value); @@ -567,7 +554,7 @@ struct ParameterAdapterTests : public UnitTest { const auto test = [&] (NormalisableRange range, float value, String expected) { - AudioParameterFloat param ({}, {}, range, {}, {}); + AudioParameterFloat param ({}, {}, range, {}); AudioProcessorValueTreeState::ParameterAdapter adapter (param); expectEquals (adapter.getTextForDenormalisedValue (value), expected); @@ -583,7 +570,7 @@ struct ParameterAdapterTests : public UnitTest { const auto test = [&] (NormalisableRange range, String text, float expected) { - AudioParameterFloat param ({}, {}, range, {}, {}); + AudioParameterFloat param ({}, {}, range, {}); AudioProcessorValueTreeState::ParameterAdapter adapter (param); expectEquals (adapter.getDenormalisedValueForText (text), expected); @@ -623,6 +610,7 @@ private: using Parameter = AudioProcessorValueTreeState::Parameter; using ParameterGroup = AudioProcessorParameterGroup; using ParameterLayout = AudioProcessorValueTreeState::ParameterLayout; + using Attributes = AudioProcessorValueTreeStateParameterAttributes; class TestAudioProcessor : public AudioProcessor { @@ -679,8 +667,11 @@ public: { TestAudioProcessor proc; - proc.state.createAndAddParameter (std::make_unique (String(), String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + proc.state.createAndAddParameter (std::make_unique ( + String(), + String(), + NormalisableRange(), + 0.0f)); expectEquals (proc.getParameters().size(), 1); } @@ -690,8 +681,11 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); expect (proc.state.getParameter (key) == param); } @@ -723,8 +717,12 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr, true)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f, + Attributes().withMeta (true))); expect (param->isMetaParameter()); } @@ -734,8 +732,12 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr, false, true)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f, + Attributes().withAutomatable (true))); expect (param->isAutomatable()); } @@ -745,8 +747,12 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr, false, false, true)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f, + Attributes().withDiscrete (true))); expect (param->isDiscrete()); } @@ -756,9 +762,12 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr, false, false, false, - AudioProcessorParameter::Category::inputMeter)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f, + Attributes().withCategory (AudioProcessorParameter::Category::inputMeter))); expect (param->category == AudioProcessorParameter::Category::inputMeter); } @@ -768,9 +777,12 @@ public: TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr, false, false, false, - AudioProcessorParameter::Category::genericParameter, true)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f, + Attributes().withBoolean (true))); expect (param->isBoolean()); } @@ -790,11 +802,17 @@ public: { TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); - proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); expectEquals (proc.getParameters().size(), 1); expect (proc.getParameters().getFirst() == param); @@ -804,8 +822,11 @@ public: { TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); const auto value = 0.5f; param->setValueNotifyingHost (value); @@ -822,11 +843,8 @@ public: proc.state.createAndAddParameter (std::make_unique ( key, String(), - String(), NormalisableRange (0.0f, 100.0f, 10.0f), - value, - nullptr, - nullptr)); + value)); expectEquals (proc.state.getRawParameterValue (key)->load(), value); } @@ -836,8 +854,11 @@ public: Listener listener; TestAudioProcessor proc; const auto key = "id"; - const auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + const auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); proc.state.addParameterListener (key, &listener); const auto value = 0.5f; @@ -893,8 +914,11 @@ public: TestAudioProcessor proc; const auto key = "id"; const auto initialValue = 0.2f; - auto param = proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - initialValue, nullptr, nullptr)); + auto param = proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + initialValue)); proc.state.state = ValueTree { "state" }; auto value = proc.state.getParameterAsValue (key); @@ -928,8 +952,11 @@ public: Listener listener; TestAudioProcessor proc; const auto key = "id"; - proc.state.createAndAddParameter (std::make_unique (key, String(), String(), NormalisableRange(), - 0.0f, nullptr, nullptr)); + proc.state.createAndAddParameter (std::make_unique ( + key, + String(), + NormalisableRange(), + 0.0f)); proc.state.addParameterListener (key, &listener); proc.state.state = ValueTree { "state" }; diff --git a/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h b/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h index 24066245f9..d32f54b482 100644 --- a/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h +++ b/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h @@ -26,6 +26,60 @@ namespace juce { +/** Advanced properties of an AudioProcessorValueTreeState::Parameter. + + The members here have the same meaning as the similarly-named member functions of + AudioParameterFloatAttributes. + + @see AudioParameterFloatAttributes, RangedAudioParameterAttributes +*/ +class AudioProcessorValueTreeStateParameterAttributes +{ + using This = AudioProcessorValueTreeStateParameterAttributes; + using StringFromValue = AudioParameterFloatAttributes::StringFromValue; + using ValueFromString = AudioParameterFloatAttributes::ValueFromString; + using Category = AudioParameterFloatAttributes::Category; + +public: + /** @see RangedAudioParameterAttributes::withStringFromValueFunction() */ + JUCE_NODISCARD auto withStringFromValueFunction (StringFromValue x) const { return withMember (*this, &This::attributes, attributes.withStringFromValueFunction (std::move (x))); } + /** @see RangedAudioParameterAttributes::withValueFromStringFunction() */ + JUCE_NODISCARD auto withValueFromStringFunction (ValueFromString x) const { return withMember (*this, &This::attributes, attributes.withValueFromStringFunction (std::move (x))); } + /** @see RangedAudioParameterAttributes::withLabel() */ + JUCE_NODISCARD auto withLabel (String x) const { return withMember (*this, &This::attributes, attributes.withLabel (std::move (x))); } + /** @see RangedAudioParameterAttributes::withCategory() */ + JUCE_NODISCARD auto withCategory (Category x) const { return withMember (*this, &This::attributes, attributes.withCategory (std::move (x))); } + /** @see RangedAudioParameterAttributes::withMeta() */ + JUCE_NODISCARD auto withMeta (bool x) const { return withMember (*this, &This::attributes, attributes.withMeta (std::move (x))); } + /** @see RangedAudioParameterAttributes::withAutomatable() */ + JUCE_NODISCARD auto withAutomatable (bool x) const { return withMember (*this, &This::attributes, attributes.withAutomatable (std::move (x))); } + /** @see RangedAudioParameterAttributes::withInverted() */ + JUCE_NODISCARD auto withInverted (bool x) const { return withMember (*this, &This::attributes, attributes.withInverted (std::move (x))); } + + /** Pass 'true' if this parameter has discrete steps, or 'false' if the parameter is continuous. + + Using an AudioParameterChoice or AudioParameterInt might be a better choice than setting this flag. + */ + JUCE_NODISCARD auto withDiscrete (bool x) const { return withMember (*this, &This::discrete, std::move (x)); } + + /** Pass 'true' if this parameter only has two valid states. + + Using an AudioParameterBool might be a better choice than setting this flag. + */ + JUCE_NODISCARD auto withBoolean (bool x) const { return withMember (*this, &This::boolean, std::move (x)); } + + /** @returns all attributes that might also apply to an AudioParameterFloat */ + JUCE_NODISCARD const auto& getAudioParameterFloatAttributes() const { return attributes; } + /** @returns 'true' if this parameter has discrete steps, or 'false' if the parameter is continuous. */ + JUCE_NODISCARD const auto& getDiscrete() const { return discrete; } + /** @returns 'true' if this parameter only has two valid states. */ + JUCE_NODISCARD const auto& getBoolean() const { return boolean; } + +private: + AudioParameterFloatAttributes attributes; + bool discrete = false, boolean = false; +}; + /** This class contains a ValueTree that is used to manage an AudioProcessor's entire state. @@ -398,25 +452,65 @@ public: class Parameter final : public AudioParameterFloat { public: - Parameter (const String& parameterID, + /** Constructs a parameter instance. + + Example usage: + @code + using Parameter = AudioProcessorValueTreeState::Parameter; + using Attributes = AudioProcessorValueTreeStateParameterAttributes; + + auto parameter = std::make_unique (ParameterID { "uniqueID", 1 }, + "Name", + NormalisableRange { 0.0f, 100.0f }, + 50.0f, + Attributes().withStringFromValueFunction (myStringFromValueFunction) + .withValueFromStringFunction (myValueFromStringFunction) + .withLabel ("%")); + @endcode + + @param parameterID The globally-unique identifier of this parameter + @param parameterName The user-facing name of this parameter + @param valueRange The valid range of values for this parameter + @param defaultValue The initial parameter value + @param attributes Further advanced settings to customise the behaviour of this parameter + */ + Parameter (const ParameterID& parameterID, const String& parameterName, - const String& labelText, NormalisableRange valueRange, float defaultValue, + const AudioProcessorValueTreeStateParameterAttributes& attributes = {}); + + [[deprecated ("Prefer the signature taking an Attributes argument")]] + Parameter (const ParameterID& parameterID, + const String& parameterName, + const String& labelText, + NormalisableRange valueRange, + float defaultParameterValue, std::function valueToTextFunction, std::function textToValueFunction, - bool isMetaParameter = false, - bool isAutomatableParameter = true, - bool isDiscrete = false, - AudioProcessorParameter::Category parameterCategory = AudioProcessorParameter::genericParameter, - bool isBoolean = false, - int versionHint = 0); + bool isMetaParameter, + bool isAutomatableParameter, + bool isDiscrete, + AudioProcessorParameter::Category parameterCategory, + bool isBoolean) + : Parameter (parameterID, + parameterName, + valueRange, + defaultParameterValue, + AudioProcessorValueTreeStateParameterAttributes().withLabel (labelText) + .withStringFromValueFunction ([valueToTextFunction] (float v, int) { return valueToTextFunction (v); }) + .withValueFromStringFunction (std::move (textToValueFunction)) + .withMeta (isMetaParameter) + .withAutomatable (isAutomatableParameter) + .withDiscrete (isDiscrete) + .withCategory (parameterCategory) + .withBoolean (isBoolean)) + { + } float getDefaultValue() const override; int getNumSteps() const override; - bool isMetaParameter() const override; - bool isAutomatable() const override; bool isDiscrete() const override; bool isBoolean() const override; @@ -426,7 +520,7 @@ public: std::function onValueChanged; const float unsnappedDefault; - const bool metaParameter, automatable, discrete, boolean; + const bool discrete, boolean; std::atomic lastValue { -1.0f }; friend class AudioProcessorValueTreeState::ParameterAdapter; diff --git a/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h b/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h index bb9eb1ffe1..542bdd75b1 100644 --- a/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h +++ b/modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h @@ -26,6 +26,67 @@ namespace juce { +/** + @internal + + Holds common attributes of audio parameters. + + CRTP is used here because we want the Attributes types for each parameter + (Float, Bool, Choice, Int) to be distinct and extensible in the future. + i.e. the identifiers AudioParameterFloatAttributes and RangedAudioParameterAttributes + should not be interchangable because we might need to add float-specific attributes in + the future. Users should not refer directly to RangedAudioParameterAttributes. +*/ +template +class RangedAudioParameterAttributes +{ + using This = RangedAudioParameterAttributes; + +public: + using Category = AudioProcessorParameter::Category; + + using StringFromValue = std::function; + using ValueFromString = std::function; + + /** An optional lambda function that converts a non-normalised value to a string with a maximum length. This may be used by hosts to display the parameter's value. */ + JUCE_NODISCARD auto withStringFromValueFunction (StringFromValue x) const { return withMember (asDerived(), &Derived::stringFromValue, std::move (x)); } + + /** 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. */ + JUCE_NODISCARD auto withValueFromStringFunction (ValueFromString x) const { return withMember (asDerived(), &Derived::valueFromString, std::move (x)); } + + /** See AudioProcessorParameterWithIDAttributes::withLabel() */ + JUCE_NODISCARD auto withLabel (String x) const { return withMember (asDerived(), &Derived::attributes, attributes.withLabel (std::move (x))); } + + /** See AudioProcessorParameterWithIDAttributes::withCategory() */ + JUCE_NODISCARD auto withCategory (Category x) const { return withMember (asDerived(), &Derived::attributes, attributes.withCategory (std::move (x))); } + + /** See AudioProcessorParameter::isMetaParameter() */ + JUCE_NODISCARD auto withMeta (bool x) const { return withMember (asDerived(), &Derived::attributes, attributes.withMeta (std::move (x))); } + + /** See AudioProcessorParameter::isAutomatable() */ + JUCE_NODISCARD auto withAutomatable (bool x) const { return withMember (asDerived(), &Derived::attributes, attributes.withAutomatable (std::move (x))); } + + /** See AudioProcessorParameter::isOrientationInverted() */ + JUCE_NODISCARD auto withInverted (bool x) const { return withMember (asDerived(), &Derived::attributes, attributes.withInverted (std::move (x))); } + + /** An optional lambda function that converts a non-normalised value to a string with a maximum length. This may be used by hosts to display the parameter's value. */ + JUCE_NODISCARD const auto& getStringFromValueFunction() const { return stringFromValue; } + + /** 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. */ + JUCE_NODISCARD const auto& getValueFromStringFunction() const { return valueFromString; } + + /** Gets attributes that would also apply to an AudioProcessorParameterWithID */ + JUCE_NODISCARD const auto& getAudioProcessorParameterWithIDAttributes() const { return attributes; } + +private: + auto& asDerived() const { return *static_cast (this); } + + AudioProcessorParameterWithIDAttributes attributes; + StringFromValue stringFromValue; + ValueFromString valueFromString; +}; + +//============================================================================== /** This abstract base class is used by some AudioProcessorParameter helper classes. @@ -36,15 +97,6 @@ namespace juce 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() - */ using AudioProcessorParameterWithID::AudioProcessorParameterWithID; /** Returns the range of values that the parameter can take. */ diff --git a/modules/juce_core/misc/juce_Functional.h b/modules/juce_core/misc/juce_Functional.h index c84bdaf5d5..142f5f9d3e 100644 --- a/modules/juce_core/misc/juce_Functional.h +++ b/modules/juce_core/misc/juce_Functional.h @@ -69,4 +69,20 @@ struct NullCheckedInvocation static void invoke (std::nullptr_t, Args&&...) {} }; +/** Can be used to disable template constructors that would otherwise cause ambiguity with + compiler-generated copy and move constructors. + + Adapted from https://ericniebler.com/2013/08/07/universal-references-and-the-copy-constructo/ +*/ +template +using DisableIfSameOrDerived = typename std::enable_if_t>::value>; + +/** Copies an object, sets one of the copy's members to the specified value, and then returns the copy. */ +template +Object withMember (Object copy, Member OtherObject::* member, Member&& value) +{ + copy.*member = std::forward (value); + return copy; +} + } // namespace juce