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.

273 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. //==============================================================================
  16. /**
  17. A component that displays a piano keyboard, whose notes can be clicked on.
  18. This component will mimic a physical midi keyboard, showing the current state of
  19. a MidiKeyboardState object. When the on-screen keys are clicked on, it will play these
  20. notes by calling the noteOn() and noteOff() methods of its MidiKeyboardState object.
  21. Another feature is that the computer keyboard can also be used to play notes. By
  22. default it maps the top two rows of a standard qwerty keyboard to the notes, but
  23. these can be remapped if needed. It will only respond to keypresses when it has
  24. the keyboard focus, so to disable this feature you can call setWantsKeyboardFocus (false).
  25. The component is also a ChangeBroadcaster, so if you want to be informed when the
  26. keyboard is scrolled, you can register a ChangeListener for callbacks.
  27. @see MidiKeyboardState
  28. @tags{Audio}
  29. */
  30. class JUCE_API MidiKeyboardComponent : public KeyboardComponentBase,
  31. private MidiKeyboardState::Listener,
  32. private Timer
  33. {
  34. public:
  35. //==============================================================================
  36. /** Creates a MidiKeyboardComponent.
  37. @param state the midi keyboard model that this component will represent
  38. @param orientation whether the keyboard is horizontal or vertical
  39. */
  40. MidiKeyboardComponent (MidiKeyboardState& state, Orientation orientation);
  41. /** Destructor. */
  42. ~MidiKeyboardComponent() override;
  43. //==============================================================================
  44. /** Changes the velocity used in midi note-on messages that are triggered by clicking
  45. on the component.
  46. Values are 0 to 1.0, where 1.0 is the heaviest.
  47. @see setMidiChannel
  48. */
  49. void setVelocity (float velocity, bool useMousePositionForVelocity);
  50. //==============================================================================
  51. /** Changes the midi channel number that will be used for events triggered by clicking
  52. on the component.
  53. The channel must be between 1 and 16 (inclusive). This is the channel that will be
  54. passed on to the MidiKeyboardState::noteOn() method when the user clicks the component.
  55. Although this is the channel used for outgoing events, the component can display
  56. incoming events from more than one channel - see setMidiChannelsToDisplay()
  57. @see setVelocity
  58. */
  59. void setMidiChannel (int midiChannelNumber);
  60. /** Returns the midi channel that the keyboard is using for midi messages.
  61. @see setMidiChannel
  62. */
  63. int getMidiChannel() const noexcept { return midiChannel; }
  64. /** Sets a mask to indicate which incoming midi channels should be represented by
  65. key movements.
  66. The mask is a set of bits, where bit 0 = midi channel 1, bit 1 = midi channel 2, etc.
  67. If the MidiKeyboardState has a key down for any of the channels whose bits are set
  68. in this mask, the on-screen keys will also go down.
  69. By default, this mask is set to 0xffff (all channels displayed).
  70. @see setMidiChannel
  71. */
  72. void setMidiChannelsToDisplay (int midiChannelMask);
  73. /** Returns the current set of midi channels represented by the component.
  74. This is the value that was set with setMidiChannelsToDisplay().
  75. */
  76. int getMidiChannelsToDisplay() const noexcept { return midiInChannelMask; }
  77. //==============================================================================
  78. /** Deletes all key-mappings.
  79. @see setKeyPressForNote
  80. */
  81. void clearKeyMappings();
  82. /** Maps a key-press to a given note.
  83. @param key the key that should trigger the note
  84. @param midiNoteOffsetFromC how many semitones above C the triggered note should
  85. be. The actual midi note that gets played will be
  86. this value + (12 * the current base octave). To change
  87. the base octave, see setKeyPressBaseOctave()
  88. */
  89. void setKeyPressForNote (const KeyPress& key, int midiNoteOffsetFromC);
  90. /** Removes any key-mappings for a given note.
  91. For a description of what the note number means, see setKeyPressForNote().
  92. */
  93. void removeKeyPressForNote (int midiNoteOffsetFromC);
  94. /** Changes the base note above which key-press-triggered notes are played.
  95. The set of key-mappings that trigger notes can be moved up and down to cover
  96. the entire scale using this method.
  97. The value passed in is an octave number between 0 and 10 (inclusive), and
  98. indicates which C is the base note to which the key-mapped notes are
  99. relative.
  100. */
  101. void setKeyPressBaseOctave (int newOctaveNumber);
  102. //==============================================================================
  103. /** A set of colour IDs to use to change the colour of various aspects of the keyboard.
  104. These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
  105. methods.
  106. @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
  107. */
  108. enum ColourIds
  109. {
  110. whiteNoteColourId = 0x1005000,
  111. blackNoteColourId = 0x1005001,
  112. keySeparatorLineColourId = 0x1005002,
  113. mouseOverKeyOverlayColourId = 0x1005003, /**< This colour will be overlaid on the normal note colour. */
  114. keyDownOverlayColourId = 0x1005004, /**< This colour will be overlaid on the normal note colour. */
  115. textLabelColourId = 0x1005005,
  116. shadowColourId = 0x1005006
  117. };
  118. //==============================================================================
  119. /** Use this method to draw a white note of the keyboard in a given rectangle.
  120. isOver indicates whether the mouse is over the key, isDown indicates whether the key is
  121. currently pressed down.
  122. When doing this, be sure to note the keyboard's orientation.
  123. */
  124. virtual void drawWhiteNote (int midiNoteNumber, Graphics& g, Rectangle<float> area,
  125. bool isDown, bool isOver, Colour lineColour, Colour textColour);
  126. /** Use this method to draw a black note of the keyboard in a given rectangle.
  127. isOver indicates whether the mouse is over the key, isDown indicates whether the key is
  128. currently pressed down.
  129. When doing this, be sure to note the keyboard's orientation.
  130. */
  131. virtual void drawBlackNote (int midiNoteNumber, Graphics& g, Rectangle<float> area,
  132. bool isDown, bool isOver, Colour noteFillColour);
  133. /** Callback when the mouse is clicked on a key.
  134. You could use this to do things like handle right-clicks on keys, etc.
  135. Return true if you want the click to trigger the note, or false if you
  136. want to handle it yourself and not have the note played.
  137. @see mouseDraggedToKey
  138. */
  139. virtual bool mouseDownOnKey (int midiNoteNumber, const MouseEvent& e) { ignoreUnused (midiNoteNumber, e); return true; }
  140. /** Callback when the mouse is dragged from one key onto another.
  141. Return true if you want the drag to trigger the new note, or false if you
  142. want to handle it yourself and not have the note played.
  143. @see mouseDownOnKey
  144. */
  145. virtual bool mouseDraggedToKey (int midiNoteNumber, const MouseEvent& e) { ignoreUnused (midiNoteNumber, e); return true; }
  146. /** Callback when the mouse is released from a key.
  147. @see mouseDownOnKey
  148. */
  149. virtual void mouseUpOnKey (int midiNoteNumber, const MouseEvent& e) { ignoreUnused (midiNoteNumber, e); }
  150. /** Allows text to be drawn on the white notes.
  151. By default this is used to label the C in each octave, but could be used for other things.
  152. @see setOctaveForMiddleC
  153. */
  154. virtual String getWhiteNoteText (int midiNoteNumber);
  155. //==============================================================================
  156. /** @internal */
  157. void mouseMove (const MouseEvent&) override;
  158. /** @internal */
  159. void mouseDrag (const MouseEvent&) override;
  160. /** @internal */
  161. void mouseDown (const MouseEvent&) override;
  162. /** @internal */
  163. void mouseUp (const MouseEvent&) override;
  164. /** @internal */
  165. void mouseEnter (const MouseEvent&) override;
  166. /** @internal */
  167. void mouseExit (const MouseEvent&) override;
  168. /** @internal */
  169. void timerCallback() override;
  170. /** @internal */
  171. bool keyStateChanged (bool isKeyDown) override;
  172. /** @internal */
  173. bool keyPressed (const KeyPress&) override;
  174. /** @internal */
  175. void focusLost (FocusChangeType) override;
  176. /** @internal */
  177. void colourChanged() override;
  178. private:
  179. //==============================================================================
  180. void drawKeyboardBackground (Graphics& g, Rectangle<float> area) override final;
  181. void drawWhiteKey (int midiNoteNumber, Graphics& g, Rectangle<float> area) override final;
  182. void drawBlackKey (int midiNoteNumber, Graphics& g, Rectangle<float> area) override final;
  183. void handleNoteOn (MidiKeyboardState*, int, int, float) override;
  184. void handleNoteOff (MidiKeyboardState*, int, int, float) override;
  185. //==============================================================================
  186. void resetAnyKeysInUse();
  187. void updateNoteUnderMouse (Point<float>, bool isDown, int fingerNum);
  188. void updateNoteUnderMouse (const MouseEvent&, bool isDown);
  189. void repaintNote (int midiNoteNumber);
  190. //==============================================================================
  191. MidiKeyboardState& state;
  192. int midiChannel = 1, midiInChannelMask = 0xffff;
  193. int keyMappingOctave = 6;
  194. float velocity = 1.0f;
  195. bool useMousePositionForVelocity = true;
  196. Array<int> mouseOverNotes, mouseDownNotes;
  197. Array<KeyPress> keyPresses;
  198. Array<int> keyPressNotes;
  199. BigInteger keysPressed, keysCurrentlyDrawnDown;
  200. std::atomic<bool> noPendingUpdates { true };
  201. //==============================================================================
  202. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardComponent)
  203. };
  204. } // namespace juce