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.

269 lines
8.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. ParameterAttachment::ParameterAttachment (RangedAudioParameter& param,
  21. std::function<void (float)> parameterChangedCallback,
  22. UndoManager* um)
  23. : parameter (param),
  24. undoManager (um),
  25. setValue (std::move (parameterChangedCallback))
  26. {
  27. parameter.addListener (this);
  28. }
  29. ParameterAttachment::~ParameterAttachment()
  30. {
  31. parameter.removeListener (this);
  32. cancelPendingUpdate();
  33. }
  34. void ParameterAttachment::sendInitialUpdate()
  35. {
  36. parameterValueChanged ({}, parameter.getValue());
  37. }
  38. void ParameterAttachment::setValueAsCompleteGesture (float newDenormalisedValue)
  39. {
  40. callIfParameterValueChanged (newDenormalisedValue, [this] (float f)
  41. {
  42. beginGesture();
  43. parameter.setValueNotifyingHost (f);
  44. endGesture();
  45. });
  46. }
  47. void ParameterAttachment::beginGesture()
  48. {
  49. if (undoManager != nullptr)
  50. undoManager->beginNewTransaction();
  51. parameter.beginChangeGesture();
  52. }
  53. void ParameterAttachment::setValueAsPartOfGesture (float newDenormalisedValue)
  54. {
  55. callIfParameterValueChanged (newDenormalisedValue, [this] (float f)
  56. {
  57. parameter.setValueNotifyingHost (f);
  58. });
  59. }
  60. void ParameterAttachment::endGesture()
  61. {
  62. parameter.endChangeGesture();
  63. }
  64. template <typename Callback>
  65. void ParameterAttachment::callIfParameterValueChanged (float newDenormalisedValue,
  66. Callback&& callback)
  67. {
  68. const auto newValue = normalise (newDenormalisedValue);
  69. if (! approximatelyEqual (parameter.getValue(), newValue))
  70. callback (newValue);
  71. }
  72. void ParameterAttachment::parameterValueChanged (int, float newValue)
  73. {
  74. lastValue = newValue;
  75. if (MessageManager::getInstance()->isThisTheMessageThread())
  76. {
  77. cancelPendingUpdate();
  78. handleAsyncUpdate();
  79. }
  80. else
  81. {
  82. triggerAsyncUpdate();
  83. }
  84. }
  85. void ParameterAttachment::handleAsyncUpdate()
  86. {
  87. NullCheckedInvocation::invoke (setValue, parameter.convertFrom0to1 (lastValue));
  88. }
  89. //==============================================================================
  90. SliderParameterAttachment::SliderParameterAttachment (RangedAudioParameter& param,
  91. Slider& s,
  92. UndoManager* um)
  93. : slider (s),
  94. attachment (param, [this] (float f) { setValue (f); }, um)
  95. {
  96. slider.valueFromTextFunction = [&param] (const String& text) { return (double) param.convertFrom0to1 (param.getValueForText (text)); };
  97. slider.textFromValueFunction = [&param] (double value) { return param.getText (param.convertTo0to1 ((float) value), 0); };
  98. slider.setDoubleClickReturnValue (true, param.convertFrom0to1 (param.getDefaultValue()));
  99. auto range = param.getNormalisableRange();
  100. auto convertFrom0To1Function = [range] (double currentRangeStart,
  101. double currentRangeEnd,
  102. double normalisedValue) mutable
  103. {
  104. range.start = (float) currentRangeStart;
  105. range.end = (float) currentRangeEnd;
  106. return (double) range.convertFrom0to1 ((float) normalisedValue);
  107. };
  108. auto convertTo0To1Function = [range] (double currentRangeStart,
  109. double currentRangeEnd,
  110. double mappedValue) mutable
  111. {
  112. range.start = (float) currentRangeStart;
  113. range.end = (float) currentRangeEnd;
  114. return (double) range.convertTo0to1 ((float) mappedValue);
  115. };
  116. auto snapToLegalValueFunction = [range] (double currentRangeStart,
  117. double currentRangeEnd,
  118. double mappedValue) mutable
  119. {
  120. range.start = (float) currentRangeStart;
  121. range.end = (float) currentRangeEnd;
  122. return (double) range.snapToLegalValue ((float) mappedValue);
  123. };
  124. NormalisableRange<double> newRange { (double) range.start,
  125. (double) range.end,
  126. std::move (convertFrom0To1Function),
  127. std::move (convertTo0To1Function),
  128. std::move (snapToLegalValueFunction) };
  129. newRange.interval = range.interval;
  130. newRange.skew = range.skew;
  131. newRange.symmetricSkew = range.symmetricSkew;
  132. slider.setNormalisableRange (newRange);
  133. sendInitialUpdate();
  134. slider.valueChanged();
  135. slider.addListener (this);
  136. }
  137. SliderParameterAttachment::~SliderParameterAttachment()
  138. {
  139. slider.removeListener (this);
  140. }
  141. void SliderParameterAttachment::sendInitialUpdate() { attachment.sendInitialUpdate(); }
  142. void SliderParameterAttachment::setValue (float newValue)
  143. {
  144. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  145. slider.setValue (newValue, sendNotificationSync);
  146. }
  147. void SliderParameterAttachment::sliderValueChanged (Slider*)
  148. {
  149. if (! ignoreCallbacks)
  150. attachment.setValueAsPartOfGesture ((float) slider.getValue());
  151. }
  152. //==============================================================================
  153. ComboBoxParameterAttachment::ComboBoxParameterAttachment (RangedAudioParameter& param,
  154. ComboBox& c,
  155. UndoManager* um)
  156. : comboBox (c),
  157. storedParameter (param),
  158. attachment (param, [this] (float f) { setValue (f); }, um)
  159. {
  160. sendInitialUpdate();
  161. comboBox.addListener (this);
  162. }
  163. ComboBoxParameterAttachment::~ComboBoxParameterAttachment()
  164. {
  165. comboBox.removeListener (this);
  166. }
  167. void ComboBoxParameterAttachment::sendInitialUpdate()
  168. {
  169. attachment.sendInitialUpdate();
  170. }
  171. void ComboBoxParameterAttachment::setValue (float newValue)
  172. {
  173. const auto normValue = storedParameter.convertTo0to1 (newValue);
  174. const auto index = roundToInt (normValue * (float) (comboBox.getNumItems() - 1));
  175. if (index == comboBox.getSelectedItemIndex())
  176. return;
  177. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  178. comboBox.setSelectedItemIndex (index, sendNotificationSync);
  179. }
  180. void ComboBoxParameterAttachment::comboBoxChanged (ComboBox*)
  181. {
  182. if (ignoreCallbacks)
  183. return;
  184. const auto numItems = comboBox.getNumItems();
  185. const auto selected = (float) comboBox.getSelectedItemIndex();
  186. const auto newValue = numItems > 1 ? selected / (float) (numItems - 1)
  187. : 0.0f;
  188. attachment.setValueAsCompleteGesture (storedParameter.convertFrom0to1 (newValue));
  189. }
  190. //==============================================================================
  191. ButtonParameterAttachment::ButtonParameterAttachment (RangedAudioParameter& param,
  192. Button& b,
  193. UndoManager* um)
  194. : button (b),
  195. attachment (param, [this] (float f) { setValue (f); }, um)
  196. {
  197. sendInitialUpdate();
  198. button.addListener (this);
  199. }
  200. ButtonParameterAttachment::~ButtonParameterAttachment()
  201. {
  202. button.removeListener (this);
  203. }
  204. void ButtonParameterAttachment::sendInitialUpdate()
  205. {
  206. attachment.sendInitialUpdate();
  207. }
  208. void ButtonParameterAttachment::setValue (float newValue)
  209. {
  210. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  211. button.setToggleState (newValue >= 0.5f, sendNotificationSync);
  212. }
  213. void ButtonParameterAttachment::buttonClicked (Button*)
  214. {
  215. if (ignoreCallbacks)
  216. return;
  217. attachment.setValueAsCompleteGesture (button.getToggleState() ? 1.0f : 0.0f);
  218. }
  219. } // namespace juce