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.

1229 lines
45KB

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