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.

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