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) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #pragma once
  18. class MPESetupComponent : public Component,
  19. public ChangeBroadcaster,
  20. private Button::Listener,
  21. private ComboBox::Listener
  22. {
  23. public:
  24. //==============================================================================
  25. class Listener
  26. {
  27. public:
  28. virtual ~Listener() {}
  29. virtual void zoneAdded (MPEZone newZone) = 0;
  30. virtual void allZonesCleared() = 0;
  31. virtual void legacyModeChanged (bool legacyModeEnabled, int pitchbendRange, Range<int> channelRange) = 0;
  32. virtual void voiceStealingEnabledChanged (bool voiceStealingEnabled) = 0;
  33. virtual void numberOfVoicesChanged (int numberOfVoices) = 0;
  34. };
  35. void addListener (Listener* listenerToAdd) { listeners.add (listenerToAdd); }
  36. void removeListener (Listener* listenerToRemove) { listeners.remove (listenerToRemove); }
  37. //==============================================================================
  38. MPESetupComponent()
  39. : masterChannelLabel (String(), "Master channel:"),
  40. noteChannelsLabel (String(), "Nr. of note channels:"),
  41. masterPitchbendRangeLabel (String(), "Master pitchbend range (semitones):"),
  42. notePitchbendRangeLabel (String(), "Note pitchbend range (semitones):"),
  43. addZoneButton ("Add this zone"),
  44. clearAllZonesButton ("Clear all zones"),
  45. legacyStartChannelLabel (String(), "First channel:"),
  46. legacyEndChannelLabel (String(), "Last channel:"),
  47. legacyPitchbendRangeLabel (String(), "Pitchbend range (semitones):"),
  48. legacyModeEnabledToggle ("Enable Legacy Mode"),
  49. voiceStealingEnabledToggle ("Enable synth voice stealing"),
  50. numberOfVoicesLabel (String(), "Number of synth voices")
  51. {
  52. initialiseComboBoxWithConsecutiveIntegers (masterChannel, masterChannelLabel, 1, 15, defaultMasterChannel);
  53. initialiseComboBoxWithConsecutiveIntegers (noteChannels, noteChannelsLabel, 1, 15, defaultNoteChannels);
  54. initialiseComboBoxWithConsecutiveIntegers (masterPitchbendRange, masterPitchbendRangeLabel, 0, 96, defaultMasterPitchbendRange);
  55. initialiseComboBoxWithConsecutiveIntegers (notePitchbendRange, notePitchbendRangeLabel, 0, 96, defaultNotePitchbendRange);
  56. initialiseComboBoxWithConsecutiveIntegers (legacyStartChannel, legacyStartChannelLabel, 1, 16, 1, false);
  57. initialiseComboBoxWithConsecutiveIntegers (legacyEndChannel, legacyEndChannelLabel, 1, 16, 16, false);
  58. initialiseComboBoxWithConsecutiveIntegers (legacyPitchbendRange, legacyPitchbendRangeLabel, 0, 96, 2, false);
  59. initialiseButton (addZoneButton);
  60. initialiseButton (clearAllZonesButton);
  61. initialiseButton (legacyModeEnabledToggle);
  62. initialiseButton (voiceStealingEnabledToggle);
  63. initialiseComboBoxWithConsecutiveIntegers (numberOfVoices, numberOfVoicesLabel, 1, 20, 15);
  64. numberOfVoices.addListener (this);
  65. }
  66. //==============================================================================
  67. void resized() override
  68. {
  69. Rectangle<int> r (proportionOfWidth (0.65f), 15, proportionOfWidth (0.25f), 3000);
  70. const int h = 24;
  71. const int hspace = 6;
  72. const int hbigspace = 18;
  73. masterChannel.setBounds (r.removeFromTop (h));
  74. r.removeFromTop (hspace);
  75. noteChannels.setBounds (r.removeFromTop (h));
  76. r.removeFromTop (hspace);
  77. notePitchbendRange.setBounds (r.removeFromTop (h));
  78. r.removeFromTop (hspace);
  79. masterPitchbendRange.setBounds (r.removeFromTop (h));
  80. legacyStartChannel.setBounds (masterChannel.getBounds());
  81. legacyEndChannel.setBounds (noteChannels.getBounds());
  82. legacyPitchbendRange.setBounds (notePitchbendRange.getBounds());
  83. r.removeFromTop (hbigspace);
  84. int buttonLeft = proportionOfWidth (0.5f);
  85. addZoneButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  86. r.removeFromTop (hspace);
  87. clearAllZonesButton.setBounds (r.removeFromTop (h).withLeft (buttonLeft));
  88. r.removeFromTop (hbigspace);
  89. int toggleLeft = proportionOfWidth (0.25f);
  90. legacyModeEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  91. r.removeFromTop (hspace);
  92. voiceStealingEnabledToggle.setBounds (r.removeFromTop (h).withLeft (toggleLeft));
  93. r.removeFromTop (hspace);
  94. numberOfVoices.setBounds (r.removeFromTop (h));
  95. }
  96. private:
  97. //==============================================================================
  98. void initialiseComboBoxWithConsecutiveIntegers (ComboBox& comboBox, Label& labelToAttach,
  99. int firstValue, int numValues, int valueToSelect,
  100. bool makeVisible = true)
  101. {
  102. for (int i = 0; i < numValues; ++i)
  103. comboBox.addItem (String (i + firstValue), i + 1);
  104. comboBox.setSelectedId (valueToSelect - firstValue + 1);
  105. labelToAttach.attachToComponent (&comboBox, true);
  106. if (makeVisible)
  107. addAndMakeVisible (comboBox);
  108. else
  109. addChildComponent (comboBox);
  110. comboBox.addListener (this);
  111. }
  112. //==============================================================================
  113. void initialiseButton (Button& button)
  114. {
  115. addAndMakeVisible (button);
  116. button.addListener (this);
  117. }
  118. //==============================================================================
  119. void buttonClicked (Button* button) override
  120. {
  121. if (button == &addZoneButton)
  122. addZoneButtonClicked();
  123. else if (button == &clearAllZonesButton)
  124. clearAllZonesButtonClicked();
  125. else if (button == &legacyModeEnabledToggle)
  126. legacyModeEnabledToggleClicked();
  127. else if (button == &voiceStealingEnabledToggle)
  128. voiceStealingEnabledToggleClicked();
  129. }
  130. //==============================================================================
  131. void addZoneButtonClicked()
  132. {
  133. if (areMPEParametersValid())
  134. {
  135. MPEZone newZone (masterChannel.getText().getIntValue(),
  136. noteChannels.getText().getIntValue(),
  137. notePitchbendRange.getText().getIntValue(),
  138. masterPitchbendRange.getText().getIntValue());
  139. zoneLayout.addZone (newZone);
  140. listeners.call (&MPESetupComponent::Listener::zoneAdded, newZone);
  141. }
  142. else
  143. {
  144. handleInvalidMPEParameters();
  145. }
  146. }
  147. //==============================================================================
  148. void clearAllZonesButtonClicked()
  149. {
  150. zoneLayout.clearAllZones();
  151. listeners.call (&MPESetupComponent::Listener::allZonesCleared);
  152. }
  153. //==============================================================================
  154. void legacyModeEnabledToggleClicked()
  155. {
  156. bool legacyModeEnabled = legacyModeEnabledToggle.getToggleState();
  157. masterChannel.setVisible (! legacyModeEnabled);
  158. noteChannels.setVisible (! legacyModeEnabled);
  159. notePitchbendRange.setVisible (! legacyModeEnabled);
  160. masterPitchbendRange.setVisible (! legacyModeEnabled);
  161. addZoneButton.setVisible (! legacyModeEnabled);
  162. clearAllZonesButton.setVisible (! legacyModeEnabled);
  163. legacyStartChannel.setVisible (legacyModeEnabled);
  164. legacyEndChannel.setVisible (legacyModeEnabled);
  165. legacyPitchbendRange.setVisible (legacyModeEnabled);
  166. if (areLegacyModeParametersValid())
  167. {
  168. listeners.call (&MPESetupComponent::Listener::legacyModeChanged,
  169. legacyModeEnabledToggle.getToggleState(),
  170. legacyPitchbendRange.getText().getIntValue(),
  171. getLegacyModeChannelRange());
  172. }
  173. else
  174. {
  175. handleInvalidLegacyModeParameters();
  176. }
  177. }
  178. //==============================================================================
  179. void voiceStealingEnabledToggleClicked()
  180. {
  181. listeners.call (&MPESetupComponent::Listener::voiceStealingEnabledChanged,
  182. voiceStealingEnabledToggle.getToggleState());
  183. }
  184. //==============================================================================
  185. void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override
  186. {
  187. if (comboBoxThatHasChanged == &numberOfVoices)
  188. {
  189. numberOfVoicesChanged();
  190. }
  191. else if (legacyModeEnabledToggle.getToggleState() == true)
  192. {
  193. if (comboBoxThatHasChanged == &legacyPitchbendRange)
  194. legacyModePitchbendRangeChanged();
  195. else if (comboBoxThatHasChanged == &legacyStartChannel || comboBoxThatHasChanged == &legacyEndChannel)
  196. legacyModeChannelRangeChanged();
  197. }
  198. }
  199. //==============================================================================
  200. void numberOfVoicesChanged()
  201. {
  202. listeners.call (&MPESetupComponent::Listener::numberOfVoicesChanged,
  203. numberOfVoices.getText().getIntValue());
  204. }
  205. void legacyModePitchbendRangeChanged()
  206. {
  207. jassert (legacyModeEnabledToggle.getToggleState() == true);
  208. listeners.call (&MPESetupComponent::Listener::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 (&MPESetupComponent::Listener::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. };