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.

1202 lines
45KB

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