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.

1181 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 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. unsnappedDefault (valueRange.convertTo0to1 (defaultValue)),
  44. metaParameter (isMetaParameter),
  45. automatable (isAutomatableParameter),
  46. discrete (isDiscrete),
  47. boolean (isBoolean)
  48. {
  49. }
  50. float AudioProcessorValueTreeState::Parameter::getDefaultValue() const { return unsnappedDefault; }
  51. int AudioProcessorValueTreeState::Parameter::getNumSteps() const { return RangedAudioParameter::getNumSteps(); }
  52. bool AudioProcessorValueTreeState::Parameter::isMetaParameter() const { return metaParameter; }
  53. bool AudioProcessorValueTreeState::Parameter::isAutomatable() const { return automatable; }
  54. bool AudioProcessorValueTreeState::Parameter::isDiscrete() const { return discrete; }
  55. bool AudioProcessorValueTreeState::Parameter::isBoolean() const { return boolean; }
  56. //==============================================================================
  57. class AudioProcessorValueTreeState::ParameterAdapter : private AudioProcessorParameter::Listener
  58. {
  59. private:
  60. using Listener = AudioProcessorValueTreeState::Listener;
  61. public:
  62. explicit ParameterAdapter (RangedAudioParameter& parameterIn)
  63. : parameter (parameterIn),
  64. // For legacy reasons, the unnormalised value should *not* be snapped on construction
  65. unnormalisedValue (getRange().convertFrom0to1 (parameter.getDefaultValue()))
  66. {
  67. parameter.addListener (this);
  68. }
  69. ~ParameterAdapter() noexcept { parameter.removeListener (this); }
  70. void addListener (Listener* l) { listeners.add (l); }
  71. void removeListener (Listener* l) { listeners.remove (l); }
  72. RangedAudioParameter& getParameter() { return parameter; }
  73. const RangedAudioParameter& getParameter() const { return parameter; }
  74. const NormalisableRange<float>& getRange() const { return parameter.getNormalisableRange(); }
  75. float getDenormalisedDefaultValue() const { return denormalise (parameter.getDefaultValue()); }
  76. void setDenormalisedValue (float value)
  77. {
  78. if (value == unnormalisedValue)
  79. return;
  80. setNormalisedValue (normalise (value));
  81. }
  82. float getDenormalisedValueForText (const String& text) const
  83. {
  84. return denormalise (parameter.getValueForText (text));
  85. }
  86. String getTextForDenormalisedValue (float value) const
  87. {
  88. return parameter.getText (normalise (value), 0);
  89. }
  90. float getDenormalisedValue() const { return unnormalisedValue; }
  91. float& getRawDenormalisedValue() { return unnormalisedValue; }
  92. bool flushToTree (ValueTree tree, const Identifier& key, UndoManager* um)
  93. {
  94. auto needsUpdateTestValue = true;
  95. if (! needsUpdate.compare_exchange_strong (needsUpdateTestValue, false))
  96. return false;
  97. if (auto valueProperty = tree.getPropertyPointer (key))
  98. {
  99. if ((float) *valueProperty != unnormalisedValue)
  100. {
  101. ScopedValueSetter<bool> svs (ignoreParameterChangedCallbacks, true);
  102. tree.setProperty (key, unnormalisedValue, um);
  103. }
  104. }
  105. else
  106. {
  107. tree.setProperty (key, unnormalisedValue, nullptr);
  108. }
  109. return true;
  110. }
  111. private:
  112. void parameterGestureChanged (int, bool) override {}
  113. void parameterValueChanged (int, float) override
  114. {
  115. const auto newValue = denormalise (parameter.getValue());
  116. if (unnormalisedValue == newValue && ! listenersNeedCalling)
  117. return;
  118. unnormalisedValue = newValue;
  119. listeners.call ([=](Listener& l) { l.parameterChanged (parameter.paramID, unnormalisedValue); });
  120. listenersNeedCalling = false;
  121. needsUpdate = true;
  122. }
  123. float denormalise (float normalised) const
  124. {
  125. return getParameter().convertFrom0to1 (normalised);
  126. }
  127. float normalise (float denormalised) const
  128. {
  129. return getParameter().convertTo0to1 (denormalised);
  130. }
  131. void setNormalisedValue (float value)
  132. {
  133. if (ignoreParameterChangedCallbacks)
  134. return;
  135. parameter.setValueNotifyingHost (value);
  136. }
  137. RangedAudioParameter& parameter;
  138. ListenerList<Listener> listeners;
  139. float unnormalisedValue{};
  140. std::atomic<bool> needsUpdate { true };
  141. bool listenersNeedCalling { true }, ignoreParameterChangedCallbacks { false };
  142. };
  143. //==============================================================================
  144. AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo,
  145. UndoManager* undoManagerToUse,
  146. const juce::Identifier& valueTreeType,
  147. ParameterLayout parameterLayout)
  148. : AudioProcessorValueTreeState (processorToConnectTo, undoManagerToUse)
  149. {
  150. struct PushBackVisitor : ParameterLayout::Visitor
  151. {
  152. explicit PushBackVisitor (AudioProcessorValueTreeState& stateIn)
  153. : state (&stateIn) {}
  154. void visit (std::unique_ptr<RangedAudioParameter> param) const override
  155. {
  156. if (param == nullptr)
  157. {
  158. jassertfalse;
  159. return;
  160. }
  161. state->parameters.emplace_back (std::make_unique<ParameterAdapter> (*param));
  162. state->processor.addParameter (param.release());
  163. }
  164. void visit (std::unique_ptr<AudioProcessorParameterGroup> group) const override
  165. {
  166. if (group == nullptr)
  167. {
  168. jassertfalse;
  169. return;
  170. }
  171. for (const auto param : group->getParameters (true))
  172. {
  173. if (const auto rangedParam = dynamic_cast<RangedAudioParameter*> (param))
  174. {
  175. state->parameters.emplace_back (std::make_unique<ParameterAdapter> (*rangedParam));
  176. }
  177. else
  178. {
  179. // If you hit this assertion then you are attempting to add a parameter that is
  180. // not derived from RangedAudioParameter to the AudioProcessorValueTreeState.
  181. jassertfalse;
  182. }
  183. }
  184. state->processor.addParameterGroup (move (group));
  185. }
  186. AudioProcessorValueTreeState* state;
  187. };
  188. for (auto& item : parameterLayout.parameters)
  189. item->accept (PushBackVisitor (*this));
  190. state = ValueTree (valueTreeType);
  191. }
  192. AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
  193. : processor (p), undoManager (um)
  194. {
  195. startTimerHz (10);
  196. state.addListener (this);
  197. }
  198. AudioProcessorValueTreeState::~AudioProcessorValueTreeState() {}
  199. //==============================================================================
  200. RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID,
  201. const String& paramName,
  202. const String& labelText,
  203. NormalisableRange<float> range,
  204. float defaultVal,
  205. std::function<String (float)> valueToTextFunction,
  206. std::function<float (const String&)> textToValueFunction,
  207. bool isMetaParameter,
  208. bool isAutomatableParameter,
  209. bool isDiscreteParameter,
  210. AudioProcessorParameter::Category category,
  211. bool isBooleanParameter)
  212. {
  213. return createAndAddParameter (std::make_unique<Parameter> (paramID,
  214. paramName,
  215. labelText,
  216. range,
  217. defaultVal,
  218. std::move (valueToTextFunction),
  219. std::move (textToValueFunction),
  220. isMetaParameter,
  221. isAutomatableParameter,
  222. isDiscreteParameter,
  223. category,
  224. isBooleanParameter));
  225. }
  226. RangedAudioParameter* AudioProcessorValueTreeState::createAndAddParameter (std::unique_ptr<RangedAudioParameter> param)
  227. {
  228. // All parameters must be created before giving this manager a ValueTree state!
  229. jassert (! state.isValid());
  230. if (getParameter (param->paramID) != nullptr)
  231. return nullptr;
  232. parameters.emplace_back (std::make_unique<ParameterAdapter> (*param));
  233. processor.addParameter (param.get());
  234. return param.release();
  235. }
  236. //==============================================================================
  237. AudioProcessorValueTreeState::ParameterAdapter* AudioProcessorValueTreeState::getParameterAdapter (StringRef paramID) const
  238. {
  239. auto it = find_if (std::begin (parameters), std::end (parameters),
  240. [&](const std::unique_ptr<ParameterAdapter>& p) { return p->getParameter().paramID == paramID; });
  241. return it == std::end (parameters) ? nullptr : it->get();
  242. }
  243. void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
  244. {
  245. if (auto* p = getParameterAdapter (paramID))
  246. p->addListener (listener);
  247. }
  248. void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
  249. {
  250. if (auto* p = getParameterAdapter (paramID))
  251. p->removeListener (listener);
  252. }
  253. Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
  254. {
  255. auto v = getChildValueTree (paramID);
  256. if (v.isValid())
  257. return v.getPropertyAsValue (valuePropertyID, undoManager);
  258. return {};
  259. }
  260. NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
  261. {
  262. if (auto* p = getParameterAdapter (paramID))
  263. return p->getRange();
  264. return {};
  265. }
  266. RangedAudioParameter* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
  267. {
  268. if (auto adapter = getParameterAdapter (paramID))
  269. return &adapter->getParameter();
  270. return nullptr;
  271. }
  272. float* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
  273. {
  274. if (auto* p = getParameterAdapter (paramID))
  275. return &p->getRawDenormalisedValue();
  276. return nullptr;
  277. }
  278. ValueTree AudioProcessorValueTreeState::copyState()
  279. {
  280. ScopedLock lock (valueTreeChanging);
  281. flushParameterValuesToValueTree();
  282. return state.createCopy();
  283. }
  284. void AudioProcessorValueTreeState::replaceState (const ValueTree& newState)
  285. {
  286. ScopedLock lock (valueTreeChanging);
  287. state = newState;
  288. if (undoManager != nullptr)
  289. undoManager->clearUndoHistory();
  290. }
  291. ValueTree AudioProcessorValueTreeState::getChildValueTree (const String& paramID) const
  292. {
  293. return { state.getChildWithProperty (idPropertyID, paramID) };
  294. }
  295. ValueTree AudioProcessorValueTreeState::getOrCreateChildValueTree (const String& paramID)
  296. {
  297. auto v = getChildValueTree (paramID);
  298. if (! v.isValid())
  299. {
  300. v = ValueTree (valueType);
  301. v.setProperty (idPropertyID, paramID, nullptr);
  302. state.appendChild (v, nullptr);
  303. }
  304. return v;
  305. }
  306. void AudioProcessorValueTreeState::setNewState (ParameterAdapter& p)
  307. {
  308. const auto tree = getOrCreateChildValueTree (p.getParameter().paramID);
  309. p.setDenormalisedValue (tree.getProperty (valuePropertyID, p.getDenormalisedDefaultValue()));
  310. }
  311. void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
  312. {
  313. ScopedLock lock (valueTreeChanging);
  314. for (const auto& p : parameters)
  315. setNewState (*p);
  316. }
  317. void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
  318. {
  319. if (! (tree.hasType (valueType) && tree.getParent() == state))
  320. return;
  321. if (property == idPropertyID)
  322. updateParameterConnectionsToChildTrees();
  323. else if (property == valuePropertyID)
  324. if (auto adapter = getParameterAdapter (tree.getProperty (idPropertyID).toString()))
  325. adapter->setDenormalisedValue (tree.getProperty (valuePropertyID));
  326. }
  327. void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
  328. {
  329. if (parent == state && tree.hasType (valueType))
  330. if (auto* param = getParameterAdapter (tree.getProperty (idPropertyID).toString()))
  331. setNewState (*param);
  332. }
  333. void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int)
  334. {
  335. if (parent == state && tree.hasType (valueType))
  336. if (auto* param = getParameterAdapter (tree.getProperty (idPropertyID).toString()))
  337. setNewState (*param);
  338. }
  339. void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
  340. {
  341. if (v == state)
  342. updateParameterConnectionsToChildTrees();
  343. }
  344. void AudioProcessorValueTreeState::valueTreeChildOrderChanged (ValueTree&, int, int) {}
  345. void AudioProcessorValueTreeState::valueTreeParentChanged (ValueTree&) {}
  346. bool AudioProcessorValueTreeState::flushParameterValuesToValueTree()
  347. {
  348. ScopedLock lock (valueTreeChanging);
  349. return std::accumulate (std::begin (parameters), std::end (parameters),
  350. false,
  351. [this](bool anyUpdated, std::unique_ptr<ParameterAdapter>& ap) {
  352. return ap->flushToTree (getChildValueTree (ap->getParameter().paramID),
  353. valuePropertyID,
  354. undoManager)
  355. || anyUpdated;
  356. });
  357. }
  358. void AudioProcessorValueTreeState::timerCallback()
  359. {
  360. auto anythingUpdated = flushParameterValuesToValueTree();
  361. startTimer (anythingUpdated ? 1000 / 50
  362. : jlimit (50, 500, getTimerInterval() + 20));
  363. }
  364. //==============================================================================
  365. struct AttachedControlBase : public AudioProcessorValueTreeState::Listener,
  366. public AsyncUpdater
  367. {
  368. AttachedControlBase (AudioProcessorValueTreeState& s, const String& p)
  369. : state (s), paramID (p), lastValue (0)
  370. {
  371. state.addParameterListener (paramID, this);
  372. }
  373. void removeListener()
  374. {
  375. state.removeParameterListener (paramID, this);
  376. }
  377. void setNewDenormalisedValue (float newDenormalisedValue)
  378. {
  379. if (auto* p = state.getParameter (paramID))
  380. {
  381. const float newValue = state.getParameterRange (paramID)
  382. .convertTo0to1 (newDenormalisedValue);
  383. if (p->getValue() != newValue)
  384. p->setValueNotifyingHost (newValue);
  385. }
  386. }
  387. void sendInitialUpdate()
  388. {
  389. if (auto* v = state.getRawParameterValue (paramID))
  390. parameterChanged (paramID, *v);
  391. }
  392. void parameterChanged (const String&, float newValue) override
  393. {
  394. lastValue = newValue;
  395. if (MessageManager::getInstance()->isThisTheMessageThread())
  396. {
  397. cancelPendingUpdate();
  398. setValue (newValue);
  399. }
  400. else
  401. {
  402. triggerAsyncUpdate();
  403. }
  404. }
  405. void beginParameterChange()
  406. {
  407. if (auto* p = state.getParameter (paramID))
  408. {
  409. if (state.undoManager != nullptr)
  410. state.undoManager->beginNewTransaction();
  411. p->beginChangeGesture();
  412. }
  413. }
  414. void endParameterChange()
  415. {
  416. if (AudioProcessorParameter* p = state.getParameter (paramID))
  417. p->endChangeGesture();
  418. }
  419. void handleAsyncUpdate() override
  420. {
  421. setValue (lastValue);
  422. }
  423. virtual void setValue (float) = 0;
  424. AudioProcessorValueTreeState& state;
  425. String paramID;
  426. float lastValue;
  427. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttachedControlBase)
  428. };
  429. //==============================================================================
  430. struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private AttachedControlBase,
  431. private Slider::Listener
  432. {
  433. Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
  434. : AttachedControlBase (s, p), slider (sl), ignoreCallbacks (false)
  435. {
  436. NormalisableRange<float> range (state.getParameterRange (paramID));
  437. if (range.interval != 0.0f || range.skew != 1.0f)
  438. {
  439. slider.setRange (range.start, range.end, range.interval);
  440. slider.setSkewFactor (range.skew, range.symmetricSkew);
  441. }
  442. else
  443. {
  444. auto convertFrom0To1Function = [range](double currentRangeStart,
  445. double currentRangeEnd,
  446. double normalisedValue) mutable
  447. {
  448. range.start = (float) currentRangeStart;
  449. range.end = (float) currentRangeEnd;
  450. return (double) range.convertFrom0to1 ((float) normalisedValue);
  451. };
  452. auto convertTo0To1Function = [range](double currentRangeStart,
  453. double currentRangeEnd,
  454. double mappedValue) mutable
  455. {
  456. range.start = (float) currentRangeStart;
  457. range.end = (float) currentRangeEnd;
  458. return (double) range.convertTo0to1 ((float) mappedValue);
  459. };
  460. auto snapToLegalValueFunction = [range](double currentRangeStart,
  461. double currentRangeEnd,
  462. double valueToSnap) mutable
  463. {
  464. range.start = (float) currentRangeStart;
  465. range.end = (float) currentRangeEnd;
  466. return (double) range.snapToLegalValue ((float) valueToSnap);
  467. };
  468. slider.setNormalisableRange ({ (double) range.start,
  469. (double) range.end,
  470. convertFrom0To1Function,
  471. convertTo0To1Function,
  472. snapToLegalValueFunction });
  473. }
  474. if (auto* param = state.getParameterAdapter (paramID))
  475. {
  476. slider.valueFromTextFunction = [param](const String& text) { return (double) param->getDenormalisedValueForText (text); };
  477. slider.textFromValueFunction = [param](double value) { return param->getTextForDenormalisedValue ((float) value); };
  478. slider.setDoubleClickReturnValue (true, range.convertFrom0to1 (param->getParameter().getDefaultValue()));
  479. }
  480. sendInitialUpdate();
  481. slider.addListener (this);
  482. }
  483. ~Pimpl()
  484. {
  485. slider.removeListener (this);
  486. removeListener();
  487. }
  488. void setValue (float newValue) override
  489. {
  490. const ScopedLock selfCallbackLock (selfCallbackMutex);
  491. {
  492. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  493. slider.setValue (newValue, sendNotificationSync);
  494. }
  495. }
  496. void sliderValueChanged (Slider* s) override
  497. {
  498. const ScopedLock selfCallbackLock (selfCallbackMutex);
  499. if ((! ignoreCallbacks) && (! ModifierKeys::currentModifiers.isRightButtonDown()))
  500. setNewDenormalisedValue ((float) s->getValue());
  501. }
  502. void sliderDragStarted (Slider*) override { beginParameterChange(); }
  503. void sliderDragEnded (Slider*) override { endParameterChange(); }
  504. Slider& slider;
  505. bool ignoreCallbacks;
  506. CriticalSection selfCallbackMutex;
  507. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  508. };
  509. AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
  510. : pimpl (new Pimpl (s, p, sl))
  511. {
  512. }
  513. AudioProcessorValueTreeState::SliderAttachment::~SliderAttachment() {}
  514. //==============================================================================
  515. struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private AttachedControlBase,
  516. private ComboBox::Listener
  517. {
  518. Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
  519. : AttachedControlBase (s, p), combo (c), ignoreCallbacks (false)
  520. {
  521. sendInitialUpdate();
  522. combo.addListener (this);
  523. }
  524. ~Pimpl()
  525. {
  526. combo.removeListener (this);
  527. removeListener();
  528. }
  529. void setValue (float newValue) override
  530. {
  531. const ScopedLock selfCallbackLock (selfCallbackMutex);
  532. if (state.getParameter (paramID) != nullptr)
  533. {
  534. auto normValue = state.getParameterRange (paramID)
  535. .convertTo0to1 (newValue);
  536. auto index = roundToInt (normValue * (combo.getNumItems() - 1));
  537. if (index != combo.getSelectedItemIndex())
  538. {
  539. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  540. combo.setSelectedItemIndex (index, sendNotificationSync);
  541. }
  542. }
  543. }
  544. void comboBoxChanged (ComboBox*) override
  545. {
  546. const ScopedLock selfCallbackLock (selfCallbackMutex);
  547. if (! ignoreCallbacks)
  548. {
  549. if (auto* p = state.getParameter (paramID))
  550. {
  551. auto newValue = (float) combo.getSelectedItemIndex() / (combo.getNumItems() - 1);
  552. if (p->getValue() != newValue)
  553. {
  554. beginParameterChange();
  555. p->setValueNotifyingHost (newValue);
  556. endParameterChange();
  557. }
  558. }
  559. }
  560. }
  561. ComboBox& combo;
  562. bool ignoreCallbacks;
  563. CriticalSection selfCallbackMutex;
  564. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  565. };
  566. AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
  567. : pimpl (new Pimpl (s, p, c))
  568. {
  569. }
  570. AudioProcessorValueTreeState::ComboBoxAttachment::~ComboBoxAttachment() {}
  571. //==============================================================================
  572. struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private AttachedControlBase,
  573. private Button::Listener
  574. {
  575. Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b)
  576. : AttachedControlBase (s, p), button (b), ignoreCallbacks (false)
  577. {
  578. sendInitialUpdate();
  579. button.addListener (this);
  580. }
  581. ~Pimpl()
  582. {
  583. button.removeListener (this);
  584. removeListener();
  585. }
  586. void setValue (float newValue) override
  587. {
  588. const ScopedLock selfCallbackLock (selfCallbackMutex);
  589. {
  590. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  591. button.setToggleState (newValue >= 0.5f, sendNotificationSync);
  592. }
  593. }
  594. void buttonClicked (Button* b) override
  595. {
  596. const ScopedLock selfCallbackLock (selfCallbackMutex);
  597. if (! ignoreCallbacks)
  598. {
  599. beginParameterChange();
  600. setNewDenormalisedValue (b->getToggleState() ? 1.0f : 0.0f);
  601. endParameterChange();
  602. }
  603. }
  604. Button& button;
  605. bool ignoreCallbacks;
  606. CriticalSection selfCallbackMutex;
  607. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  608. };
  609. AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& s, const String& p, Button& b)
  610. : pimpl (new Pimpl (s, p, b))
  611. {
  612. }
  613. AudioProcessorValueTreeState::ButtonAttachment::~ButtonAttachment() {}
  614. #if JUCE_UNIT_TESTS
  615. static struct ParameterAdapterTests final : public UnitTest
  616. {
  617. ParameterAdapterTests() : UnitTest ("Parameter Adapter") {}
  618. void runTest() override
  619. {
  620. beginTest ("The default value is returned correctly");
  621. {
  622. const auto test = [&] (NormalisableRange<float> range, float value)
  623. {
  624. AudioParameterFloat param ({}, {}, range, value, {});
  625. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  626. expectEquals (adapter.getDenormalisedDefaultValue(), value);
  627. };
  628. test ({ -100, 100 }, 0);
  629. test ({ -2.5, 12.5 }, 10);
  630. }
  631. beginTest ("Denormalised parameter values can be retrieved");
  632. {
  633. const auto test = [&](NormalisableRange<float> range, float value)
  634. {
  635. AudioParameterFloat param ({}, {}, range, {}, {});
  636. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  637. adapter.setDenormalisedValue (value);
  638. expectEquals (adapter.getDenormalisedValue(), value);
  639. expectEquals (adapter.getRawDenormalisedValue(), value);
  640. };
  641. test ({ -20, -10 }, -15);
  642. test ({ 0, 7.5 }, 2.5);
  643. }
  644. beginTest ("Floats can be converted to text");
  645. {
  646. const auto test = [&](NormalisableRange<float> range, float value, juce::String expected)
  647. {
  648. AudioParameterFloat param ({}, {}, range, {}, {});
  649. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  650. expectEquals (adapter.getTextForDenormalisedValue (value), expected);
  651. };
  652. test ({ -100, 100 }, 0, "0.0");
  653. test ({ -2.5, 12.5 }, 10, "10.0");
  654. test ({ -20, -10 }, -15, "-15.0");
  655. test ({ 0, 7.5 }, 2.5, "2.5");
  656. }
  657. beginTest ("Text can be converted to floats");
  658. {
  659. const auto test = [&](NormalisableRange<float> range, juce::String text, float expected)
  660. {
  661. AudioParameterFloat param ({}, {}, range, {}, {});
  662. AudioProcessorValueTreeState::ParameterAdapter adapter (param);
  663. expectEquals (adapter.getDenormalisedValueForText (text), expected);
  664. };
  665. test ({ -100, 100 }, "0.0", 0);
  666. test ({ -2.5, 12.5 }, "10.0", 10);
  667. test ({ -20, -10 }, "-15.0", -15);
  668. test ({ 0, 7.5 }, "2.5", 2.5);
  669. }
  670. }
  671. } parameterAdapterTests;
  672. namespace
  673. {
  674. template <typename ValueType>
  675. inline bool operator== (const NormalisableRange<ValueType>& a,
  676. const NormalisableRange<ValueType>& b)
  677. {
  678. return std::tie (a.start, a.end, a.interval, a.skew, a.symmetricSkew)
  679. == std::tie (b.start, b.end, b.interval, b.skew, b.symmetricSkew);
  680. }
  681. template <typename ValueType>
  682. inline bool operator!= (const NormalisableRange<ValueType>& a,
  683. const NormalisableRange<ValueType>& b)
  684. {
  685. return ! (a == b);
  686. }
  687. } // namespace
  688. static class AudioProcessorValueTreeStateTests final : public UnitTest
  689. {
  690. private:
  691. using Parameter = AudioProcessorValueTreeState::Parameter;
  692. using ParameterGroup = AudioProcessorParameterGroup;
  693. using ParameterLayout = AudioProcessorValueTreeState::ParameterLayout;
  694. class TestAudioProcessor : public AudioProcessor
  695. {
  696. public:
  697. TestAudioProcessor() = default;
  698. explicit TestAudioProcessor (ParameterLayout layout)
  699. : state (*this, nullptr, "state", std::move (layout)) {}
  700. const String getName() const override { return {}; }
  701. void prepareToPlay (double, int) override {}
  702. void releaseResources() override {}
  703. void processBlock (AudioBuffer<float>&, MidiBuffer&) override {}
  704. double getTailLengthSeconds() const override { return {}; }
  705. bool acceptsMidi() const override { return {}; }
  706. bool producesMidi() const override { return {}; }
  707. AudioProcessorEditor* createEditor() override { return {}; }
  708. bool hasEditor() const override { return {}; }
  709. int getNumPrograms() override { return 1; }
  710. int getCurrentProgram() override { return {}; }
  711. void setCurrentProgram (int) override {}
  712. const String getProgramName (int) override { return {}; }
  713. void changeProgramName (int, const String&) override {}
  714. void getStateInformation (MemoryBlock&) override {}
  715. void setStateInformation (const void*, int) override {}
  716. AudioProcessorValueTreeState state { *this, nullptr };
  717. };
  718. struct Listener final : public AudioProcessorValueTreeState::Listener
  719. {
  720. void parameterChanged (const String& idIn, float valueIn) override
  721. {
  722. id = idIn;
  723. value = valueIn;
  724. }
  725. String id;
  726. float value{};
  727. };
  728. public:
  729. AudioProcessorValueTreeStateTests() : UnitTest ("Audio Processor Value Tree State", "AudioProcessor parameters") {}
  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. juce::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)));
  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)));
  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. auto param = proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
  899. 0.0f, nullptr, nullptr));
  900. proc.state.state = ValueTree { "state" };
  901. const auto newValue = 0.75f;
  902. auto value = proc.state.getParameterAsValue (key);
  903. value = newValue;
  904. expectEquals (param->getValue(), newValue);
  905. expectEquals (*proc.state.getRawParameterValue (key), newValue);
  906. }
  907. beginTest ("When the parameter value is changed, custom parameter values are updated");
  908. {
  909. const auto key = "id";
  910. const auto choices = StringArray ("foo", "bar", "baz");
  911. auto param = std::make_unique<AudioParameterChoice> (key, "", choices, 0);
  912. const auto paramPtr = param.get();
  913. TestAudioProcessor proc (std::move (param));
  914. const auto newValue = 2.0f;
  915. auto value = proc.state.getParameterAsValue (key);
  916. value = newValue;
  917. expectEquals (paramPtr->getCurrentChoiceName(), choices[int (newValue)]);
  918. expectEquals (*proc.state.getRawParameterValue (key), newValue);
  919. }
  920. beginTest ("When the parameter value is changed, listeners are notified");
  921. {
  922. Listener listener;
  923. TestAudioProcessor proc;
  924. const auto key = "id";
  925. proc.state.createAndAddParameter (std::make_unique<Parameter> (key, String(), String(), NormalisableRange<float>(),
  926. 0.0f, nullptr, nullptr));
  927. proc.state.addParameterListener (key, &listener);
  928. proc.state.state = ValueTree { "state" };
  929. const auto newValue = 0.75f;
  930. proc.state.getParameterAsValue (key) = newValue;
  931. expectEquals (listener.value, newValue);
  932. expectEquals (listener.id, String { key });
  933. }
  934. beginTest ("When the parameter value is changed, listeners are notified");
  935. {
  936. const auto key = "id";
  937. const auto choices = StringArray { "foo", "bar", "baz" };
  938. Listener listener;
  939. TestAudioProcessor proc (std::make_unique<AudioParameterChoice> (key, "", choices, 0));
  940. proc.state.addParameterListener (key, &listener);
  941. const auto newValue = 2.0f;
  942. proc.state.getParameterAsValue (key) = newValue;
  943. expectEquals (listener.value, newValue);
  944. expectEquals (listener.id, String (key));
  945. }
  946. }
  947. } audioProcessorValueTreeStateTests;
  948. #endif
  949. } // namespace juce