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.

1185 lines
44KB

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