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.

557 lines
18KB

  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. //==============================================================================
  20. struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParameterWithID,
  21. private ValueTree::Listener
  22. {
  23. Parameter (AudioProcessorValueTreeState& s,
  24. const String& parameterID, const String& paramName, const String& labelText,
  25. NormalisableRange<float> r, float defaultVal,
  26. std::function<String (float)> valueToText,
  27. std::function<float (const String&)> textToValue)
  28. : AudioProcessorParameterWithID (parameterID, paramName, labelText),
  29. owner (s), valueToTextFunction (valueToText), textToValueFunction (textToValue),
  30. range (r), value (defaultVal), defaultValue (defaultVal),
  31. listenersNeedCalling (true)
  32. {
  33. state.addListener (this);
  34. needsUpdate.set (1);
  35. }
  36. ~Parameter()
  37. {
  38. // should have detached all callbacks before destroying the parameters!
  39. jassert (listeners.size() <= 1);
  40. }
  41. float getValue() const override { return range.convertTo0to1 (value); }
  42. float getDefaultValue() const override { return range.convertTo0to1 (defaultValue); }
  43. float getValueForText (const String& text) const override
  44. {
  45. return range.convertTo0to1 (textToValueFunction != nullptr ? textToValueFunction (text)
  46. : text.getFloatValue());
  47. }
  48. String getText (float v, int length) const override
  49. {
  50. return valueToTextFunction != nullptr ? valueToTextFunction (range.convertFrom0to1 (v))
  51. : AudioProcessorParameter::getText (v, length);
  52. }
  53. int getNumSteps() const override
  54. {
  55. if (range.interval > 0)
  56. return (static_cast<int> ((range.end - range.start) / range.interval) + 1);
  57. return AudioProcessor::getDefaultNumParameterSteps();
  58. }
  59. void setValue (float newValue) override
  60. {
  61. newValue = range.snapToLegalValue (range.convertFrom0to1 (newValue));
  62. if (value != newValue || listenersNeedCalling)
  63. {
  64. value = newValue;
  65. listeners.call (&AudioProcessorValueTreeState::Listener::parameterChanged, paramID, value);
  66. listenersNeedCalling = false;
  67. needsUpdate.set (1);
  68. }
  69. }
  70. void setNewState (const ValueTree& v)
  71. {
  72. state = v;
  73. updateFromValueTree();
  74. }
  75. void setUnnormalisedValue (float newUnnormalisedValue)
  76. {
  77. if (value != newUnnormalisedValue)
  78. {
  79. const float newValue = range.convertTo0to1 (newUnnormalisedValue);
  80. setValueNotifyingHost (newValue);
  81. }
  82. }
  83. void updateFromValueTree()
  84. {
  85. setUnnormalisedValue (state.getProperty (owner.valuePropertyID, defaultValue));
  86. }
  87. void copyValueToValueTree()
  88. {
  89. if (state.isValid())
  90. state.setPropertyExcludingListener (this, owner.valuePropertyID, value, owner.undoManager);
  91. }
  92. void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
  93. {
  94. if (property == owner.valuePropertyID)
  95. updateFromValueTree();
  96. }
  97. void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
  98. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
  99. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  100. void valueTreeParentChanged (ValueTree&) override {}
  101. static Parameter* getParameterForID (AudioProcessor& processor, StringRef paramID) noexcept
  102. {
  103. const int numParams = processor.getParameters().size();
  104. for (int i = 0; i < numParams; ++i)
  105. {
  106. AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
  107. // When using this class, you must allow it to manage all the parameters in your AudioProcessor, and
  108. // not add any parameter objects of other types!
  109. jassert (dynamic_cast<Parameter*> (ap) != nullptr);
  110. Parameter* const p = static_cast<Parameter*> (ap);
  111. if (paramID == p->paramID)
  112. return p;
  113. }
  114. return nullptr;
  115. }
  116. AudioProcessorValueTreeState& owner;
  117. ValueTree state;
  118. ListenerList<AudioProcessorValueTreeState::Listener> listeners;
  119. std::function<String (float)> valueToTextFunction;
  120. std::function<float (const String&)> textToValueFunction;
  121. NormalisableRange<float> range;
  122. float value, defaultValue;
  123. Atomic<int> needsUpdate;
  124. bool listenersNeedCalling;
  125. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
  126. };
  127. //==============================================================================
  128. AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
  129. : processor (p),
  130. undoManager (um),
  131. valueType ("PARAM"),
  132. valuePropertyID ("value"),
  133. idPropertyID ("id"),
  134. updatingConnections (false)
  135. {
  136. startTimerHz (10);
  137. state.addListener (this);
  138. }
  139. AudioProcessorValueTreeState::~AudioProcessorValueTreeState() {}
  140. AudioProcessorParameterWithID* AudioProcessorValueTreeState::createAndAddParameter (const String& paramID, const String& paramName,
  141. const String& labelText, NormalisableRange<float> r,
  142. float defaultVal, std::function<String (float)> valueToTextFunction,
  143. std::function<float (const String&)> textToValueFunction)
  144. {
  145. // All parameters must be created before giving this manager a ValueTree state!
  146. jassert (! state.isValid());
  147. #if ! JUCE_LINUX
  148. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  149. #endif
  150. Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
  151. defaultVal, valueToTextFunction, textToValueFunction);
  152. processor.addParameter (p);
  153. return p;
  154. }
  155. void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
  156. {
  157. if (Parameter* p = Parameter::getParameterForID (processor, paramID))
  158. p->listeners.add (listener);
  159. }
  160. void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
  161. {
  162. if (Parameter* p = Parameter::getParameterForID (processor, paramID))
  163. p->listeners.remove (listener);
  164. }
  165. Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
  166. {
  167. if (Parameter* p = Parameter::getParameterForID (processor, paramID))
  168. return p->state.getPropertyAsValue (valuePropertyID, undoManager);
  169. return Value();
  170. }
  171. NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
  172. {
  173. if (Parameter* p = Parameter::getParameterForID (processor, paramID))
  174. return p->range;
  175. return NormalisableRange<float>();
  176. }
  177. AudioProcessorParameterWithID* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
  178. {
  179. return Parameter::getParameterForID (processor, paramID);
  180. }
  181. float* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
  182. {
  183. if (Parameter* p = Parameter::getParameterForID (processor, paramID))
  184. return &(p->value);
  185. return nullptr;
  186. }
  187. ValueTree AudioProcessorValueTreeState::getOrCreateChildValueTree (const String& paramID)
  188. {
  189. ValueTree v (state.getChildWithProperty (idPropertyID, paramID));
  190. if (! v.isValid())
  191. {
  192. v = ValueTree (valueType);
  193. v.setProperty (idPropertyID, paramID, undoManager);
  194. state.addChild (v, -1, undoManager);
  195. }
  196. return v;
  197. }
  198. void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
  199. {
  200. if (! updatingConnections)
  201. {
  202. ScopedValueSetter<bool> svs (updatingConnections, true, false);
  203. const int numParams = processor.getParameters().size();
  204. for (int i = 0; i < numParams; ++i)
  205. {
  206. AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
  207. jassert (dynamic_cast<Parameter*> (ap) != nullptr);
  208. Parameter* p = static_cast<Parameter*> (ap);
  209. p->setNewState (getOrCreateChildValueTree (p->paramID));
  210. }
  211. }
  212. }
  213. void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree& tree, const Identifier& property)
  214. {
  215. if (property == idPropertyID && tree.hasType (valueType) && tree.getParent() == state)
  216. updateParameterConnectionsToChildTrees();
  217. }
  218. void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree& tree)
  219. {
  220. if (parent == state && tree.hasType (valueType))
  221. updateParameterConnectionsToChildTrees();
  222. }
  223. void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree& tree, int)
  224. {
  225. if (parent == state && tree.hasType (valueType))
  226. updateParameterConnectionsToChildTrees();
  227. }
  228. void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
  229. {
  230. if (v == state)
  231. updateParameterConnectionsToChildTrees();
  232. }
  233. void AudioProcessorValueTreeState::valueTreeChildOrderChanged (ValueTree&, int, int) {}
  234. void AudioProcessorValueTreeState::valueTreeParentChanged (ValueTree&) {}
  235. void AudioProcessorValueTreeState::timerCallback()
  236. {
  237. const int numParams = processor.getParameters().size();
  238. bool anythingUpdated = false;
  239. for (int i = 0; i < numParams; ++i)
  240. {
  241. AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
  242. jassert (dynamic_cast<Parameter*> (ap) != nullptr);
  243. Parameter* p = static_cast<Parameter*> (ap);
  244. if (p->needsUpdate.compareAndSetBool (0, 1))
  245. {
  246. p->copyValueToValueTree();
  247. anythingUpdated = true;
  248. }
  249. }
  250. startTimer (anythingUpdated ? 1000 / 50
  251. : jlimit (50, 500, getTimerInterval() + 20));
  252. }
  253. AudioProcessorValueTreeState::Listener::Listener() {}
  254. AudioProcessorValueTreeState::Listener::~Listener() {}
  255. //==============================================================================
  256. struct AttachedControlBase : public AudioProcessorValueTreeState::Listener,
  257. public AsyncUpdater
  258. {
  259. AttachedControlBase (AudioProcessorValueTreeState& s, const String& p)
  260. : state (s), paramID (p), lastValue (0)
  261. {
  262. state.addParameterListener (paramID, this);
  263. }
  264. void removeListener()
  265. {
  266. state.removeParameterListener (paramID, this);
  267. }
  268. void setNewUnnormalisedValue (float newUnnormalisedValue)
  269. {
  270. if (AudioProcessorParameter* p = state.getParameter (paramID))
  271. {
  272. const float newValue = state.getParameterRange (paramID)
  273. .convertTo0to1 (newUnnormalisedValue);
  274. if (p->getValue() != newValue)
  275. p->setValueNotifyingHost (newValue);
  276. }
  277. }
  278. void sendInitialUpdate()
  279. {
  280. if (float* v = state.getRawParameterValue (paramID))
  281. parameterChanged (paramID, *v);
  282. }
  283. void parameterChanged (const String&, float newValue) override
  284. {
  285. lastValue = newValue;
  286. if (MessageManager::getInstance()->isThisTheMessageThread())
  287. {
  288. cancelPendingUpdate();
  289. setValue (newValue);
  290. }
  291. else
  292. {
  293. triggerAsyncUpdate();
  294. }
  295. }
  296. void beginParameterChange()
  297. {
  298. if (AudioProcessorParameter* p = state.getParameter (paramID))
  299. p->beginChangeGesture();
  300. }
  301. void endParameterChange()
  302. {
  303. if (AudioProcessorParameter* p = state.getParameter (paramID))
  304. p->endChangeGesture();
  305. }
  306. void handleAsyncUpdate() override
  307. {
  308. setValue (lastValue);
  309. }
  310. virtual void setValue (float) = 0;
  311. AudioProcessorValueTreeState& state;
  312. String paramID;
  313. float lastValue;
  314. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttachedControlBase)
  315. };
  316. //==============================================================================
  317. struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private AttachedControlBase,
  318. private Slider::Listener
  319. {
  320. Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
  321. : AttachedControlBase (s, p), slider (sl), ignoreCallbacks (false)
  322. {
  323. NormalisableRange<float> range (s.getParameterRange (paramID));
  324. slider.setRange (range.start, range.end, range.interval);
  325. slider.setSkewFactor (range.skew, range.symmetricSkew);
  326. if (AudioProcessorParameter* param = state.getParameter (paramID))
  327. slider.setDoubleClickReturnValue (true, range.convertFrom0to1 (param->getDefaultValue()));
  328. sendInitialUpdate();
  329. slider.addListener (this);
  330. }
  331. ~Pimpl()
  332. {
  333. slider.removeListener (this);
  334. removeListener();
  335. }
  336. void setValue (float newValue) override
  337. {
  338. const ScopedLock selfCallbackLock (selfCallbackMutex);
  339. {
  340. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  341. slider.setValue (newValue, sendNotificationSync);
  342. }
  343. }
  344. void sliderValueChanged (Slider* s) override
  345. {
  346. const ScopedLock selfCallbackLock (selfCallbackMutex);
  347. if ((! ignoreCallbacks) && (! ModifierKeys::getCurrentModifiers().isRightButtonDown()))
  348. setNewUnnormalisedValue ((float) s->getValue());
  349. }
  350. void sliderDragStarted (Slider*) override { beginParameterChange(); }
  351. void sliderDragEnded (Slider*) override { endParameterChange(); }
  352. Slider& slider;
  353. bool ignoreCallbacks;
  354. CriticalSection selfCallbackMutex;
  355. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  356. };
  357. AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
  358. : pimpl (new Pimpl (s, p, sl))
  359. {
  360. }
  361. AudioProcessorValueTreeState::SliderAttachment::~SliderAttachment() {}
  362. //==============================================================================
  363. struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private AttachedControlBase,
  364. private ComboBox::Listener
  365. {
  366. Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
  367. : AttachedControlBase (s, p), combo (c), ignoreCallbacks (false)
  368. {
  369. sendInitialUpdate();
  370. combo.addListener (this);
  371. }
  372. ~Pimpl()
  373. {
  374. combo.removeListener (this);
  375. removeListener();
  376. }
  377. void setValue (float newValue) override
  378. {
  379. const ScopedLock selfCallbackLock (selfCallbackMutex);
  380. {
  381. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  382. combo.setSelectedItemIndex (roundToInt (newValue), sendNotificationSync);
  383. }
  384. }
  385. void comboBoxChanged (ComboBox* comboBox) override
  386. {
  387. const ScopedLock selfCallbackLock (selfCallbackMutex);
  388. if (! ignoreCallbacks)
  389. {
  390. beginParameterChange();
  391. setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f);
  392. endParameterChange();
  393. }
  394. }
  395. ComboBox& combo;
  396. bool ignoreCallbacks;
  397. CriticalSection selfCallbackMutex;
  398. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  399. };
  400. AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
  401. : pimpl (new Pimpl (s, p, c))
  402. {
  403. }
  404. AudioProcessorValueTreeState::ComboBoxAttachment::~ComboBoxAttachment() {}
  405. //==============================================================================
  406. struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private AttachedControlBase,
  407. private Button::Listener
  408. {
  409. Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b)
  410. : AttachedControlBase (s, p), button (b), ignoreCallbacks (false)
  411. {
  412. sendInitialUpdate();
  413. button.addListener (this);
  414. }
  415. ~Pimpl()
  416. {
  417. button.removeListener (this);
  418. removeListener();
  419. }
  420. void setValue (float newValue) override
  421. {
  422. const ScopedLock selfCallbackLock (selfCallbackMutex);
  423. {
  424. ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  425. button.setToggleState (newValue >= 0.5f, sendNotificationSync);
  426. }
  427. }
  428. void buttonClicked (Button* b) override
  429. {
  430. const ScopedLock selfCallbackLock (selfCallbackMutex);
  431. if (! ignoreCallbacks)
  432. {
  433. beginParameterChange();
  434. setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f);
  435. endParameterChange();
  436. }
  437. }
  438. Button& button;
  439. bool ignoreCallbacks;
  440. CriticalSection selfCallbackMutex;
  441. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  442. };
  443. AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& s, const String& p, Button& b)
  444. : pimpl (new Pimpl (s, p, b))
  445. {
  446. }
  447. AudioProcessorValueTreeState::ButtonAttachment::~ButtonAttachment() {}