Audio plugin host https://kx.studio/carla
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.

410 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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 JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED
  18. #define JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED
  19. //==============================================================================
  20. /**
  21. A component that displays a piano keyboard, whose notes can be clicked on.
  22. This component will mimic a physical midi keyboard, showing the current state of
  23. a MidiKeyboardState object. When the on-screen keys are clicked on, it will play these
  24. notes by calling the noteOn() and noteOff() methods of its MidiKeyboardState object.
  25. Another feature is that the computer keyboard can also be used to play notes. By
  26. default it maps the top two rows of a standard querty keyboard to the notes, but
  27. these can be remapped if needed. It will only respond to keypresses when it has
  28. the keyboard focus, so to disable this feature you can call setWantsKeyboardFocus (false).
  29. The component is also a ChangeBroadcaster, so if you want to be informed when the
  30. keyboard is scrolled, you can register a ChangeListener for callbacks.
  31. @see MidiKeyboardState
  32. */
  33. class JUCE_API MidiKeyboardComponent : public Component,
  34. public MidiKeyboardStateListener,
  35. public ChangeBroadcaster,
  36. private Timer
  37. {
  38. public:
  39. //==============================================================================
  40. /** The direction of the keyboard.
  41. @see setOrientation
  42. */
  43. enum Orientation
  44. {
  45. horizontalKeyboard,
  46. verticalKeyboardFacingLeft,
  47. verticalKeyboardFacingRight,
  48. };
  49. /** Creates a MidiKeyboardComponent.
  50. @param state the midi keyboard model that this component will represent
  51. @param orientation whether the keyboard is horizonal or vertical
  52. */
  53. MidiKeyboardComponent (MidiKeyboardState& state,
  54. Orientation orientation);
  55. /** Destructor. */
  56. ~MidiKeyboardComponent();
  57. //==============================================================================
  58. /** Changes the velocity used in midi note-on messages that are triggered by clicking
  59. on the component.
  60. Values are 0 to 1.0, where 1.0 is the heaviest.
  61. @see setMidiChannel
  62. */
  63. void setVelocity (float velocity, bool useMousePositionForVelocity);
  64. /** Changes the midi channel number that will be used for events triggered by clicking
  65. on the component.
  66. The channel must be between 1 and 16 (inclusive). This is the channel that will be
  67. passed on to the MidiKeyboardState::noteOn() method when the user clicks the component.
  68. Although this is the channel used for outgoing events, the component can display
  69. incoming events from more than one channel - see setMidiChannelsToDisplay()
  70. @see setVelocity
  71. */
  72. void setMidiChannel (int midiChannelNumber);
  73. /** Returns the midi channel that the keyboard is using for midi messages.
  74. @see setMidiChannel
  75. */
  76. int getMidiChannel() const noexcept { return midiChannel; }
  77. /** Sets a mask to indicate which incoming midi channels should be represented by
  78. key movements.
  79. The mask is a set of bits, where bit 0 = midi channel 1, bit 1 = midi channel 2, etc.
  80. If the MidiKeyboardState has a key down for any of the channels whose bits are set
  81. in this mask, the on-screen keys will also go down.
  82. By default, this mask is set to 0xffff (all channels displayed).
  83. @see setMidiChannel
  84. */
  85. void setMidiChannelsToDisplay (int midiChannelMask);
  86. /** Returns the current set of midi channels represented by the component.
  87. This is the value that was set with setMidiChannelsToDisplay().
  88. */
  89. int getMidiChannelsToDisplay() const noexcept { return midiInChannelMask; }
  90. //==============================================================================
  91. /** Changes the width used to draw the white keys. */
  92. void setKeyWidth (float widthInPixels);
  93. /** Returns the width that was set by setKeyWidth(). */
  94. float getKeyWidth() const noexcept { return keyWidth; }
  95. /** Changes the keyboard's current direction. */
  96. void setOrientation (Orientation newOrientation);
  97. /** Returns the keyboard's current direction. */
  98. Orientation getOrientation() const noexcept { return orientation; }
  99. /** Sets the range of midi notes that the keyboard will be limited to.
  100. By default the range is 0 to 127 (inclusive), but you can limit this if you
  101. only want a restricted set of the keys to be shown.
  102. Note that the values here are inclusive and must be between 0 and 127.
  103. */
  104. void setAvailableRange (int lowestNote,
  105. int highestNote);
  106. /** Returns the first note in the available range.
  107. @see setAvailableRange
  108. */
  109. int getRangeStart() const noexcept { return rangeStart; }
  110. /** Returns the last note in the available range.
  111. @see setAvailableRange
  112. */
  113. int getRangeEnd() const noexcept { return rangeEnd; }
  114. /** If the keyboard extends beyond the size of the component, this will scroll
  115. it to show the given key at the start.
  116. Whenever the keyboard's position is changed, this will use the ChangeBroadcaster
  117. base class to send a callback to any ChangeListeners that have been registered.
  118. */
  119. void setLowestVisibleKey (int noteNumber);
  120. /** Returns the number of the first key shown in the component.
  121. @see setLowestVisibleKey
  122. */
  123. int getLowestVisibleKey() const noexcept { return (int) firstKey; }
  124. /** Returns the length of the black notes.
  125. This will be their vertical or horizontal length, depending on the keyboard's orientation.
  126. */
  127. int getBlackNoteLength() const noexcept { return blackNoteLength; }
  128. /** If set to true, then scroll buttons will appear at either end of the keyboard
  129. if there are too many notes to fit them all in the component at once.
  130. */
  131. void setScrollButtonsVisible (bool canScroll);
  132. //==============================================================================
  133. /** A set of colour IDs to use to change the colour of various aspects of the keyboard.
  134. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
  135. methods.
  136. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
  137. */
  138. enum ColourIds
  139. {
  140. whiteNoteColourId = 0x1005000,
  141. blackNoteColourId = 0x1005001,
  142. keySeparatorLineColourId = 0x1005002,
  143. mouseOverKeyOverlayColourId = 0x1005003, /**< This colour will be overlaid on the normal note colour. */
  144. keyDownOverlayColourId = 0x1005004, /**< This colour will be overlaid on the normal note colour. */
  145. textLabelColourId = 0x1005005,
  146. upDownButtonBackgroundColourId = 0x1005006,
  147. upDownButtonArrowColourId = 0x1005007,
  148. shadowColourId = 0x1005008
  149. };
  150. /** Returns the position within the component of the left-hand edge of a key.
  151. Depending on the keyboard's orientation, this may be a horizontal or vertical
  152. distance, in either direction.
  153. */
  154. int getKeyStartPosition (int midiNoteNumber) const;
  155. /** Returns the key at a given coordinate. */
  156. int getNoteAtPosition (Point<int> position);
  157. //==============================================================================
  158. /** Deletes all key-mappings.
  159. @see setKeyPressForNote
  160. */
  161. void clearKeyMappings();
  162. /** Maps a key-press to a given note.
  163. @param key the key that should trigger the note
  164. @param midiNoteOffsetFromC how many semitones above C the triggered note should
  165. be. The actual midi note that gets played will be
  166. this value + (12 * the current base octave). To change
  167. the base octave, see setKeyPressBaseOctave()
  168. */
  169. void setKeyPressForNote (const KeyPress& key,
  170. int midiNoteOffsetFromC);
  171. /** Removes any key-mappings for a given note.
  172. For a description of what the note number means, see setKeyPressForNote().
  173. */
  174. void removeKeyPressForNote (int midiNoteOffsetFromC);
  175. /** Changes the base note above which key-press-triggered notes are played.
  176. The set of key-mappings that trigger notes can be moved up and down to cover
  177. the entire scale using this method.
  178. The value passed in is an octave number between 0 and 10 (inclusive), and
  179. indicates which C is the base note to which the key-mapped notes are
  180. relative.
  181. */
  182. void setKeyPressBaseOctave (int newOctaveNumber);
  183. /** This sets the octave number which is shown as the octave number for middle C.
  184. This affects only the default implementation of getWhiteNoteText(), which
  185. passes this octave number to MidiMessage::getMidiNoteName() in order to
  186. get the note text. See MidiMessage::getMidiNoteName() for more info about
  187. the parameter.
  188. By default this value is set to 3.
  189. @see getOctaveForMiddleC
  190. */
  191. void setOctaveForMiddleC (int octaveNumForMiddleC);
  192. /** This returns the value set by setOctaveForMiddleC().
  193. @see setOctaveForMiddleC
  194. */
  195. int getOctaveForMiddleC() const noexcept { return octaveNumForMiddleC; }
  196. //==============================================================================
  197. /** @internal */
  198. void paint (Graphics&) override;
  199. /** @internal */
  200. void resized() override;
  201. /** @internal */
  202. void mouseMove (const MouseEvent&) override;
  203. /** @internal */
  204. void mouseDrag (const MouseEvent&) override;
  205. /** @internal */
  206. void mouseDown (const MouseEvent&) override;
  207. /** @internal */
  208. void mouseUp (const MouseEvent&) override;
  209. /** @internal */
  210. void mouseEnter (const MouseEvent&) override;
  211. /** @internal */
  212. void mouseExit (const MouseEvent&) override;
  213. /** @internal */
  214. void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
  215. /** @internal */
  216. void timerCallback() override;
  217. /** @internal */
  218. bool keyStateChanged (bool isKeyDown) override;
  219. /** @internal */
  220. void focusLost (FocusChangeType) override;
  221. /** @internal */
  222. void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
  223. /** @internal */
  224. void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override;
  225. /** @internal */
  226. void colourChanged() override;
  227. protected:
  228. //==============================================================================
  229. /** Draws a white note in the given rectangle.
  230. isOver indicates whether the mouse is over the key, isDown indicates whether the key is
  231. currently pressed down.
  232. When doing this, be sure to note the keyboard's orientation.
  233. */
  234. virtual void drawWhiteNote (int midiNoteNumber,
  235. Graphics& g,
  236. int x, int y, int w, int h,
  237. bool isDown, bool isOver,
  238. const Colour& lineColour,
  239. const Colour& textColour);
  240. /** Draws a black note in the given rectangle.
  241. isOver indicates whether the mouse is over the key, isDown indicates whether the key is
  242. currently pressed down.
  243. When doing this, be sure to note the keyboard's orientation.
  244. */
  245. virtual void drawBlackNote (int midiNoteNumber,
  246. Graphics& g,
  247. int x, int y, int w, int h,
  248. bool isDown, bool isOver,
  249. const Colour& noteFillColour);
  250. /** Allows text to be drawn on the white notes.
  251. By default this is used to label the C in each octave, but could be used for other things.
  252. @see setOctaveForMiddleC
  253. */
  254. virtual String getWhiteNoteText (const int midiNoteNumber);
  255. /** Draws the up and down buttons that change the base note. */
  256. virtual void drawUpDownButton (Graphics& g, int w, int h,
  257. const bool isMouseOver,
  258. const bool isButtonPressed,
  259. const bool movesOctavesUp);
  260. /** Callback when the mouse is clicked on a key.
  261. You could use this to do things like handle right-clicks on keys, etc.
  262. Return true if you want the click to trigger the note, or false if you
  263. want to handle it yourself and not have the note played.
  264. @see mouseDraggedToKey
  265. */
  266. virtual bool mouseDownOnKey (int midiNoteNumber, const MouseEvent& e);
  267. /** Callback when the mouse is dragged from one key onto another.
  268. @see mouseDownOnKey
  269. */
  270. virtual void mouseDraggedToKey (int midiNoteNumber, const MouseEvent& e);
  271. /** Callback when the mouse is released from a key.
  272. @see mouseDownOnKey
  273. */
  274. virtual void mouseUpOnKey (int midiNoteNumber, const MouseEvent& e);
  275. /** Calculates the positon of a given midi-note.
  276. This can be overridden to create layouts with custom key-widths.
  277. @param midiNoteNumber the note to find
  278. @param keyWidth the desired width in pixels of one key - see setKeyWidth()
  279. @param x the x position of the left-hand edge of the key (this method
  280. always works in terms of a horizontal keyboard)
  281. @param w the width of the key
  282. */
  283. virtual void getKeyPosition (int midiNoteNumber, float keyWidth,
  284. int& x, int& w) const;
  285. private:
  286. //==============================================================================
  287. friend class MidiKeyboardUpDownButton;
  288. MidiKeyboardState& state;
  289. int xOffset, blackNoteLength;
  290. float keyWidth;
  291. Orientation orientation;
  292. int midiChannel, midiInChannelMask;
  293. float velocity;
  294. Array<int> mouseOverNotes, mouseDownNotes;
  295. BigInteger keysPressed, keysCurrentlyDrawnDown;
  296. bool shouldCheckState;
  297. int rangeStart, rangeEnd;
  298. float firstKey;
  299. bool canScroll, useMousePositionForVelocity, shouldCheckMousePos;
  300. ScopedPointer<Button> scrollDown, scrollUp;
  301. Array<KeyPress> keyPresses;
  302. Array<int> keyPressNotes;
  303. int keyMappingOctave, octaveNumForMiddleC;
  304. static const uint8 whiteNotes[];
  305. static const uint8 blackNotes[];
  306. void getKeyPos (int midiNoteNumber, int& x, int& w) const;
  307. int xyToNote (Point<int>, float& mousePositionVelocity);
  308. int remappedXYToNote (Point<int>, float& mousePositionVelocity) const;
  309. void resetAnyKeysInUse();
  310. void updateNoteUnderMouse (Point<int>, bool isDown, int fingerNum);
  311. void updateNoteUnderMouse (const MouseEvent&, bool isDown);
  312. void repaintNote (const int midiNoteNumber);
  313. void setLowestVisibleKeyFloat (float noteNumber);
  314. Rectangle<int> getWhiteNotePos (int noteNumber) const;
  315. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardComponent)
  316. };
  317. #endif // JUCE_MIDIKEYBOARDCOMPONENT_H_INCLUDED