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.

986 lines
36KB

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