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.

154 lines
5.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  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. This class handles the assignment of new MIDI notes to member channels of an active
  22. MPE zone.
  23. To use it, create an instance passing in the MPE zone that it should operate on
  24. and then call use the findMidiChannelForNewNote() method for all note-on messages
  25. and the noteOff() method for all note-off messages.
  26. @tags{Audio}
  27. */
  28. class MPEChannelAssigner
  29. {
  30. public:
  31. /** Constructor.
  32. This will assign channels within the range of the specified MPE zone.
  33. */
  34. MPEChannelAssigner (MPEZoneLayout::Zone zoneToUse);
  35. /** Legacy mode constructor.
  36. This will assign channels within the specified range.
  37. */
  38. MPEChannelAssigner (Range<int> channelRange = Range<int> (1, 17));
  39. /** This method will use a set of rules recommended in the MPE specification to
  40. determine which member channel the specified MIDI note should be assigned to
  41. and will return this channel number.
  42. The rules have the following precedence:
  43. - find a free channel on which the last note played was the same as the one specified
  44. - find the next free channel in round-robin assignment
  45. - find the channel number that is currently playing the closest note (but not the same)
  46. @param noteNumber the MIDI note number to be assigned to a channel
  47. @returns the zone's member channel that this note should be assigned to
  48. */
  49. int findMidiChannelForNewNote (int noteNumber) noexcept;
  50. /** You must call this method for all note-offs that you receive so that this class
  51. can keep track of the currently playing notes internally.
  52. You can specify the channel number the note off happened on. If you don't, it will
  53. look through all channels to find the registered midi note matching the given note number.
  54. */
  55. void noteOff (int noteNumber, int midiChannel = -1);
  56. /** Call this to clear all currently playing notes. */
  57. void allNotesOff();
  58. private:
  59. bool isLegacy = false;
  60. std::unique_ptr<MPEZoneLayout::Zone> zone;
  61. int channelIncrement, numChannels, firstChannel, lastChannel, midiChannelLastAssigned;
  62. //==============================================================================
  63. struct MidiChannel
  64. {
  65. Array<int> notes;
  66. int lastNotePlayed = -1;
  67. bool isFree() const noexcept { return notes.isEmpty(); }
  68. };
  69. MidiChannel midiChannels[17];
  70. //==============================================================================
  71. int findMidiChannelPlayingClosestNonequalNote (int noteNumber) noexcept;
  72. };
  73. //==============================================================================
  74. /**
  75. This class handles the logic for remapping MIDI note messages from multiple MPE
  76. sources onto a specified MPE zone.
  77. @tags{Audio}
  78. */
  79. class MPEChannelRemapper
  80. {
  81. public:
  82. /** Used to indicate that a particular source & channel combination is not currently using MPE. */
  83. static const uint32 notMPE = 0;
  84. /** Constructor */
  85. MPEChannelRemapper (MPEZoneLayout::Zone zoneToRemap);
  86. //==============================================================================
  87. /** Remaps the MIDI channel of the specified MIDI message (if necessary).
  88. Note that the MidiMessage object passed in will have it's channel changed if it
  89. needs to be remapped.
  90. @param message the message to be remapped
  91. @param mpeSourceID the ID of the MPE source of the message. This is up to the
  92. user to define and keep constant
  93. */
  94. void remapMidiChannelIfNeeded (MidiMessage& message, uint32 mpeSourceID) noexcept;
  95. //==============================================================================
  96. /** Resets all the source & channel combinations. */
  97. void reset() noexcept;
  98. /** Clears a specified channel of this MPE zone. */
  99. void clearChannel (int channel) noexcept;
  100. /** Clears all channels in use by a specified source. */
  101. void clearSource (uint32 mpeSourceID);
  102. private:
  103. MPEZoneLayout::Zone zone;
  104. int channelIncrement;
  105. int firstChannel, lastChannel;
  106. uint32 sourceAndChannel[17];
  107. uint32 lastUsed[17];
  108. uint32 counter = 0;
  109. //==============================================================================
  110. bool applyRemapIfExisting (int channel, uint32 sourceAndChannelID, MidiMessage& m) noexcept;
  111. int getBestChanToReuse() const noexcept;
  112. void zeroArrays();
  113. //==============================================================================
  114. bool messageIsNoteData (const MidiMessage& m) { return (*m.getRawData() & 0xf0) != 0xf0; }
  115. };
  116. } // namespace juce