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.

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