The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

989 lines
36KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. //==============================================================================
  21. AudioProcessorValueTreeState::Parameter::Parameter (const ParameterID& parameterID,
  22. const String& parameterName,
  23. NormalisableRange<float> valueRange,
  24. float defaultParameterValue,
  25. const AudioProcessorValueTreeStateParameterAttributes& attributes)
  26. : AudioParameterFloat (parameterID,
  27. parameterName,
  28. valueRange,
  29. defaultParameterValue,
  30. attributes.getAudioParameterFloatAttributes()),
  31. unsnappedDefault (valueRange.convertTo0to1 (defaultParameterValue)),
  32. discrete (attributes.getDiscrete()),
  33. boolean (attributes.getBoolean())
  34. {
  35. }
  36. float AudioProcessorValueTreeState::Parameter::getDefaultValue() const { return unsnappedDefault; }
  37. int AudioProcessorValueTreeState::Parameter::getNumSteps() const { return RangedAudioParameter::getNumSteps(); }
  38. bool AudioProcessorValueTreeState::Parameter::isDiscrete() const { return discrete; }
  39. bool AudioProcessorValueTreeState::Parameter::isBoolean() const { return boolean; }
  40. void AudioProcessorValueTreeState::Parameter::valueChanged (float newValue)
  41. {
  42. if (approximatelyEqual ((float) lastValue, newValue))
  43. return;
  44. lastValue = newValue;
  45. NullCheckedInvocation::invoke (onValueChanged);
  46. }
  47. //==============================================================================
  48. class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorParameter::Listener
  49. {
  50. private:
  51. using Listener = AudioProcessorValueTreeState::Listener;
  52. public:
  53. explicit ParameterAdapter (RangedAudioParameter& parameterIn)
  54. : parameter (parameterIn),
  55. // For legacy reasons, the unnormalised value should *not* be snapped on construction
  56. unnormalisedValue (getRange().convertFrom0to1 (parameter.getDefaultValue()))
  57. {
  58. parameter.addListener (this);
  59. if (auto* ptr = dynamic_cast<Parameter*> (&parameter))
  60. ptr->onValueChanged = [this] { parameterValueChanged ({}, {}); };
  61. }
  62. ~ParameterAdapter() override { parameter.removeListener (this); }
  63. void addListener (Listener* l) { listeners.add (l); }
  64. void removeListener (Listener* l) { listeners.remove (l); }
  65. RangedAudioParameter& getParameter() { return parameter; }
  66. const RangedAudioParameter& getParameter() const { return parameter; }
  67. const NormalisableRange<float>& getRange() const { return parameter.getNormalisableRange(); }
  68. float getDenormalisedDefaultValue() const { return denormalise (parameter.getDefaultValue()); }
  69. void setDenormalisedValue (float value)
  70. {
  71. if (! approximatelyEqual (value, (float) unnormalisedValue))
  72. setNormalisedValue (normalise (value));
  73. }
  74. float getDenormalisedValueForText (const String& text) const
  75. {
  76. return denormalise (parameter.getValueForText (text));
  77. }
  78. String getTextForDenormalisedValue (float value) const
  79. {
  80. return parameter.getText (normalise (value), 0);
  81. }
  82. float getDenormalisedValue() const { return unnormalisedValue; }
  83. std::atomic<float>& getRawDenormalisedValue() { return unnormalisedValue; }
  84. bool flushToTree (const Identifier& key, UndoManager* um)
  85. {
  86. auto needsUpdateTestValue = true;
  87. if (! needsUpdate.compare_exchange_strong (needsUpdateTestValue, false))
  88. return false;
  89. if (auto* valueProperty = tree.getPropertyPointer (key))
  90. {
  91. if (! approximatelyEqual ((float) *valueProperty, unnormalisedValue.load()))
  92. {
  93. ScopedValueSetter<bool> svs (ignoreParameterChangedCallbacks, true);
  94. tree.setProperty (key, unnormalisedValue.load(), um);
  95. }
  96. }
  97. else
  98. {
  99. tree.setProperty (key, unnormalisedValue.load(), nullptr);
  100. }
  101. return true;
  102. }
  103. ValueTree tree;
  104. private:
  105. void parameterGestureChanged (int, bool) override {}
  106. void parameterValueChanged (int, float) override
  107. {
  108. const auto newValue = denormalise (parameter.getValue());
  109. if (! listenersNeedCalling && approximatelyEqual ((float) unnormalisedValue, newValue))
  110. return;
  111. unnormalisedValue = newValue;
  112. listeners.call ([this] (Listener& l) { l.parameterChanged (parameter.paramID, unnormalisedValue); });
  113. listenersNeedCalling = false;
  114. needsUpdate = true;
  115. }
  116. float denormalise (float normalised) const
  117. {
  118. return getParameter().convertFrom0to1 (normalised);
  119. }
  120. float normalise (float denormalised) const
  121. {
  122. return getParameter().convertTo0to1 (denormalised);
  123. }
  124. void setNormalisedValue (float value)
  125. {
  126. if (ignoreParameterChangedCallbacks)
  127. return;
  128. parameter.setValueNotifyingHost (value);
  129. }
  130. class LockedListeners
  131. {
  132. public:
  133. template <typename Fn>
  134. void call (Fn&& fn)
  135. {
  136. const CriticalSection::ScopedLockType lock (mutex);
  137. listeners.call (std::forward<Fn> (fn));
  138. }
  139. void add (Listener* l)
  140. {
  141. const CriticalSection::ScopedLockType lock (mutex);
  142. listeners.add (l);
  143. }
  144. void remove (Listener* l)
  145. {
  146. const CriticalSection::ScopedLockType lock (mutex);
  147. listeners.remove (l);
  148. }
  149. private:
  150. CriticalSection mutex;
  151. ListenerList<Listener> listeners;
  152. };
  153. RangedAudioParameter& parameter;
  154. LockedListeners listeners;
  155. std::atomic<float> unnormalisedValue { 0.0f };
  156. std::atomic<bool> needsUpdate { true }, listenersNeedCalling { true };
  157. bool ignoreParameterChangedCallbacks { false };
  158. };
  159. //==============================================================================
  160. AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo,
  161. UndoManager* undoManagerToUse,
  162. const Identifier& valueTreeType,
  163. ParameterLayout parameterLayout)
  164. : AudioProcessorValueTreeState (processorToConnectTo, undoManagerToUse)
  165. {
  166. struct PushBackVisitor : ParameterLayout::Visitor
  167. {
  168. explicit PushBackVisitor (AudioProcessorValueTreeState& stateIn)
  169. : state (&stateIn) {}
  170. void visit (std::unique_ptr<RangedAudioParameter> param) const override
  171. {
  172. if (param == nullptr)
  173. {
  174. jassertfalse;
  175. return;
  176. }
  177. state->addParameterAdapter (*param);
  178. state->processor.addParameter (param.release());
  179. }
  180. void visit (std::unique_ptr<AudioProcessorParameterGroup> group) const override
  181. {
  182. if (group == nullptr)
  183. {
  184. jassertfalse;
  185. return;
  186. }
  187. for (const auto param : group->getParameters (true))
  188. {
  189. if (const auto rangedParam = dynamic_cast<RangedAudioParameter*> (param))
  190. {
  191. state->addParameterAdapter (*rangedParam);
  192. }
  193. else
  194. {
  195. // If you hit this assertion then you are attempting to add a parameter that is
  196. // not derived from RangedAudioParameter to the AudioProcessorValueTreeState.
  197. jassertfalse;
  198. }
  199. }
  200. state->processor.addParameterGroup (std::move (group));
  201. }
  202. AudioProcessorValueTreeState* state;
  203. };
  204. for (auto& item : parameterLayout.parameters)
  205. item->accept (PushBackVisitor (*this));
  206. state = ValueTree (valueTreeType);
  207. }
  208. AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
  209. : processor (p), undoManager (um)
  210. {
  211. startTimerHz (10);
  212. state.addListener (this);
  213. }
  214. AudioProcessorValueTreeState::~AudioProcessorValueTreeState()
  215. {
  216. stopTimer();
  217. }
  218. //==============================================================================
  219. RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID,
  220. const String& paramName,
  221. const String& labelText,
  222. NormalisableRange<float> range,
  223. float defaultVal,
  224. std::function<String (float)> valueToTextFunction,
  225. std::function<float (const String&)> textToValueFunction,
  226. bool isMetaParameter,
  227. bool isAutomatableParameter,
  228. bool isDiscreteParameter,
  229. AudioProcessorParameter::Category category,
  230. bool isBooleanParameter)
  231. {
  232. auto attributes = AudioProcessorValueTreeStateParameterAttributes()
  233. .withLabel (labelText)
  234. .withStringFromValueFunction ([fn = std::move (valueToTextFunction)] (float v, int) { return fn (v); })
  235. .withValueFromStringFunction (std::move (textToValueFunction))
  236. .withMeta (isMetaParameter)
  237. .withAutomatable (isAutomatableParameter)
  238. .withDiscrete (isDiscreteParameter)
  239. .withCategory (category)
  240. .withBoolean (isBooleanParameter);
  241. return createAndAddParameter (std::make_unique<Parameter> (paramID,
  242. paramName,
  243. range,
  244. defaultVal,
  245. std::move (attributes)));
  246. }
  247. RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (std::unique_ptr<RangedAudioParameter> param)
  248. {
  249. if (param == nullptr)
  250. return nullptr;
  251. // All parameters must be created before giving this manager a ValueTree state!
  252. jassert (! state.isValid());
  253. if (getParameter (param->paramID) != nullptr)
  254. return nullptr;
  255. addParameterAdapter (*param);
  256. processor.addParameter (param.get());
  257. return param.release();
  258. }
  259. //==============================================================================
  260. void AudioProcessorValueTreeState::addParameterAdapter (RangedAudioParameter& param)
  261. {
  262. adapterTable.emplace (param.paramID, std::make_unique<ParameterAdapter> (param));
  263. }
  264. AudioProcessorValueTreeState::ParameterAdapter* AudioProcessorValueTreeState::getParameterAdapter (StringRef paramID) const
  265. {
  266. auto it = adapterTable.find (paramID);
  267. return it == adapterTable.end() ? nullptr : it->second.get();
  268. }
  269. void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
  270. {
  271. if (auto* p = getParameterAdapter (paramID))
  272. p->addListener (listener);
  273. }
  274. void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
  275. {
  276. if (auto* p = getParameterAdapter (paramID))
  277. p->removeListener (listener);
  278. }
  279. Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
  280. {
  281. if (auto* adapter = getParameterAdapter (paramID))
  282. if (adapter->tree.isValid())
  283. return adapter->tree.getPropertyAsValue (valuePropertyID, undoManager);
  284. return {};
  285. }
  286. NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
  287. {
  288. if (auto* p = getParameterAdapter (paramID))
  289. return p->getRange();
  290. return {};
  291. }
  292. RangedAudioParameter* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
  293. {
  294. if (auto adapter = getParameterAdapter (paramID))
  295. return &adapter->getParameter();
  296. return nullptr;
  297. }
  298. std::atomic<float>* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
  299. {
  300. if (auto* p = getParameterAdapter (paramID))
  301. return &p->getRawDenormalisedValue();
  302. return nullptr;
  303. }
  304. ValueTree AudioProcessorValueTreeState::copyState()
  305. {
  306. ScopedLock lock (valueTreeChanging);
  307. flushParameterValuesToValueTree();
  308. return state.createCopy();
  309. }
  310. void AudioProcessorValueTreeState::replaceState (const ValueTree& newState)
  311. {
  312. ScopedLock lock (valueTreeChanging);
  313. state = newState;
  314. if (undoManager != nullptr)
  315. undoManager->clearUndoHistory();
  316. }
  317. void AudioProcessorValueTreeState::setNewState (ValueTree vt)
  318. {
  319. jassert (vt.getParent() == state);
  320. if (auto* p = getParameterAdapter (vt.getProperty (idPropertyID).toString()))
  321. {
  322. p->tree = vt;
  323. p->setDenormalisedValue (p->tree.getProperty (valuePropertyID, p->getDenormalisedDefaultValue()));
  324. }
  325. }
  326. void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
  327. {
  328. ScopedLock lock (valueTreeChanging);
  329. for (auto& p : adapterTable)
  330. p.second->tree = ValueTree();
  331. for (const auto& child : state)
  332. setNewState (child);
  333. for (auto& p : adapterTable)
  334. {
  335. auto& adapter = *p.second;
  336. if (! adapter.tree.isValid())
  337. {
  338. adapter.tree = ValueTree (valueType);
  339. adapter.tree.setProperty (idPropertyID, adapter.getParameter().paramID, nullptr);
  340. state.appendChild (adapter.tree, nullptr);
  341. }
  342. }
  343. flushParameterValuesToValueTree();
  344. }
  345. void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier&)
  346. {
  347. if (tree.hasType (valueType) && tree.getParent() == state)
  348. setNewState (tree);
  349. }
  350. void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
  351. {
  352. if (parent == state && tree.hasType (valueType))
  353. setNewState (tree);
  354. }
  355. void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
  356. {
  357. if (v == state)
  358. updateParameterConnectionsToChildTrees();
  359. }
  360. bool AudioProcessorValueTreeState::flushParameterValuesToValueTree()
  361. {
  362. ScopedLock lock (valueTreeChanging);
  363. bool anyUpdated = false;
  364. for (auto& p : adapterTable)
  365. anyUpdated |= p.second->flushToTree (valuePropertyID, undoManager);
  366. return anyUpdated;
  367. }
  368. void AudioProcessorValueTreeState::timerCallback()
  369. {
  370. auto anythingUpdated = flushParameterValuesToValueTree();
  371. startTimer (anythingUpdated ? 1000 / 50
  372. : jlimit (50, 500, getTimerInterval() + 20));
  373. }
  374. //==============================================================================
  375. template <typename Attachment, typename Control>
  376. std::unique_ptr<Attachment> makeAttachment (const AudioProcessorValueTreeState& stateToUse,
  377. const String& parameterID,
  378. Control& control)
  379. {
  380. if (auto* parameter = stateToUse.getParameter (parameterID))
  381. return std::make_unique<Attachment> (*parameter, control, stateToUse.undoManager);
  382. jassertfalse;
  383. return nullptr;
  384. }
  385. AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& stateToUse,
  386. const String& parameterID,
  387. Slider& slider)
  388. : attachment (makeAttachment<SliderParameterAttachment> (stateToUse, parameterID, slider))
  389. {
  390. }
  391. AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& stateToUse,
  392. const String& parameterID,
  393. ComboBox& combo)
  394. : attachment (makeAttachment<ComboBoxParameterAttachment> (stateToUse, parameterID, combo))
  395. {
  396. }
  397. AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& stateToUse,
  398. const String& parameterID,
  399. Button& button)
  400. : attachment (makeAttachment<ButtonParameterAttachment> (stateToUse, parameterID, button))
  401. {
  402. }
  403. //==============================================================================
  404. //==============================================================================
  405. #if JUCE_UNIT_TESTS
  406. struct ParameterAdapterTests : public UnitTest
  407. {
  408. ParameterAdapterTests()
  409. : UnitTest ("Parameter Adapter", UnitTestCategories::audioProcessorParameters)
  410. {}
  411. void runTest() override
  412. {
  413. beginTest ("The default value is returned correctly");
  414. {
  415. const auto test = [&] (NormalisableRange<float> range, float value)
  416. {
  417. AudioParameterFloat param ({}, {}, range, value);
  418. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  419. expectEquals (adapter.getDenormalisedDefaultValue(), value);
  420. };
  421. test ({ -100, 100 }, 0);
  422. test ({ -2.5, 12.5 }, 10);
  423. }
  424. beginTest ("Denormalised parameter values can be retrieved");
  425. {
  426. const auto test = [&] (NormalisableRange<float> range, float value)
  427. {
  428. AudioParameterFloat param ({}, {}, range, {});
  429. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  430. adapter.setDenormalisedValue (value);
  431. expectEquals (adapter.getDenormalisedValue(), value);
  432. expectEquals (adapter.getRawDenormalisedValue().load(), value);
  433. };
  434. test ({ -20, -10 }, -15);
  435. test ({ 0, 7.5 }, 2.5);
  436. }
  437. beginTest ("Floats can be converted to text");
  438. {
  439. const auto test = [&] (NormalisableRange<float> range, float value, String expected)
  440. {
  441. AudioParameterFloat param ({}, {}, range, {});
  442. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  443. expectEquals (adapter.getTextForDenormalisedValue (value), expected);
  444. };
  445. test ({ -100, 100 }, 0, "0.0000000");
  446. test ({ -2.5, 12.5 }, 10, "10.0000000");
  447. test ({ -20, -10 }, -15, "-15.0000000");
  448. test ({ 0, 7.5 }, 2.5, "2.5000000");
  449. }
  450. beginTest ("Text can be converted to floats");
  451. {
  452. const auto test = [&] (NormalisableRange<float> range, String text, float expected)
  453. {
  454. AudioParameterFloat param ({}, {}, range, {});
  455. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  456. expectEquals (adapter.getDenormalisedValueForText (text), expected);
  457. };
  458. test ({ -100, 100 }, "0.0", 0);
  459. test ({ -2.5, 12.5 }, "10.0", 10);
  460. test ({ -20, -10 }, "-15.0", -15);
  461. test ({ 0, 7.5 }, "2.5", 2.5);
  462. }
  463. }
  464. };
  465. static ParameterAdapterTests parameterAdapterTests;
  466. namespace
  467. {
  468. template <typename ValueType>
  469. inline bool operator== (const NormalisableRange<ValueType>& a,
  470. const NormalisableRange<ValueType>& b)
  471. {
  472. return std::tie (a.start, a.end, a.interval, a.skew, a.symmetricSkew)
  473. == std::tie (b.start, b.end, b.interval, b.skew, b.symmetricSkew);
  474. }
  475. template <typename ValueType>
  476. inline bool operator!= (const NormalisableRange<ValueType>& a,
  477. const NormalisableRange<ValueType>& b)
  478. {
  479. return ! (a == b);
  480. }
  481. } // namespace
  482. class AudioProcessorValueTreeStateTests : public UnitTest
  483. {
  484. private:
  485. using Parameter = AudioProcessorValueTreeState::Parameter;
  486. using ParameterGroup = AudioProcessorParameterGroup;
  487. using ParameterLayout = AudioProcessorValueTreeState::ParameterLayout;
  488. using Attributes = AudioProcessorValueTreeStateParameterAttributes;
  489. class TestAudioProcessor : public AudioProcessor
  490. {
  491. public:
  492. TestAudioProcessor() = default;
  493. explicit TestAudioProcessor (ParameterLayout layout)
  494. : state (*this, nullptr, "state", std::move (layout)) {}
  495. const String getName() const override { return {}; }
  496. void prepareToPlay (double, int) override {}
  497. void releaseResources() override {}
  498. void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
  499. using AudioProcessor::processBlock;
  500. double getTailLengthSeconds() const override { return {}; }
  501. bool acceptsMidi() const override { return {}; }
  502. bool producesMidi() const override { return {}; }
  503. AudioProcessorEditor* createEditor() override { return {}; }
  504. bool hasEditor() const override { return {}; }
  505. int getNumPrograms() override { return 1; }
  506. int getCurrentProgram() override { return {}; }
  507. void setCurrentProgram (int) override {}
  508. const String getProgramName (int) override { return {}; }
  509. void changeProgramName (int, const String&) override {}
  510. void getStateInformation (MemoryBlock&) override {}
  511. void setStateInformation (const void*, int) override {}
  512. AudioProcessorValueTreeState state { *this, nullptr };
  513. };
  514. struct Listener final : public AudioProcessorValueTreeState::Listener
  515. {
  516. void parameterChanged (const String& idIn, float valueIn) override
  517. {
  518. id = idIn;
  519. value = valueIn;
  520. }
  521. String id;
  522. float value{};
  523. };
  524. public:
  525. AudioProcessorValueTreeStateTests()
  526. : UnitTest ("Audio Processor Value Tree State", UnitTestCategories::audioProcessorParameters)
  527. {}
  528. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
  529. void runTest() override
  530. {
  531. ScopedJuceInitialiser_GUI scopedJuceInitialiser_gui;
  532. beginTest ("After calling createAndAddParameter, the number of parameters increases by one");
  533. {
  534. TestAudioProcessor proc;
  535. proc.state.createAndAddParameter (std::make_unique<Parameter> (
  536. String(),
  537. String(),
  538. NormalisableRange<float>(),
  539. 0.0f));
  540. expectEquals (proc.getParameters().size(), 1);
  541. }
  542. beginTest ("After creating a normal named parameter, we can later retrieve that parameter");
  543. {
  544. TestAudioProcessor proc;
  545. const auto key = "id";
  546. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  547. key,
  548. String(),
  549. NormalisableRange<float>(),
  550. 0.0f));
  551. expect (proc.state.getParameter (key) == param);
  552. }
  553. beginTest ("After construction, the value tree has the expected format");
  554. {
  555. TestAudioProcessor proc ({
  556. std::make_unique<AudioProcessorParameterGroup> ("A", "", "",
  557. std::make_unique<AudioParameterBool> ("a", "", false),
  558. std::make_unique<AudioParameterFloat> ("b", "", NormalisableRange<float>{}, 0.0f)),
  559. std::make_unique<AudioProcessorParameterGroup> ("B", "", "",
  560. std::make_unique<AudioParameterInt> ("c", "", 0, 1, 0),
  561. std::make_unique<AudioParameterChoice> ("d", "", StringArray { "foo", "bar" }, 0)) });
  562. const auto valueTree = proc.state.copyState();
  563. expectEquals (valueTree.getNumChildren(), 4);
  564. for (auto child : valueTree)
  565. {
  566. expect (child.hasType ("PARAM"));
  567. expect (child.hasProperty ("id"));
  568. expect (child.hasProperty ("value"));
  569. }
  570. }
  571. beginTest ("Meta parameters can be created");
  572. {
  573. TestAudioProcessor proc;
  574. const auto key = "id";
  575. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  576. key,
  577. String(),
  578. NormalisableRange<float>(),
  579. 0.0f,
  580. Attributes().withMeta (true)));
  581. expect (param->isMetaParameter());
  582. }
  583. beginTest ("Automatable parameters can be created");
  584. {
  585. TestAudioProcessor proc;
  586. const auto key = "id";
  587. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  588. key,
  589. String(),
  590. NormalisableRange<float>(),
  591. 0.0f,
  592. Attributes().withAutomatable (true)));
  593. expect (param->isAutomatable());
  594. }
  595. beginTest ("Discrete parameters can be created");
  596. {
  597. TestAudioProcessor proc;
  598. const auto key = "id";
  599. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  600. key,
  601. String(),
  602. NormalisableRange<float>(),
  603. 0.0f,
  604. Attributes().withDiscrete (true)));
  605. expect (param->isDiscrete());
  606. }
  607. beginTest ("Custom category parameters can be created");
  608. {
  609. TestAudioProcessor proc;
  610. const auto key = "id";
  611. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  612. key,
  613. String(),
  614. NormalisableRange<float>(),
  615. 0.0f,
  616. Attributes().withCategory (AudioProcessorParameter::Category::inputMeter)));
  617. expect (param->category == AudioProcessorParameter::Category::inputMeter);
  618. }
  619. beginTest ("Boolean parameters can be created");
  620. {
  621. TestAudioProcessor proc;
  622. const auto key = "id";
  623. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  624. key,
  625. String(),
  626. NormalisableRange<float>(),
  627. 0.0f,
  628. Attributes().withBoolean (true)));
  629. expect (param->isBoolean());
  630. }
  631. beginTest ("After creating a custom named parameter, we can later retrieve that parameter");
  632. {
  633. const auto key = "id";
  634. auto param = std::make_unique<AudioParameterBool> (key, "", false);
  635. const auto paramPtr = param.get();
  636. TestAudioProcessor proc (std::move (param));
  637. expect (proc.state.getParameter (key) == paramPtr);
  638. }
  639. beginTest ("After adding a normal parameter that already exists, the AudioProcessor parameters are unchanged");
  640. {
  641. TestAudioProcessor proc;
  642. const auto key = "id";
  643. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  644. key,
  645. String(),
  646. NormalisableRange<float>(),
  647. 0.0f));
  648. proc.state.createAndAddParameter (std::make_unique<Parameter> (
  649. key,
  650. String(),
  651. NormalisableRange<float>(),
  652. 0.0f));
  653. expectEquals (proc.getParameters().size(), 1);
  654. expect (proc.getParameters().getFirst() == param);
  655. }
  656. beginTest ("After setting a parameter value, that value is reflected in the state");
  657. {
  658. TestAudioProcessor proc;
  659. const auto key = "id";
  660. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  661. key,
  662. String(),
  663. NormalisableRange<float>(),
  664. 0.0f));
  665. const auto value = 0.5f;
  666. param->setValueNotifyingHost (value);
  667. expectEquals (proc.state.getRawParameterValue (key)->load(), value);
  668. }
  669. beginTest ("After adding an APVTS::Parameter, its value is the default value");
  670. {
  671. TestAudioProcessor proc;
  672. const auto key = "id";
  673. const auto value = 5.0f;
  674. proc.state.createAndAddParameter (std::make_unique<Parameter> (
  675. key,
  676. String(),
  677. NormalisableRange<float> (0.0f, 100.0f, 10.0f),
  678. value));
  679. expectEquals (proc.state.getRawParameterValue (key)->load(), value);
  680. }
  681. beginTest ("Listeners receive notifications when parameters change");
  682. {
  683. Listener listener;
  684. TestAudioProcessor proc;
  685. const auto key = "id";
  686. const auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  687. key,
  688. String(),
  689. NormalisableRange<float>(),
  690. 0.0f));
  691. proc.state.addParameterListener (key, &listener);
  692. const auto value = 0.5f;
  693. param->setValueNotifyingHost (value);
  694. expectEquals (listener.id, String { key });
  695. expectEquals (listener.value, value);
  696. }
  697. beginTest ("Bool parameters have a range of 0-1");
  698. {
  699. const auto key = "id";
  700. TestAudioProcessor proc (std::make_unique<AudioParameterBool> (key, "", false));
  701. expect (proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, 1.0f, 1.0f));
  702. }
  703. beginTest ("Float parameters retain their specified range");
  704. {
  705. const auto key = "id";
  706. const auto range = NormalisableRange<float> { -100, 100, 0.7f, 0.2f, true };
  707. TestAudioProcessor proc (std::make_unique<AudioParameterFloat> (key, "", range, 0.0f));
  708. expect (proc.state.getParameterRange (key) == range);
  709. }
  710. beginTest ("Int parameters retain their specified range");
  711. {
  712. const auto key = "id";
  713. const auto min = -27;
  714. const auto max = 53;
  715. TestAudioProcessor proc (std::make_unique<AudioParameterInt> (key, "", min, max, 0));
  716. expect (proc.state.getParameterRange (key) == NormalisableRange<float> (float (min), float (max), 1.0f));
  717. }
  718. beginTest ("Choice parameters retain their specified range");
  719. {
  720. const auto key = "id";
  721. const auto choices = StringArray { "", "", "" };
  722. TestAudioProcessor proc (std::make_unique<AudioParameterChoice> (key, "", choices, 0));
  723. expect (proc.state.getParameterRange (key) == NormalisableRange<float> (0.0f, (float) (choices.size() - 1), 1.0f));
  724. expect (proc.state.getParameter (key)->getNumSteps() == choices.size());
  725. }
  726. beginTest ("When the parameter value is changed, normal parameter values are updated");
  727. {
  728. TestAudioProcessor proc;
  729. const auto key = "id";
  730. const auto initialValue = 0.2f;
  731. auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (
  732. key,
  733. String(),
  734. NormalisableRange<float>(),
  735. initialValue));
  736. proc.state.state = ValueTree { "state" };
  737. auto value = proc.state.getParameterAsValue (key);
  738. expectEquals (float (value.getValue()), initialValue);
  739. const auto newValue = 0.75f;
  740. value = newValue;
  741. expectEquals (param->getValue(), newValue);
  742. expectEquals (proc.state.getRawParameterValue (key)->load(), newValue);
  743. }
  744. beginTest ("When the parameter value is changed, custom parameter values are updated");
  745. {
  746. const auto key = "id";
  747. const auto choices = StringArray ("foo", "bar", "baz");
  748. auto param = std::make_unique<AudioParameterChoice> (key, "", choices, 0);
  749. const auto paramPtr = param.get();
  750. TestAudioProcessor proc (std::move (param));
  751. const auto newValue = 2.0f;
  752. auto value = proc.state.getParameterAsValue (key);
  753. value = newValue;
  754. expectEquals (paramPtr->getCurrentChoiceName(), choices[int (newValue)]);
  755. expectEquals (proc.state.getRawParameterValue (key)->load(), newValue);
  756. }
  757. beginTest ("When the parameter value is changed, listeners are notified");
  758. {
  759. Listener listener;
  760. TestAudioProcessor proc;
  761. const auto key = "id";
  762. proc.state.createAndAddParameter (std::make_unique<Parameter> (
  763. key,
  764. String(),
  765. NormalisableRange<float>(),
  766. 0.0f));
  767. proc.state.addParameterListener (key, &listener);
  768. proc.state.state = ValueTree { "state" };
  769. const auto newValue = 0.75f;
  770. proc.state.getParameterAsValue (key) = newValue;
  771. expectEquals (listener.value, newValue);
  772. expectEquals (listener.id, String { key });
  773. }
  774. beginTest ("When the parameter value is changed, listeners are notified");
  775. {
  776. const auto key = "id";
  777. const auto choices = StringArray { "foo", "bar", "baz" };
  778. Listener listener;
  779. TestAudioProcessor proc (std::make_unique<AudioParameterChoice> (key, "", choices, 0));
  780. proc.state.addParameterListener (key, &listener);
  781. const auto newValue = 2.0f;
  782. proc.state.getParameterAsValue (key) = newValue;
  783. expectEquals (listener.value, newValue);
  784. expectEquals (listener.id, String (key));
  785. }
  786. }
  787. JUCE_END_IGNORE_WARNINGS_MSVC
  788. };
  789. static AudioProcessorValueTreeStateTests audioProcessorValueTreeStateTests;
  790. #endif
  791. } // namespace juce