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.

197 lines
8.1KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. //==============================================================================
  20. /**
  21. Represents a piano keyboard, keeping track of which keys are currently pressed.
  22. This object can parse a stream of midi events, using them to update its idea
  23. of which keys are pressed for each individual midi channel.
  24. When keys go up or down, it can broadcast these events to listener objects.
  25. It also allows key up/down events to be triggered with its noteOn() and noteOff()
  26. methods, and midi messages for these events will be merged into the
  27. midi stream that gets processed by processNextMidiBuffer().
  28. @tags{Audio}
  29. */
  30. class JUCE_API MidiKeyboardState
  31. {
  32. public:
  33. //==============================================================================
  34. MidiKeyboardState();
  35. ~MidiKeyboardState();
  36. //==============================================================================
  37. /** Resets the state of the object.
  38. All internal data for all the channels is reset, but no events are sent as a
  39. result.
  40. If you want to release any keys that are currently down, and to send out note-up
  41. midi messages for this, use the allNotesOff() method instead.
  42. */
  43. void reset();
  44. /** Returns true if the given midi key is currently held down for the given midi channel.
  45. The channel number must be between 1 and 16. If you want to see if any notes are
  46. on for a range of channels, use the isNoteOnForChannels() method.
  47. */
  48. bool isNoteOn (int midiChannel, int midiNoteNumber) const noexcept;
  49. /** Returns true if the given midi key is currently held down on any of a set of midi channels.
  50. The channel mask has a bit set for each midi channel you want to test for - bit
  51. 0 = midi channel 1, bit 1 = midi channel 2, etc.
  52. If a note is on for at least one of the specified channels, this returns true.
  53. */
  54. bool isNoteOnForChannels (int midiChannelMask, int midiNoteNumber) const noexcept;
  55. /** Turns a specified note on.
  56. This will cause a suitable midi note-on event to be injected into the midi buffer during the
  57. next call to processNextMidiBuffer().
  58. It will also trigger a synchronous callback to the listeners to tell them that the key has
  59. gone down.
  60. */
  61. void noteOn (int midiChannel, int midiNoteNumber, float velocity);
  62. /** Turns a specified note off.
  63. This will cause a suitable midi note-off event to be injected into the midi buffer during the
  64. next call to processNextMidiBuffer().
  65. It will also trigger a synchronous callback to the listeners to tell them that the key has
  66. gone up.
  67. But if the note isn't actually down for the given channel, this method will in fact do nothing.
  68. */
  69. void noteOff (int midiChannel, int midiNoteNumber, float velocity);
  70. /** This will turn off any currently-down notes for the given midi channel.
  71. If you pass 0 for the midi channel, it will in fact turn off all notes on all channels.
  72. Calling this method will make calls to noteOff(), so can trigger synchronous callbacks
  73. and events being added to the midi stream.
  74. */
  75. void allNotesOff (int midiChannel);
  76. //==============================================================================
  77. /** Looks at a key-up/down event and uses it to update the state of this object.
  78. To process a buffer full of midi messages, use the processNextMidiBuffer() method
  79. instead.
  80. */
  81. void processNextMidiEvent (const MidiMessage& message);
  82. /** Scans a midi stream for up/down events and adds its own events to it.
  83. This will look for any up/down events and use them to update the internal state,
  84. synchronously making suitable callbacks to the listeners.
  85. If injectIndirectEvents is true, then midi events to produce the recent noteOn()
  86. and noteOff() calls will be added into the buffer.
  87. Only the section of the buffer whose timestamps are between startSample and
  88. (startSample + numSamples) will be affected, and any events added will be placed
  89. between these times.
  90. If you're going to use this method, you'll need to keep calling it regularly for
  91. it to work satisfactorily.
  92. To process a single midi event at a time, use the processNextMidiEvent() method
  93. instead.
  94. */
  95. void processNextMidiBuffer (MidiBuffer& buffer,
  96. int startSample,
  97. int numSamples,
  98. bool injectIndirectEvents);
  99. //==============================================================================
  100. /** Receives events from a MidiKeyboardState object. */
  101. class Listener
  102. {
  103. public:
  104. //==============================================================================
  105. virtual ~Listener() = default;
  106. //==============================================================================
  107. /** Called when one of the MidiKeyboardState's keys is pressed.
  108. This will be called synchronously when the state is either processing a
  109. buffer in its MidiKeyboardState::processNextMidiBuffer() method, or
  110. when a note is being played with its MidiKeyboardState::noteOn() method.
  111. Note that this callback could happen from an audio callback thread, so be
  112. careful not to block, and avoid any UI activity in the callback.
  113. */
  114. virtual void handleNoteOn (MidiKeyboardState* source,
  115. int midiChannel, int midiNoteNumber, float velocity) = 0;
  116. /** Called when one of the MidiKeyboardState's keys is released.
  117. This will be called synchronously when the state is either processing a
  118. buffer in its MidiKeyboardState::processNextMidiBuffer() method, or
  119. when a note is being played with its MidiKeyboardState::noteOff() method.
  120. Note that this callback could happen from an audio callback thread, so be
  121. careful not to block, and avoid any UI activity in the callback.
  122. */
  123. virtual void handleNoteOff (MidiKeyboardState* source,
  124. int midiChannel, int midiNoteNumber, float velocity) = 0;
  125. };
  126. /** Registers a listener for callbacks when keys go up or down.
  127. @see removeListener
  128. */
  129. void addListener (Listener* listener);
  130. /** Deregisters a listener.
  131. @see addListener
  132. */
  133. void removeListener (Listener* listener);
  134. private:
  135. //==============================================================================
  136. CriticalSection lock;
  137. std::atomic<uint16> noteStates[128];
  138. MidiBuffer eventsToAdd;
  139. ListenerList<Listener> listeners;
  140. void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity);
  141. void noteOffInternal (int midiChannel, int midiNoteNumber, float velocity);
  142. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState)
  143. };
  144. using MidiKeyboardStateListener = MidiKeyboardState::Listener;
  145. } // namespace juce