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.

334 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 (&MPESetupComponent::Listener::zoneAdded, newZone);
  143. }
  144. else
  145. {
  146. handleInvalidMPEParameters();
  147. }
  148. }
  149. //==============================================================================
  150. void clearAllZonesButtonClicked()
  151. {
  152. zoneLayout.clearAllZones();
  153. listeners.call (&MPESetupComponent::Listener::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 (&MPESetupComponent::Listener::legacyModeChanged,
  171. legacyModeEnabledToggle.getToggleState(),
  172. legacyPitchbendRange.getText().getIntValue(),
  173. getLegacyModeChannelRange());
  174. }
  175. else
  176. {
  177. handleInvalidLegacyModeParameters();
  178. }
  179. }
  180. //==============================================================================
  181. void voiceStealingEnabledToggleClicked()
  182. {
  183. listeners.call (&MPESetupComponent::Listener::voiceStealingEnabledChanged,
  184. voiceStealingEnabledToggle.getToggleState());
  185. }
  186. //==============================================================================
  187. void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override
  188. {
  189. if (comboBoxThatHasChanged == &numberOfVoices)
  190. {
  191. numberOfVoicesChanged();
  192. }
  193. else if (legacyModeEnabledToggle.getToggleState() == true)
  194. {
  195. if (comboBoxThatHasChanged == &legacyPitchbendRange)
  196. legacyModePitchbendRangeChanged();
  197. else if (comboBoxThatHasChanged == &legacyStartChannel || comboBoxThatHasChanged == &legacyEndChannel)
  198. legacyModeChannelRangeChanged();
  199. }
  200. }
  201. //==============================================================================
  202. void numberOfVoicesChanged()
  203. {
  204. listeners.call (&MPESetupComponent::Listener::numberOfVoicesChanged,
  205. numberOfVoices.getText().getIntValue());
  206. }
  207. void legacyModePitchbendRangeChanged()
  208. {
  209. jassert (legacyModeEnabledToggle.getToggleState() == true);
  210. listeners.call (&MPESetupComponent::Listener::legacyModeChanged, true,
  211. legacyPitchbendRange.getText().getIntValue(),
  212. getLegacyModeChannelRange());
  213. }
  214. void legacyModeChannelRangeChanged()
  215. {
  216. jassert (legacyModeEnabledToggle.getToggleState() == true);
  217. if (areLegacyModeParametersValid())
  218. {
  219. listeners.call (&MPESetupComponent::Listener::legacyModeChanged, true,
  220. legacyPitchbendRange.getText().getIntValue(),
  221. getLegacyModeChannelRange());
  222. }
  223. else
  224. {
  225. handleInvalidLegacyModeParameters();
  226. }
  227. }
  228. //==============================================================================
  229. bool areMPEParametersValid() const
  230. {
  231. int maxPossibleNumNoteChannels = 16 - masterChannel.getText().getIntValue();
  232. return noteChannels.getText().getIntValue() <= maxPossibleNumNoteChannels;
  233. }
  234. void handleInvalidMPEParameters() const
  235. {
  236. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  237. "Invalid zone layout",
  238. "Cannot create MPE zone:\n"
  239. "Invalid zone parameters selected!",
  240. "Got it");
  241. }
  242. bool areLegacyModeParametersValid() const
  243. {
  244. return legacyStartChannel.getText().getIntValue() <= legacyEndChannel.getText().getIntValue();
  245. }
  246. void handleInvalidLegacyModeParameters() const
  247. {
  248. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  249. "Invalid legacy mode channel layout",
  250. "Cannot set legacy mode start/end channel:\n"
  251. "The end channel must not be less than the start channel!",
  252. "Got it");
  253. }
  254. //==============================================================================
  255. Range<int> getLegacyModeChannelRange() const
  256. {
  257. return Range<int> (legacyStartChannel.getText().getIntValue(),
  258. legacyEndChannel.getText().getIntValue() + 1);
  259. }
  260. //==============================================================================
  261. MPEZoneLayout zoneLayout;
  262. ComboBox masterChannel, noteChannels, masterPitchbendRange, notePitchbendRange;
  263. Label masterChannelLabel, noteChannelsLabel, masterPitchbendRangeLabel, notePitchbendRangeLabel;
  264. TextButton addZoneButton, clearAllZonesButton;
  265. ComboBox legacyStartChannel, legacyEndChannel, legacyPitchbendRange;
  266. Label legacyStartChannelLabel, legacyEndChannelLabel, legacyPitchbendRangeLabel;
  267. ToggleButton legacyModeEnabledToggle, voiceStealingEnabledToggle;
  268. ComboBox numberOfVoices;
  269. Label numberOfVoicesLabel;
  270. ListenerList<Listener> listeners;
  271. const int defaultMasterChannel = 1, defaultNoteChannels = 15,
  272. defaultMasterPitchbendRange = 2, defaultNotePitchbendRange = 48;
  273. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESetupComponent)
  274. };