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.

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