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.

966 lines
37KB

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