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.

332 lines
14KB

  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 zoneAdded (MPEZone newZone) = 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. : masterChannelLabel (String(), "Master channel:"),
  42. noteChannelsLabel (String(), "Nr. of note channels:"),
  43. masterPitchbendRangeLabel (String(), "Master pitchbend range (semitones):"),
  44. notePitchbendRangeLabel (String(), "Note pitchbend range (semitones):"),
  45. addZoneButton ("Add this zone"),
  46. clearAllZonesButton ("Clear all zones"),
  47. legacyStartChannelLabel (String(), "First channel:"),
  48. legacyEndChannelLabel (String(), "Last channel:"),
  49. legacyPitchbendRangeLabel (String(), "Pitchbend range (semitones):"),
  50. legacyModeEnabledToggle ("Enable Legacy Mode"),
  51. voiceStealingEnabledToggle ("Enable synth voice stealing"),
  52. numberOfVoicesLabel (String(), "Number of synth voices")
  53. {
  54. initialiseComboBoxWithConsecutiveIntegers (masterChannel, masterChannelLabel, 1, 15, defaultMasterChannel);
  55. initialiseComboBoxWithConsecutiveIntegers (noteChannels, noteChannelsLabel, 1, 15, defaultNoteChannels);
  56. initialiseComboBoxWithConsecutiveIntegers (masterPitchbendRange, masterPitchbendRangeLabel, 0, 96, defaultMasterPitchbendRange);
  57. initialiseComboBoxWithConsecutiveIntegers (notePitchbendRange, notePitchbendRangeLabel, 0, 96, defaultNotePitchbendRange);
  58. initialiseComboBoxWithConsecutiveIntegers (legacyStartChannel, legacyStartChannelLabel, 1, 16, 1, false);
  59. initialiseComboBoxWithConsecutiveIntegers (legacyEndChannel, legacyEndChannelLabel, 1, 16, 16, false);
  60. initialiseComboBoxWithConsecutiveIntegers (legacyPitchbendRange, legacyPitchbendRangeLabel, 0, 96, 2, false);
  61. initialiseButton (addZoneButton);
  62. initialiseButton (clearAllZonesButton);
  63. initialiseButton (legacyModeEnabledToggle);
  64. initialiseButton (voiceStealingEnabledToggle);
  65. initialiseComboBoxWithConsecutiveIntegers (numberOfVoices, numberOfVoicesLabel, 1, 20, 15);
  66. numberOfVoices.addListener (this);
  67. }
  68. //==============================================================================
  69. void resized() override
  70. {
  71. Rectangle<int> r (proportionOfWidth (0.65f), 15, proportionOfWidth (0.25f), 3000);
  72. const int h = 24;
  73. const int hspace = 6;
  74. const int hbigspace = 18;
  75. masterChannel.setBounds (r.removeFromTop (h));
  76. r.removeFromTop (hspace);
  77. noteChannels.setBounds (r.removeFromTop (h));
  78. r.removeFromTop (hspace);
  79. notePitchbendRange.setBounds (r.removeFromTop (h));
  80. r.removeFromTop (hspace);
  81. masterPitchbendRange.setBounds (r.removeFromTop (h));
  82. legacyStartChannel.setBounds (masterChannel.getBounds());
  83. legacyEndChannel.setBounds (noteChannels.getBounds());
  84. legacyPitchbendRange.setBounds (notePitchbendRange.getBounds());
  85. r.removeFromTop (hbigspace);
  86. int buttonLeft = proportionOfWidth (0.5f);
  87. addZoneButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  88. r.removeFromTop (hspace);
  89. clearAllZonesButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  90. r.removeFromTop (hbigspace);
  91. int toggleLeft = proportionOfWidth (0.25f);
  92. legacyModeEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  93. r.removeFromTop (hspace);
  94. voiceStealingEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  95. r.removeFromTop (hspace);
  96. numberOfVoices.setBounds (r.removeFromTop (h));
  97. }
  98. private:
  99. //==============================================================================
  100. void initialiseComboBoxWithConsecutiveIntegers (ComboBox& comboBox, Label& labelToAttach,
  101. int firstValue, int numValues, int valueToSelect,
  102. bool makeVisible = true)
  103. {
  104. for (int i = 0; i < numValues; ++i)
  105. comboBox.addItem (String (i + firstValue), i + 1);
  106. comboBox.setSelectedId (valueToSelect - firstValue + 1);
  107. labelToAttach.attachToComponent (&comboBox, true);
  108. if (makeVisible)
  109. addAndMakeVisible (comboBox);
  110. else
  111. addChildComponent (comboBox);
  112. comboBox.addListener (this);
  113. }
  114. //==============================================================================
  115. void initialiseButton (Button& button)
  116. {
  117. addAndMakeVisible (button);
  118. button.addListener (this);
  119. }
  120. //==============================================================================
  121. void buttonClicked (Button* button) override
  122. {
  123. if (button == &addZoneButton)
  124. addZoneButtonClicked();
  125. else if (button == &clearAllZonesButton)
  126. clearAllZonesButtonClicked();
  127. else if (button == &legacyModeEnabledToggle)
  128. legacyModeEnabledToggleClicked();
  129. else if (button == &voiceStealingEnabledToggle)
  130. voiceStealingEnabledToggleClicked();
  131. }
  132. //==============================================================================
  133. void addZoneButtonClicked()
  134. {
  135. if (areMPEParametersValid())
  136. {
  137. MPEZone newZone (masterChannel.getText().getIntValue(),
  138. noteChannels.getText().getIntValue(),
  139. notePitchbendRange.getText().getIntValue(),
  140. masterPitchbendRange.getText().getIntValue());
  141. zoneLayout.addZone (newZone);
  142. listeners.call ([&] (Listener& l) { l.zoneAdded (newZone); });
  143. }
  144. else
  145. {
  146. handleInvalidMPEParameters();
  147. }
  148. }
  149. //==============================================================================
  150. void clearAllZonesButtonClicked()
  151. {
  152. zoneLayout.clearAllZones();
  153. listeners.call ([] (Listener& l) { l.allZonesCleared(); });
  154. }
  155. //==============================================================================
  156. void legacyModeEnabledToggleClicked()
  157. {
  158. bool legacyModeEnabled = legacyModeEnabledToggle.getToggleState();
  159. masterChannel.setVisible (! legacyModeEnabled);
  160. noteChannels.setVisible (! legacyModeEnabled);
  161. notePitchbendRange.setVisible (! legacyModeEnabled);
  162. masterPitchbendRange.setVisible (! legacyModeEnabled);
  163. addZoneButton.setVisible (! legacyModeEnabled);
  164. clearAllZonesButton.setVisible (! legacyModeEnabled);
  165. legacyStartChannel.setVisible (legacyModeEnabled);
  166. legacyEndChannel.setVisible (legacyModeEnabled);
  167. legacyPitchbendRange.setVisible (legacyModeEnabled);
  168. if (areLegacyModeParametersValid())
  169. {
  170. listeners.call ([&] (Listener& l) { l.legacyModeChanged (legacyModeEnabledToggle.getToggleState(),
  171. legacyPitchbendRange.getText().getIntValue(),
  172. getLegacyModeChannelRange()); });
  173. }
  174. else
  175. {
  176. handleInvalidLegacyModeParameters();
  177. }
  178. }
  179. //==============================================================================
  180. void voiceStealingEnabledToggleClicked()
  181. {
  182. bool newState = voiceStealingEnabledToggle.getToggleState();
  183. listeners.call ([=] (Listener& l) { l.voiceStealingEnabledChanged (newState); });
  184. }
  185. //==============================================================================
  186. void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override
  187. {
  188. if (comboBoxThatHasChanged == &numberOfVoices)
  189. {
  190. numberOfVoicesChanged();
  191. }
  192. else if (legacyModeEnabledToggle.getToggleState())
  193. {
  194. if (comboBoxThatHasChanged == &legacyPitchbendRange)
  195. legacyModePitchbendRangeChanged();
  196. else if (comboBoxThatHasChanged == &legacyStartChannel || comboBoxThatHasChanged == &legacyEndChannel)
  197. legacyModeChannelRangeChanged();
  198. }
  199. }
  200. //==============================================================================
  201. void numberOfVoicesChanged()
  202. {
  203. listeners.call ([this] (Listener& l) { l.numberOfVoicesChanged (numberOfVoices.getText().getIntValue()); });
  204. }
  205. void legacyModePitchbendRangeChanged()
  206. {
  207. jassert (legacyModeEnabledToggle.getToggleState() == true);
  208. listeners.call ([this] (Listener& l) { l.legacyModeChanged (true,
  209. legacyPitchbendRange.getText().getIntValue(),
  210. getLegacyModeChannelRange()); });
  211. }
  212. void legacyModeChannelRangeChanged()
  213. {
  214. jassert (legacyModeEnabledToggle.getToggleState() == true);
  215. if (areLegacyModeParametersValid())
  216. {
  217. listeners.call ([this] (Listener& l) { l.legacyModeChanged (true,
  218. legacyPitchbendRange.getText().getIntValue(),
  219. getLegacyModeChannelRange()); });
  220. }
  221. else
  222. {
  223. handleInvalidLegacyModeParameters();
  224. }
  225. }
  226. //==============================================================================
  227. bool areMPEParametersValid() const
  228. {
  229. int maxPossibleNumNoteChannels = 16 - masterChannel.getText().getIntValue();
  230. return noteChannels.getText().getIntValue() <= maxPossibleNumNoteChannels;
  231. }
  232. void handleInvalidMPEParameters() const
  233. {
  234. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  235. "Invalid zone layout",
  236. "Cannot create MPE zone:\n"
  237. "Invalid zone parameters selected!",
  238. "Got it");
  239. }
  240. bool areLegacyModeParametersValid() const
  241. {
  242. return legacyStartChannel.getText().getIntValue() <= legacyEndChannel.getText().getIntValue();
  243. }
  244. void handleInvalidLegacyModeParameters() const
  245. {
  246. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  247. "Invalid legacy mode channel layout",
  248. "Cannot set legacy mode start/end channel:\n"
  249. "The end channel must not be less than the start channel!",
  250. "Got it");
  251. }
  252. //==============================================================================
  253. Range<int> getLegacyModeChannelRange() const
  254. {
  255. return Range<int> (legacyStartChannel.getText().getIntValue(),
  256. legacyEndChannel.getText().getIntValue() + 1);
  257. }
  258. //==============================================================================
  259. MPEZoneLayout zoneLayout;
  260. ComboBox masterChannel, noteChannels, masterPitchbendRange, notePitchbendRange;
  261. Label masterChannelLabel, noteChannelsLabel, masterPitchbendRangeLabel, notePitchbendRangeLabel;
  262. TextButton addZoneButton, clearAllZonesButton;
  263. ComboBox legacyStartChannel, legacyEndChannel, legacyPitchbendRange;
  264. Label legacyStartChannelLabel, legacyEndChannelLabel, legacyPitchbendRangeLabel;
  265. ToggleButton legacyModeEnabledToggle, voiceStealingEnabledToggle;
  266. ComboBox numberOfVoices;
  267. Label numberOfVoicesLabel;
  268. ListenerList<Listener> listeners;
  269. const int defaultMasterChannel = 1, defaultNoteChannels = 15,
  270. defaultMasterPitchbendRange = 2, defaultNotePitchbendRange = 48;
  271. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESetupComponent)
  272. };