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.

266 lines
10KB

  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 SETUP_H_INCLUDED
  18. #define SETUP_H_INCLUDED
  19. //==============================================================================
  20. struct Utilities
  21. {
  22. static Colour getZoneColour (int index) noexcept
  23. {
  24. return Colours::red; // TIMUR TODO: use different colours for different zones!
  25. }
  26. };
  27. //==============================================================================
  28. class ExpressiveMidiSetupComponent : public Component,
  29. public ChangeBroadcaster,
  30. private Button::Listener
  31. {
  32. public:
  33. //==========================================================================
  34. ExpressiveMidiSetupComponent()
  35. : masterChannelLabel (String::empty, "Master channel:"),
  36. noteChannelsLabel (String::empty, "Nr. of note channels:"),
  37. masterPitchbendRangeLabel (String::empty, "Master pitchbend range (semitones):"),
  38. notePitchbendRangeLabel (String::empty, "Note pitchbend range (semitones):"),
  39. addZoneButton ("Add this zone"),
  40. clearAllZonesButton ("Clear all zones")
  41. {
  42. initialiseComboBoxWithConsecutiveIntegers (masterChannel, masterChannelLabel, 1, 15, defaultMasterChannel);
  43. initialiseComboBoxWithConsecutiveIntegers (noteChannels, noteChannelsLabel, 1, 15, defaultNoteChannels);
  44. initialiseComboBoxWithConsecutiveIntegers (masterPitchbendRange, masterPitchbendRangeLabel, 0, 96, defaultMasterPitchbendRange);
  45. initialiseComboBoxWithConsecutiveIntegers (notePitchbendRange, notePitchbendRangeLabel, 0, 96, defaultNotePitchbendRange);
  46. initialiseTextButton (addZoneButton);
  47. initialiseTextButton (clearAllZonesButton);
  48. }
  49. //==========================================================================
  50. void resized() override
  51. {
  52. Rectangle<int> r (proportionOfWidth (0.65f), 15, proportionOfWidth (0.25f), 3000);
  53. const int h = 24;
  54. const int space = h / 4;
  55. masterChannel.setBounds (r.removeFromTop (h));
  56. r.removeFromTop (space);
  57. noteChannels.setBounds (r.removeFromTop (h));
  58. r.removeFromTop (space);
  59. masterPitchbendRange.setBounds (r.removeFromTop (h));
  60. r.removeFromTop (space);
  61. notePitchbendRange.setBounds (r.removeFromTop (h));
  62. r.removeFromTop (18);
  63. r.setLeft (proportionOfWidth (0.5f));
  64. addZoneButton.setBounds (r.removeFromTop (h));
  65. r.removeFromTop (space);
  66. clearAllZonesButton.setBounds (r.removeFromTop (h));
  67. }
  68. //==========================================================================
  69. class Listener
  70. {
  71. public:
  72. virtual ~Listener() {}
  73. virtual void expressiveMidiZoneLayoutChanged (ExpressiveMidiZoneLayout newLayout) = 0;
  74. };
  75. void addListener (Listener* listenerToAdd)
  76. {
  77. listeners.add (listenerToAdd);
  78. if (zoneLayout.getNumZones() > 0)
  79. // make the new listener immediately aware of current zone layout
  80. listenerToAdd->expressiveMidiZoneLayoutChanged (zoneLayout);
  81. }
  82. void removeListener (Listener* listenerToRemove)
  83. {
  84. listeners.remove (listenerToRemove);
  85. }
  86. private:
  87. //==========================================================================
  88. void initialiseComboBoxWithConsecutiveIntegers (ComboBox& comboBox, Label& labelToAttach, int firstValue, int numValues, int valueToSelect)
  89. {
  90. addAndMakeVisible (comboBox);
  91. for (int i = 0; i < numValues; ++i)
  92. comboBox.addItem (String (i + firstValue), i + 1);
  93. comboBox.setSelectedId (valueToSelect - firstValue + 1);
  94. labelToAttach.attachToComponent (&comboBox, true);
  95. }
  96. //==========================================================================
  97. void initialiseTextButton (TextButton& button)
  98. {
  99. addAndMakeVisible (button);
  100. button.addListener (this);
  101. }
  102. //==========================================================================
  103. void buttonClicked (Button* button) override
  104. {
  105. if (button == &addZoneButton)
  106. addZoneButtonClicked();
  107. else if (button == &clearAllZonesButton)
  108. clearAllZonesButtonClicked();
  109. }
  110. //==========================================================================
  111. void addZoneButtonClicked()
  112. {
  113. if (selectedZoneParametersValid())
  114. {
  115. ExpressiveMidiZone newZone (masterChannel.getText().getIntValue(),
  116. noteChannels.getText().getIntValue(),
  117. notePitchbendRange.getText().getIntValue(),
  118. masterPitchbendRange.getText().getIntValue());
  119. zoneLayout.addZone (newZone);
  120. listeners.call (&Listener::expressiveMidiZoneLayoutChanged, zoneLayout);
  121. }
  122. else
  123. {
  124. handleInvalidNrOfNoteChannels();
  125. }
  126. }
  127. //==========================================================================
  128. void clearAllZonesButtonClicked()
  129. {
  130. zoneLayout.clearAllZones();
  131. listeners.call (&Listener::expressiveMidiZoneLayoutChanged, zoneLayout);
  132. }
  133. //==========================================================================
  134. bool selectedZoneParametersValid() const
  135. {
  136. int maxPossibleNumNoteChannels = 16 - masterChannel.getText().getIntValue();
  137. return noteChannels.getText().getIntValue() <= maxPossibleNumNoteChannels;
  138. }
  139. //==========================================================================
  140. void handleInvalidNrOfNoteChannels() const
  141. {
  142. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  143. "Invalid zone layout",
  144. "Cannot create Expressive MIDI zone:\n"
  145. "Invalid zone parameters selected!",
  146. "Got it");
  147. }
  148. //==========================================================================
  149. ExpressiveMidiZoneLayout zoneLayout;
  150. ComboBox masterChannel, noteChannels, masterPitchbendRange, notePitchbendRange;
  151. Label masterChannelLabel, noteChannelsLabel, masterPitchbendRangeLabel, notePitchbendRangeLabel;
  152. TextButton addZoneButton, clearAllZonesButton;
  153. ListenerList<Listener> listeners;
  154. const int defaultMasterChannel = 1, defaultNoteChannels = 15,
  155. defaultMasterPitchbendRange = 2, defaultNotePitchbendRange = 48;
  156. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExpressiveMidiSetupComponent)
  157. };
  158. //==============================================================================
  159. class ZoneLayoutComponent : public Component,
  160. public ExpressiveMidiSetupComponent::Listener
  161. {
  162. public:
  163. //==========================================================================
  164. void paint (Graphics& g) override
  165. {
  166. g.setColour (Colours::black);
  167. float channelWidth = getChannelRectangleWidth();
  168. for (int i = 0; i < numMidiChannels; ++i)
  169. {
  170. float x = float (i) * channelWidth;
  171. Rectangle<int> channelArea (x, 0.0f, channelWidth, getHeight());
  172. Line<float> line (x, 0.0f, x, float (getHeight()));
  173. g.drawLine (line);
  174. g.drawText (String (i + 1), channelArea.reduced (4, 4), Justification::topLeft, false);
  175. }
  176. paintZones (g);
  177. }
  178. //==========================================================================
  179. void paintZones (Graphics& g)
  180. {
  181. float channelWidth = getChannelRectangleWidth();
  182. for (int i = 0; i < zoneLayout.getNumZones(); ++i)
  183. {
  184. ExpressiveMidiZone zone = zoneLayout.getZone (i);
  185. Rectangle<int> zoneRect (getChannelRectangleWidth() * (zone.getMasterChannel() - 1), 0,
  186. getChannelRectangleWidth() * (zone.getNumNoteChannels() + 1), getHeight());
  187. zoneRect.removeFromTop (20);
  188. g.setColour (Utilities::getZoneColour (i).withAlpha (0.3f));
  189. g.fillRect (zoneRect.withWidth (channelWidth));
  190. g.setColour (Utilities::getZoneColour (i));
  191. g.drawRect (zoneRect, 3);
  192. g.drawText ("<>" + String (zone.getPerNotePitchbendRange()), zoneRect.withTrimmedLeft (channelWidth).reduced (4, 4), Justification::bottomLeft, false);
  193. g.setColour (Colours::black);
  194. g.drawText ("ZONE " + String (i + 1), zoneRect.reduced (4, 4), Justification::topLeft, false);
  195. g.drawText ("<>" + String (zone.getMasterPitchbendRange()), zoneRect.reduced (4, 4), Justification::bottomLeft, false);
  196. }
  197. }
  198. //==========================================================================
  199. void expressiveMidiZoneLayoutChanged (ExpressiveMidiZoneLayout newLayout) override
  200. {
  201. zoneLayout = newLayout;
  202. repaint();
  203. }
  204. private:
  205. //==========================================================================
  206. float getChannelRectangleWidth() const noexcept
  207. {
  208. return float (getWidth()) / numMidiChannels;
  209. }
  210. //==========================================================================
  211. ExpressiveMidiZoneLayout zoneLayout;
  212. const int numMidiChannels = 16;
  213. };
  214. #endif // SETUP_H_INCLUDED