Audio plugin host https://kx.studio/carla
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.

juce_ParameterAttachments.cpp 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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 (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. if (setValue != nullptr)
  88. setValue (parameter.convertFrom0to1 (lastValue));
  89. }
  90. //==============================================================================
  91. SliderParameterAttachment::SliderParameterAttachment (RangedAudioParameter& param,
  92. Slider& s,
  93. UndoManager* um)
  94. : slider (s),
  95. attachment (param, [this] (float f) { setValue (f); }, um)
  96. {
  97. slider.valueFromTextFunction = [&param] (const String& text) { return (double) param.convertFrom0to1 (param.getValueForText (text)); };
  98. slider.textFromValueFunction = [&param] (double value) { return param.getText (param.convertTo0to1 ((float) value), 0); };
  99. slider.setDoubleClickReturnValue (true, param.convertFrom0to1 (param.getDefaultValue()));
  100. auto range = param.getNormalisableRange();
  101. auto convertFrom0To1Function = [range] (double currentRangeStart,
  102. double currentRangeEnd,
  103. double normalisedValue) mutable
  104. {
  105. range.start = (float) currentRangeStart;
  106. range.end = (float) currentRangeEnd;
  107. return (double) range.convertFrom0to1 ((float) normalisedValue);
  108. };
  109. auto convertTo0To1Function = [range] (double currentRangeStart,
  110. double currentRangeEnd,
  111. double mappedValue) mutable
  112. {
  113. range.start = (float) currentRangeStart;
  114. range.end = (float) currentRangeEnd;
  115. return (double) range.convertTo0to1 ((float) mappedValue);
  116. };
  117. auto snapToLegalValueFunction = [range] (double currentRangeStart,
  118. double currentRangeEnd,
  119. double mappedValue) mutable
  120. {
  121. range.start = (float) currentRangeStart;
  122. range.end = (float) currentRangeEnd;
  123. return (double) range.snapToLegalValue ((float) mappedValue);
  124. };
  125. NormalisableRange<double> newRange { (double) range.start,
  126. (double) range.end,
  127. std::move (convertFrom0To1Function),
  128. std::move (convertTo0To1Function),
  129. std::move (snapToLegalValueFunction) };
  130. newRange.interval = range.interval;
  131. newRange.skew = range.skew;
  132. newRange.symmetricSkew = range.symmetricSkew;
  133. slider.setNormalisableRange (newRange);
  134. sendInitialUpdate();
  135. slider.valueChanged();
  136. slider.addListener (this);
  137. }
  138. SliderParameterAttachment::~SliderParameterAttachment()
  139. {
  140. slider.removeListener (this);
  141. }
  142. void SliderParameterAttachment::sendInitialUpdate() { attachment.sendInitialUpdate(); }
  143. void SliderParameterAttachment::setValue (float newValue)
  144. {
  145. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  146. slider.setValue (newValue, sendNotificationSync);
  147. }
  148. void SliderParameterAttachment::sliderValueChanged (Slider*)
  149. {
  150. if (! ignoreCallbacks)
  151. attachment.setValueAsPartOfGesture ((float) slider.getValue());
  152. }
  153. //==============================================================================
  154. ComboBoxParameterAttachment::ComboBoxParameterAttachment (RangedAudioParameter& param,
  155. ComboBox& c,
  156. UndoManager* um)
  157. : comboBox (c),
  158. storedParameter (param),
  159. attachment (param, [this] (float f) { setValue (f); }, um)
  160. {
  161. sendInitialUpdate();
  162. comboBox.addListener (this);
  163. }
  164. ComboBoxParameterAttachment::~ComboBoxParameterAttachment()
  165. {
  166. comboBox.removeListener (this);
  167. }
  168. void ComboBoxParameterAttachment::sendInitialUpdate()
  169. {
  170. attachment.sendInitialUpdate();
  171. }
  172. void ComboBoxParameterAttachment::setValue (float newValue)
  173. {
  174. const auto normValue = storedParameter.convertTo0to1 (newValue);
  175. const auto index = roundToInt (normValue * (float) (comboBox.getNumItems() - 1));
  176. if (index == comboBox.getSelectedItemIndex())
  177. return;
  178. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  179. comboBox.setSelectedItemIndex (index, sendNotificationSync);
  180. }
  181. void ComboBoxParameterAttachment::comboBoxChanged (ComboBox*)
  182. {
  183. if (ignoreCallbacks)
  184. return;
  185. const auto numItems = comboBox.getNumItems();
  186. const auto selected = (float) comboBox.getSelectedItemIndex();
  187. const auto newValue = numItems > 1 ? selected / (float) (numItems - 1)
  188. : 0.0f;
  189. attachment.setValueAsCompleteGesture (storedParameter.convertFrom0to1 (newValue));
  190. }
  191. //==============================================================================
  192. ButtonParameterAttachment::ButtonParameterAttachment (RangedAudioParameter& param,
  193. Button& b,
  194. UndoManager* um)
  195. : button (b),
  196. attachment (param, [this] (float f) { setValue (f); }, um)
  197. {
  198. sendInitialUpdate();
  199. button.addListener (this);
  200. }
  201. ButtonParameterAttachment::~ButtonParameterAttachment()
  202. {
  203. button.removeListener (this);
  204. }
  205. void ButtonParameterAttachment::sendInitialUpdate()
  206. {
  207. attachment.sendInitialUpdate();
  208. }
  209. void ButtonParameterAttachment::setValue (float newValue)
  210. {
  211. const ScopedValueSetter<bool> svs (ignoreCallbacks, true);
  212. button.setToggleState (newValue >= 0.5f, sendNotificationSync);
  213. }
  214. void ButtonParameterAttachment::buttonClicked (Button*)
  215. {
  216. if (ignoreCallbacks)
  217. return;
  218. attachment.setValueAsCompleteGesture (button.getToggleState() ? 1.0f : 0.0f);
  219. }
  220. } // namespace juce