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.

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