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.

312 lines
13KB

  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. #pragma once
  20. class MPESetupComponent : public Component,
  21. public ChangeBroadcaster,
  22. private Button::Listener,
  23. private ComboBox::Listener
  24. {
  25. public:
  26. //==============================================================================
  27. class Listener
  28. {
  29. public:
  30. virtual ~Listener() {}
  31. virtual void zoneChanged (bool isLower, int numMemberChans, int perNotePb, int masterPb) = 0;
  32. virtual void allZonesCleared() = 0;
  33. virtual void legacyModeChanged (bool legacyModeEnabled, int pitchbendRange, Range<int> channelRange) = 0;
  34. virtual void voiceStealingEnabledChanged (bool voiceStealingEnabled) = 0;
  35. virtual void numberOfVoicesChanged (int numberOfVoices) = 0;
  36. };
  37. void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); }
  38. void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); }
  39. //==============================================================================
  40. MPESetupComponent()
  41. {
  42. addAndMakeVisible (isLowerZoneButton);
  43. isLowerZoneButton.setToggleState (true, NotificationType::dontSendNotification);
  44. initialiseComboBoxWithConsecutiveIntegers (memberChannels, memberChannelsLabel, 0, 16, defaultMemberChannels);
  45. initialiseComboBoxWithConsecutiveIntegers (masterPitchbendRange, masterPitchbendRangeLabel, 0, 96, defaultMasterPitchbendRange);
  46. initialiseComboBoxWithConsecutiveIntegers (notePitchbendRange, notePitchbendRangeLabel, 0, 96, defaultNotePitchbendRange);
  47. initialiseComboBoxWithConsecutiveIntegers (legacyStartChannel, legacyStartChannelLabel, 1, 16, 1, false);
  48. initialiseComboBoxWithConsecutiveIntegers (legacyEndChannel, legacyEndChannelLabel, 1, 16, 16, false);
  49. initialiseComboBoxWithConsecutiveIntegers (legacyPitchbendRange, legacyPitchbendRangeLabel, 0, 96, 2, false);
  50. initialiseButton (setZoneButton);
  51. initialiseButton (clearAllZonesButton);
  52. initialiseButton (legacyModeEnabledToggle);
  53. initialiseButton (voiceStealingEnabledToggle);
  54. initialiseComboBoxWithConsecutiveIntegers (numberOfVoices, numberOfVoicesLabel, 1, 20, 15);
  55. numberOfVoices.addListener (this);
  56. }
  57. //==============================================================================
  58. void resized() override
  59. {
  60. Rectangle<int> r (proportionOfWidth (0.65f), 15, proportionOfWidth (0.25f), 3000);
  61. const int h = 24;
  62. const int hspace = 6;
  63. const int hbigspace = 18;
  64. isLowerZoneButton.setBounds (r.removeFromTop (h));
  65. r.removeFromTop (hspace);
  66. memberChannels.setBounds (r.removeFromTop (h));
  67. r.removeFromTop (hspace);
  68. notePitchbendRange.setBounds (r.removeFromTop (h));
  69. r.removeFromTop (hspace);
  70. masterPitchbendRange.setBounds (r.removeFromTop (h));
  71. legacyStartChannel.setBounds (isLowerZoneButton.getBounds());
  72. legacyEndChannel.setBounds (memberChannels.getBounds());
  73. legacyPitchbendRange.setBounds (notePitchbendRange.getBounds());
  74. r.removeFromTop (hbigspace);
  75. auto buttonLeft = proportionOfWidth (0.5f);
  76. setZoneButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  77. r.removeFromTop (hspace);
  78. clearAllZonesButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  79. r.removeFromTop (hbigspace);
  80. auto toggleLeft = proportionOfWidth (0.25f);
  81. legacyModeEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  82. r.removeFromTop (hspace);
  83. voiceStealingEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  84. r.removeFromTop (hspace);
  85. numberOfVoices.setBounds (r.removeFromTop (h));
  86. }
  87. private:
  88. //==============================================================================
  89. void initialiseComboBoxWithConsecutiveIntegers (ComboBox& comboBox, Label& labelToAttach,
  90. int firstValue, int numValues, int valueToSelect,
  91. bool makeVisible = true)
  92. {
  93. for (int i = 0; i < numValues; ++i)
  94. comboBox.addItem (String (i + firstValue), i + 1);
  95. comboBox.setSelectedId (valueToSelect - firstValue + 1);
  96. labelToAttach.attachToComponent (&comboBox, true);
  97. if (makeVisible)
  98. addAndMakeVisible (comboBox);
  99. else
  100. addChildComponent (comboBox);
  101. comboBox.addListener (this);
  102. }
  103. //==============================================================================
  104. void initialiseButton (Button& button)
  105. {
  106. addAndMakeVisible (button);
  107. button.addListener (this);
  108. }
  109. //==============================================================================
  110. void buttonClicked (Button* button) override
  111. {
  112. if (button == &setZoneButton) setZoneButtonClicked();
  113. else if (button == &clearAllZonesButton) clearAllZonesButtonClicked();
  114. else if (button == &legacyModeEnabledToggle) legacyModeEnabledToggleClicked();
  115. else if (button == &voiceStealingEnabledToggle) voiceStealingEnabledToggleClicked();
  116. }
  117. //==============================================================================
  118. void setZoneButtonClicked()
  119. {
  120. auto isLowerZone = isLowerZoneButton.getToggleState();
  121. auto numMemberChannels = memberChannels.getText().getIntValue();
  122. auto perNotePb = notePitchbendRange.getText().getIntValue();
  123. auto masterPb = masterPitchbendRange.getText().getIntValue();
  124. if (isLowerZone)
  125. zoneLayout.setLowerZone (numMemberChannels, perNotePb, masterPb);
  126. else
  127. zoneLayout.setUpperZone (numMemberChannels, perNotePb, masterPb);
  128. listeners.call ([&] (Listener& l) { l.zoneChanged (isLowerZone, numMemberChannels, perNotePb, masterPb); });
  129. }
  130. //==============================================================================
  131. void clearAllZonesButtonClicked()
  132. {
  133. zoneLayout.clearAllZones();
  134. listeners.call ([] (Listener& l) { l.allZonesCleared(); });
  135. }
  136. //==============================================================================
  137. void legacyModeEnabledToggleClicked()
  138. {
  139. auto legacyModeEnabled = legacyModeEnabledToggle.getToggleState();
  140. isLowerZoneButton.setVisible (! legacyModeEnabled);
  141. memberChannels.setVisible (! legacyModeEnabled);
  142. notePitchbendRange.setVisible (! legacyModeEnabled);
  143. masterPitchbendRange.setVisible (! legacyModeEnabled);
  144. setZoneButton.setVisible (! legacyModeEnabled);
  145. clearAllZonesButton.setVisible (! legacyModeEnabled);
  146. legacyStartChannel.setVisible (legacyModeEnabled);
  147. legacyEndChannel.setVisible (legacyModeEnabled);
  148. legacyPitchbendRange.setVisible (legacyModeEnabled);
  149. if (areLegacyModeParametersValid())
  150. {
  151. listeners.call ([&] (Listener& l) { l.legacyModeChanged (legacyModeEnabledToggle.getToggleState(),
  152. legacyPitchbendRange.getText().getIntValue(),
  153. getLegacyModeChannelRange()); });
  154. }
  155. else
  156. {
  157. handleInvalidLegacyModeParameters();
  158. }
  159. }
  160. //==============================================================================
  161. void voiceStealingEnabledToggleClicked()
  162. {
  163. auto newState = voiceStealingEnabledToggle.getToggleState();
  164. listeners.call ([=] (Listener& l) { l.voiceStealingEnabledChanged (newState); });
  165. }
  166. //==============================================================================
  167. void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override
  168. {
  169. if (comboBoxThatHasChanged == &numberOfVoices)
  170. {
  171. numberOfVoicesChanged();
  172. }
  173. else if (legacyModeEnabledToggle.getToggleState())
  174. {
  175. if (comboBoxThatHasChanged == &legacyPitchbendRange)
  176. legacyModePitchbendRangeChanged();
  177. else if (comboBoxThatHasChanged == &legacyStartChannel || comboBoxThatHasChanged == &legacyEndChannel)
  178. legacyModeChannelRangeChanged();
  179. }
  180. }
  181. //==============================================================================
  182. void numberOfVoicesChanged()
  183. {
  184. listeners.call ([this] (Listener& l) { l.numberOfVoicesChanged (numberOfVoices.getText().getIntValue()); });
  185. }
  186. void legacyModePitchbendRangeChanged()
  187. {
  188. jassert (legacyModeEnabledToggle.getToggleState() == true);
  189. listeners.call ([this] (Listener& l) { l.legacyModeChanged (true,
  190. legacyPitchbendRange.getText().getIntValue(),
  191. getLegacyModeChannelRange()); });
  192. }
  193. void legacyModeChannelRangeChanged()
  194. {
  195. jassert (legacyModeEnabledToggle.getToggleState() == true);
  196. if (areLegacyModeParametersValid())
  197. {
  198. listeners.call ([this] (Listener& l) { l.legacyModeChanged (true,
  199. legacyPitchbendRange.getText().getIntValue(),
  200. getLegacyModeChannelRange()); });
  201. }
  202. else
  203. {
  204. handleInvalidLegacyModeParameters();
  205. }
  206. }
  207. //==============================================================================
  208. bool areLegacyModeParametersValid() const
  209. {
  210. return legacyStartChannel.getText().getIntValue() <= legacyEndChannel.getText().getIntValue();
  211. }
  212. void handleInvalidLegacyModeParameters() const
  213. {
  214. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  215. "Invalid legacy mode channel layout",
  216. "Cannot set legacy mode start/end channel:\n"
  217. "The end channel must not be less than the start channel!",
  218. "Got it");
  219. }
  220. //==============================================================================
  221. Range<int> getLegacyModeChannelRange() const
  222. {
  223. return { legacyStartChannel.getText().getIntValue(),
  224. legacyEndChannel.getText().getIntValue() + 1 };
  225. }
  226. //==============================================================================
  227. MPEZoneLayout zoneLayout;
  228. ComboBox memberChannels, masterPitchbendRange, notePitchbendRange;
  229. ToggleButton isLowerZoneButton { "Lower zone" };
  230. Label memberChannelsLabel { {}, "Nr. of member channels:" };
  231. Label masterPitchbendRangeLabel { {}, "Master pitchbend range (semitones):" };
  232. Label notePitchbendRangeLabel { {}, "Note pitchbend range (semitones):" };
  233. TextButton setZoneButton { "Set zone" };
  234. TextButton clearAllZonesButton { "Clear all zones" };
  235. ComboBox legacyStartChannel, legacyEndChannel, legacyPitchbendRange;
  236. Label legacyStartChannelLabel { {}, "First channel:" };
  237. Label legacyEndChannelLabel { {}, "Last channel:" };
  238. Label legacyPitchbendRangeLabel { {}, "Pitchbend range (semitones):"};
  239. ToggleButton legacyModeEnabledToggle { "Enable Legacy Mode" };
  240. ToggleButton voiceStealingEnabledToggle { "Enable synth voice stealing" };
  241. ComboBox numberOfVoices;
  242. Label numberOfVoicesLabel { {}, "Number of synth voices"};
  243. ListenerList<Listener> listeners;
  244. const int defaultMasterChannel = 1, defaultMemberChannels = 15,
  245. defaultMasterPitchbendRange = 2, defaultNotePitchbendRange = 48;
  246. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESetupComponent)
  247. };