32 const String& parameterName,
40 attributes.getAudioParameterFloatAttributes()),
42 discrete (attributes.getDiscrete()),
43 boolean (attributes.getBoolean())
53void AudioProcessorValueTreeState::Parameter::valueChanged (
float newValue)
59 NullCheckedInvocation::invoke (onValueChanged);
70 : parameter (parameterIn),
72 unnormalisedValue (getRange().convertFrom0to1 (parameter.getDefaultValue()))
74 parameter.addListener (
this);
76 if (
auto* ptr =
dynamic_cast<Parameter*
> (¶meter))
77 ptr->onValueChanged = [
this] { parameterValueChanged ({}, {}); };
82 void addListener (
Listener* l) { listeners.add (l); }
83 void removeListener (
Listener* l) { listeners.remove (l); }
90 float getDenormalisedDefaultValue()
const {
return denormalise (parameter.getDefaultValue()); }
92 void setDenormalisedValue (
float value)
95 setNormalisedValue (normalise (value));
98 float getDenormalisedValueForText (
const String& text)
const
100 return denormalise (parameter.getValueForText (text));
103 String getTextForDenormalisedValue (
float value)
const
105 return parameter.getText (normalise (value), 0);
108 float getDenormalisedValue()
const {
return unnormalisedValue; }
113 auto needsUpdateTestValue =
true;
115 if (! needsUpdate.compare_exchange_strong (needsUpdateTestValue,
false))
118 if (
auto* valueProperty = tree.getPropertyPointer (key))
123 tree.setProperty (key, unnormalisedValue.load(), um);
128 tree.setProperty (key, unnormalisedValue.load(),
nullptr);
137 void parameterGestureChanged (
int,
bool)
override {}
139 void parameterValueChanged (
int,
float)
override
141 const auto newValue = denormalise (parameter.getValue());
143 if (! listenersNeedCalling &&
approximatelyEqual ((
float) unnormalisedValue, newValue))
146 unnormalisedValue = newValue;
148 listenersNeedCalling =
false;
152 float denormalise (
float normalised)
const
154 return getParameter().convertFrom0to1 (normalised);
157 float normalise (
float denormalised)
const
159 return getParameter().convertTo0to1 (denormalised);
162 void setNormalisedValue (
float value)
164 if (ignoreParameterChangedCallbacks)
167 parameter.setValueNotifyingHost (value);
170 class LockedListeners
173 template <
typename Fn>
177 listeners.call (std::forward<Fn> (fn));
189 listeners.remove (l);
198 LockedListeners listeners;
201 bool ignoreParameterChangedCallbacks {
false };
214 : state (&stateIn) {}
218 if (param ==
nullptr)
224 state->addParameterAdapter (*param);
225 state->processor.addParameter (param.
release());
230 if (group ==
nullptr)
236 for (
const auto param : group->getParameters (
true))
240 state->addParameterAdapter (*rangedParam);
250 state->processor.addParameterGroup (std::move (group));
263 : processor (p), undoManager (
um)
282 bool isMetaParameter,
290 .withStringFromValueFunction ([fn = std::move (
valueToTextFunction)] (
float v,
int) {
return fn (v); })
292 .withMeta (isMetaParameter)
295 .withCategory (category)
302 std::move (attributes)));
307 if (param ==
nullptr)
316 addParameterAdapter (*param);
324void AudioProcessorValueTreeState::addParameterAdapter (RangedAudioParameter& param)
326 adapterTable.emplace (param.paramID, std::make_unique<ParameterAdapter> (param));
329AudioProcessorValueTreeState::ParameterAdapter* AudioProcessorValueTreeState::getParameterAdapter (StringRef paramID)
const
331 auto it = adapterTable.find (paramID);
332 return it == adapterTable.end() ?
nullptr :
it->second.get();
337 if (
auto* p = getParameterAdapter (paramID))
338 p->addListener (listener);
343 if (
auto* p = getParameterAdapter (paramID))
344 p->removeListener (listener);
349 if (
auto*
adapter = getParameterAdapter (paramID))
358 if (
auto* p = getParameterAdapter (paramID))
366 if (
auto adapter = getParameterAdapter (paramID))
367 return &
adapter->getParameter();
374 if (
auto* p = getParameterAdapter (paramID))
375 return &p->getRawDenormalisedValue();
383 flushParameterValuesToValueTree();
397void AudioProcessorValueTreeState::setNewState (
ValueTree vt)
401 if (
auto* p = getParameterAdapter (
vt.getProperty (idPropertyID).toString()))
404 p->setDenormalisedValue (p->tree.getProperty (valuePropertyID, p->getDenormalisedDefaultValue()));
408void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
412 for (
auto& p : adapterTable)
413 p.second->tree = ValueTree();
415 for (
const auto& child :
state)
418 for (
auto& p : adapterTable)
424 adapter.tree = ValueTree (valueType);
425 adapter.tree.setProperty (idPropertyID,
adapter.getParameter().paramID,
nullptr);
430 flushParameterValuesToValueTree();
433void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree,
const Identifier&)
435 if (tree.hasType (valueType) && tree.getParent() ==
state)
439void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
441 if (parent ==
state && tree.hasType (valueType))
445void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
448 updateParameterConnectionsToChildTrees();
451bool AudioProcessorValueTreeState::flushParameterValuesToValueTree()
457 for (
auto& p : adapterTable)
463void AudioProcessorValueTreeState::timerCallback()
472template <
typename Attachment,
typename Control>
478 return std::make_unique<Attachment> (*parameter, control,
stateToUse.undoManager);
484AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState&
stateToUse,
491AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState&
stateToUse,
498AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState&
stateToUse,
515 void runTest()
override
517 beginTest (
"The default value is returned correctly");
519 const auto test = [&] (NormalisableRange<float> range,
float value)
521 AudioParameterFloat param ({}, {}, range, value);
523 AudioProcessorValueTreeState::ParameterAdapter
adapter (param);
525 expectEquals (
adapter.getDenormalisedDefaultValue(), value);
528 test ({ -100, 100 }, 0);
529 test ({ -2.5, 12.5 }, 10);
532 beginTest (
"Denormalised parameter values can be retrieved");
534 const auto test = [&] (NormalisableRange<float> range,
float value)
536 AudioParameterFloat param ({}, {}, range, {});
537 AudioProcessorValueTreeState::ParameterAdapter
adapter (param);
539 adapter.setDenormalisedValue (value);
541 expectEquals (
adapter.getDenormalisedValue(), value);
542 expectEquals (
adapter.getRawDenormalisedValue().load(), value);
545 test ({ -20, -10 }, -15);
546 test ({ 0, 7.5 }, 2.5);
549 beginTest (
"Floats can be converted to text");
551 const auto test = [&] (NormalisableRange<float> range,
float value, String
expected)
553 AudioParameterFloat param ({}, {}, range, {});
554 AudioProcessorValueTreeState::ParameterAdapter
adapter (param);
559 test ({ -100, 100 }, 0,
"0.0000000");
560 test ({ -2.5, 12.5 }, 10,
"10.0000000");
561 test ({ -20, -10 }, -15,
"-15.0000000");
562 test ({ 0, 7.5 }, 2.5,
"2.5000000");
565 beginTest (
"Text can be converted to floats");
567 const auto test = [&] (NormalisableRange<float> range, String text,
float expected)
569 AudioParameterFloat param ({}, {}, range, {});
570 AudioProcessorValueTreeState::ParameterAdapter
adapter (param);
575 test ({ -100, 100 },
"0.0", 0);
576 test ({ -2.5, 12.5 },
"10.0", 10);
577 test ({ -20, -10 },
"-15.0", -15);
578 test ({ 0, 7.5 },
"2.5", 2.5);
587template <
typename ValueType>
591 return std::tie (a.start, a.end, a.interval, a.skew, a.symmetricSkew)
592 ==
std::tie (b.start, b.end, b.interval, b.skew, b.symmetricSkew);
595template <
typename ValueType>
606 using Parameter = AudioProcessorValueTreeState::Parameter;
608 using ParameterLayout = AudioProcessorValueTreeState::ParameterLayout;
609 using Attributes = AudioProcessorValueTreeStateParameterAttributes;
614 TestAudioProcessor() =
default;
616 explicit TestAudioProcessor (ParameterLayout layout)
617 : state (*this, nullptr,
"state",
std::
move (layout)) {}
619 const String getName()
const override {
return {}; }
620 void prepareToPlay (
double,
int)
override {}
621 void releaseResources()
override {}
622 void processBlock (AudioBuffer<float>&, MidiBuffer&)
override {}
623 using AudioProcessor::processBlock;
624 double getTailLengthSeconds()
const override {
return {}; }
625 bool acceptsMidi()
const override {
return {}; }
626 bool producesMidi()
const override {
return {}; }
627 AudioProcessorEditor* createEditor()
override {
return {}; }
628 bool hasEditor()
const override {
return {}; }
629 int getNumPrograms()
override {
return 1; }
630 int getCurrentProgram()
override {
return {}; }
631 void setCurrentProgram (
int)
override {}
632 const String getProgramName (
int)
override {
return {}; }
633 void changeProgramName (
int,
const String&)
override {}
634 void getStateInformation (MemoryBlock&)
override {}
635 void setStateInformation (
const void*,
int)
override {}
637 AudioProcessorValueTreeState state { *
this,
nullptr };
640 struct Listener
final :
public AudioProcessorValueTreeState::Listener
642 void parameterChanged (
const String& idIn,
float valueIn)
override
657 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
658 void runTest()
override
662 beginTest (
"After calling createAndAddParameter, the number of parameters increases by one");
666 proc.state.createAndAddParameter (std::make_unique<Parameter> (
669 NormalisableRange<float>(),
672 expectEquals (
proc.getParameters().size(), 1);
675 beginTest (
"After creating a normal named parameter, we can later retrieve that parameter");
679 const auto key =
"id";
680 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
683 NormalisableRange<float>(),
686 expect (
proc.state.getParameter (key) == param);
689 beginTest (
"After construction, the value tree has the expected format");
692 std::make_unique<AudioProcessorParameterGroup> (
"A",
"",
"",
693 std::make_unique<AudioParameterBool> (
"a",
"",
false),
694 std::make_unique<AudioParameterFloat> (
"b",
"", NormalisableRange<float>{}, 0.0f)),
695 std::make_unique<AudioProcessorParameterGroup> (
"B",
"",
"",
696 std::make_unique<AudioParameterInt> (
"c",
"", 0, 1, 0),
697 std::make_unique<AudioParameterChoice> (
"d",
"", StringArray {
"foo",
"bar" }, 0)) });
699 const auto valueTree =
proc.state.copyState();
701 expectEquals (valueTree.getNumChildren(), 4);
703 for (
auto child : valueTree)
705 expect (child.hasType (
"PARAM"));
706 expect (child.hasProperty (
"id"));
707 expect (child.hasProperty (
"value"));
711 beginTest (
"Meta parameters can be created");
715 const auto key =
"id";
716 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
719 NormalisableRange<float>(),
721 Attributes().withMeta (
true)));
723 expect (param->isMetaParameter());
726 beginTest (
"Automatable parameters can be created");
730 const auto key =
"id";
731 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
734 NormalisableRange<float>(),
736 Attributes().withAutomatable (
true)));
738 expect (param->isAutomatable());
741 beginTest (
"Discrete parameters can be created");
745 const auto key =
"id";
746 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
749 NormalisableRange<float>(),
751 Attributes().withDiscrete (
true)));
753 expect (param->isDiscrete());
756 beginTest (
"Custom category parameters can be created");
760 const auto key =
"id";
761 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
764 NormalisableRange<float>(),
771 beginTest (
"Boolean parameters can be created");
775 const auto key =
"id";
776 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
779 NormalisableRange<float>(),
781 Attributes().withBoolean (
true)));
783 expect (param->isBoolean());
786 beginTest (
"After creating a custom named parameter, we can later retrieve that parameter");
788 const auto key =
"id";
789 auto param = std::make_unique<AudioParameterBool> (key,
"",
false);
797 beginTest (
"After adding a normal parameter that already exists, the AudioProcessor parameters are unchanged");
800 const auto key =
"id";
801 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
804 NormalisableRange<float>(),
807 proc.state.createAndAddParameter (std::make_unique<Parameter> (
810 NormalisableRange<float>(),
813 expectEquals (
proc.getParameters().size(), 1);
814 expect (
proc.getParameters().getFirst() == param);
817 beginTest (
"After setting a parameter value, that value is reflected in the state");
820 const auto key =
"id";
821 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
824 NormalisableRange<float>(),
827 const auto value = 0.5f;
828 param->setValueNotifyingHost (value);
830 expectEquals (
proc.state.getRawParameterValue (key)->load(), value);
833 beginTest (
"After adding an APVTS::Parameter, its value is the default value");
836 const auto key =
"id";
837 const auto value = 5.0f;
839 proc.state.createAndAddParameter (std::make_unique<Parameter> (
842 NormalisableRange<float> (0.0f, 100.0f, 10.0f),
845 expectEquals (
proc.state.getRawParameterValue (key)->load(), value);
848 beginTest (
"Listeners receive notifications when parameters change");
852 const auto key =
"id";
853 const auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
856 NormalisableRange<float>(),
858 proc.state.addParameterListener (key, &listener);
860 const auto value = 0.5f;
861 param->setValueNotifyingHost (value);
863 expectEquals (listener.id, String { key });
864 expectEquals (listener.value, value);
867 beginTest (
"Bool parameters have a range of 0-1");
869 const auto key =
"id";
873 expect (
proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, 1.0f, 1.0f));
876 beginTest (
"Float parameters retain their specified range");
878 const auto key =
"id";
879 const auto range = NormalisableRange<float> { -100, 100, 0.7f, 0.2f,
true };
883 expect (
proc.state.getParameterRange (key) == range);
886 beginTest (
"Int parameters retain their specified range");
888 const auto key =
"id";
889 const auto min = -27;
894 expect (
proc.state.getParameterRange (key) == NormalisableRange<float> (
float (min),
float (max), 1.0f));
897 beginTest (
"Choice parameters retain their specified range");
899 const auto key =
"id";
900 const auto choices = StringArray {
"",
"",
"" };
904 expect (
proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, (
float) (choices.size() - 1), 1.0f));
905 expect (
proc.state.getParameter (key)->getNumSteps() == choices.size());
908 beginTest (
"When the parameter value is changed, normal parameter values are updated");
911 const auto key =
"id";
913 auto param =
proc.state.createAndAddParameter (std::make_unique<Parameter> (
916 NormalisableRange<float>(),
918 proc.state.state = ValueTree {
"state" };
920 auto value =
proc.state.getParameterAsValue (key);
923 const auto newValue = 0.75f;
926 expectEquals (param->getValue(), newValue);
927 expectEquals (
proc.state.getRawParameterValue (key)->load(), newValue);
930 beginTest (
"When the parameter value is changed, custom parameter values are updated");
932 const auto key =
"id";
933 const auto choices = StringArray (
"foo",
"bar",
"baz");
934 auto param = std::make_unique<AudioParameterChoice> (key,
"", choices, 0);
938 const auto newValue = 2.0f;
939 auto value =
proc.state.getParameterAsValue (key);
942 expectEquals (
paramPtr->getCurrentChoiceName(), choices[
int (newValue)]);
943 expectEquals (
proc.state.getRawParameterValue (key)->load(), newValue);
946 beginTest (
"When the parameter value is changed, listeners are notified");
950 const auto key =
"id";
951 proc.state.createAndAddParameter (std::make_unique<Parameter> (
954 NormalisableRange<float>(),
956 proc.state.addParameterListener (key, &listener);
957 proc.state.state = ValueTree {
"state" };
959 const auto newValue = 0.75f;
960 proc.state.getParameterAsValue (key) = newValue;
962 expectEquals (listener.value, newValue);
963 expectEquals (listener.id, String { key });
966 beginTest (
"When the parameter value is changed, listeners are notified");
968 const auto key =
"id";
969 const auto choices = StringArray {
"foo",
"bar",
"baz" };
972 proc.state.addParameterListener (key, &listener);
974 const auto newValue = 2.0f;
975 proc.state.getParameterAsValue (key) = newValue;
977 expectEquals (listener.value, newValue);
978 expectEquals (listener.id, String (key));
981 JUCE_END_IGNORE_WARNINGS_MSVC
A subclass of AudioProcessorParameter that provides an easy way to create a parameter which maps onto...
A base class for listeners that want to know about changes to an AudioProcessorParameter.
@ inputMeter
The following categories tell the host that this parameter is a meter level value and therefore read-...
Advanced properties of an AudioProcessorValueTreeState::Parameter.
auto withLabel(String x) const
A class to contain a set of RangedAudioParameters and AudioProcessorParameterGroups containing Ranged...
A parameter class that maintains backwards compatibility with deprecated AudioProcessorValueTreeState...
bool isBoolean() const override
Returns whether the parameter represents a boolean switch, typically with "On" and "Off" states.
bool isDiscrete() const override
Returns whether the parameter uses discrete values, based on the result of getNumSteps,...
Parameter(const ParameterID ¶meterID, const String ¶meterName, NormalisableRange< float > valueRange, float defaultValue, const AudioProcessorValueTreeStateParameterAttributes &attributes={})
Constructs a parameter instance.
float getDefaultValue() const override
This should return the default value for this parameter.
int getNumSteps() const override
Returns the number of steps that this parameter's range should be quantised into.
This class contains a ValueTree that is used to manage an AudioProcessor's entire state.
Value getParameterAsValue(StringRef parameterID) const
Returns a Value object that can be used to control a particular parameter.
UndoManager *const undoManager
Provides access to the undo manager that this object is using.
RangedAudioParameter * getParameter(StringRef parameterID) const noexcept
Returns a parameter by its ID string.
~AudioProcessorValueTreeState() override
Destructor.
AudioProcessor & processor
A reference to the processor with which this state is associated.
void addParameterListener(StringRef parameterID, Listener *listener)
Attaches a callback to one of the parameters, which will be called when the parameter changes.
NormalisableRange< float > getParameterRange(StringRef parameterID) const noexcept
Returns the range that was set when the given parameter was created.
AudioProcessorValueTreeState(AudioProcessor &processorToConnectTo, UndoManager *undoManagerToUse, const Identifier &valueTreeType, ParameterLayout parameterLayout)
Creates a state object for a given processor, and sets up all the parameters that will control that p...
void replaceState(const ValueTree &newState)
Replaces the state value tree.
void removeParameterListener(StringRef parameterID, Listener *listener)
Removes a callback that was previously added with addParameterCallback().
ValueTree copyState()
Returns a copy of the state value tree.
RangedAudioParameter * createAndAddParameter(std::unique_ptr< RangedAudioParameter > parameter)
This function adds a parameter to the attached AudioProcessor and that parameter will be managed by t...
ValueTree state
The state of the whole processor.
std::atomic< float > * getRawParameterValue(StringRef parameterID) const noexcept
Returns a pointer to a floating point representation of a particular parameter which a realtime proce...
Base class for audio processing classes or plugins.
void addParameter(AudioProcessorParameter *)
Adds a parameter to the AudioProcessor.
Automatically locks and unlocks a mutex object.
Represents a string identifier, designed for accessing properties by name.
Holds a set of objects and can invoke a member function callback on each object in the set with a sin...
Represents a mapping between an arbitrary range of values and a normalised 0->1 range.
Range< ValueType > getRange() const noexcept
Returns the extent of the normalisable range.
Combines a parameter ID and a version hint.
This abstract base class is used by some AudioProcessorParameter helper classes.
int getNumSteps() const override
Returns the number of steps for this parameter based on the normalisable range's interval.
Helper class providing an RAII-based mechanism for temporarily setting and then re-setting a value.
A simple class for holding temporary references to a string literal or String.
void stopTimer() noexcept
Stops the timer.
int getTimerInterval() const noexcept
Returns the timer's interval.
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
void startTimer(int intervalInMilliseconds) noexcept
Starts the timer and sets the length of interval required.
Manages a list of undo/redo commands.
void clearUndoHistory()
Deletes all stored actions in the list.
A powerful tree structure that can be used to hold free-form data, and which can handle its own undo ...
bool isValid() const noexcept
Returns true if this tree refers to some valid data.
void addListener(Listener *listener)
Adds a listener to receive callbacks when this tree is changed in some way.
void appendChild(const ValueTree &child, UndoManager *undoManager)
Appends a new child sub-tree to this tree.
ValueTree createCopy() const
Returns a deep copy of this tree and all its sub-trees.
Represents a shared variant value.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
A listener class that can be attached to an AudioProcessorValueTreeState.
virtual void parameterChanged(const String ¶meterID, float newValue)=0
This callback method is called by the AudioProcessorValueTreeState when a parameter changes.