28 channelIncrement (zone->isLowerZone() ? 1 : -1),
29 numChannels (zone->numMemberChannels),
30 firstChannel (zone->getFirstMemberChannel()),
31 lastChannel (zone->getLastMemberChannel()),
32 midiChannelLastAssigned (firstChannel - channelIncrement)
41 numChannels (channelRange.getLength()),
42 firstChannel (channelRange.getStart()),
43 lastChannel (channelRange.getEnd() - 1),
44 midiChannelLastAssigned (firstChannel - channelIncrement)
57 if (midiChannels[(
size_t)
ch].isFree() && midiChannels[(
size_t)
ch].lastNotePlayed ==
noteNumber)
59 midiChannelLastAssigned =
ch;
65 for (
int ch = midiChannelLastAssigned + channelIncrement; ;
ch += channelIncrement)
67 if (
ch == lastChannel + channelIncrement)
70 if (midiChannels[(
size_t)
ch].isFree())
72 midiChannelLastAssigned =
ch;
77 if (
ch == midiChannelLastAssigned)
81 midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (
noteNumber);
84 return midiChannelLastAssigned;
89 const auto iter =
std::find_if (midiChannels.cbegin(), midiChannels.cend(), [&] (
auto&
ch)
91 return std::find (ch.notes.begin(), ch.notes.end(), noteNumber) != ch.notes.end();
94 return iter != midiChannels.cend() ? (
int)
std::distance (midiChannels.cbegin(), iter) : -1;
99 const auto removeNote = [] (MidiChannel&
ch,
int noteNum)
101 if (
ch.notes.removeAllInstancesOf (
noteNum) > 0)
110 if (midiChannel >= 0 && midiChannel <= 16)
112 removeNote (midiChannels[(
size_t) midiChannel],
noteNumber);
116 for (
auto&
ch : midiChannels)
125 for (
auto&
ch : midiChannels)
127 if (
ch.notes.size() > 0)
128 ch.lastNotePlayed =
ch.notes.getLast();
134int MPEChannelAssigner::findMidiChannelPlayingClosestNonequalNote (
int noteNumber)
noexcept
141 for (
auto note : midiChannels[(
size_t)
ch].notes)
159 channelIncrement (zone.isLowerZone() ? 1 : -1),
160 firstChannel (zone.getFirstMemberChannel()),
161 lastChannel (zone.getLastMemberChannel())
164 jassert (zone.numMemberChannels > 0);
170 auto channel = message.getChannel();
172 if (! zone.isUsingChannelAsMemberChannel (channel))
175 if (channel == zone.getMasterChannel() && (message.isResetAllControllers() || message.isAllNotesOff()))
183 if (messageIsNoteData (message))
197 if (sourceAndChannel[channel] == notMPE)
199 lastUsed[channel] = counter;
205 auto chan = getBestChanToReuse();
208 lastUsed[
chan] = counter;
209 message.setChannel (
chan);
215 for (
auto& s : sourceAndChannel)
221 sourceAndChannel[channel] = notMPE;
226 for (
auto& s : sourceAndChannel)
241 sourceAndChannel[channel] = notMPE;
243 lastUsed[channel] = counter;
245 m.setChannel (channel);
252int MPEChannelRemapper::getBestChanToReuse()
const noexcept
273void MPEChannelRemapper::zeroArrays()
275 for (
int i = 0; i < 17; ++i)
277 sourceAndChannel[i] = 0;
293 void runTest()
override
295 beginTest (
"MPEChannelAssigner");
297 MPEZoneLayout layout;
301 layout.setLowerZone (15);
304 MPEChannelAssigner channelAssigner (layout.getLowerZone());
308 for (
int ch = 2;
ch <= 16; ++
ch)
310 expectEquals (channelAssigner.findMidiChannelForNewNote (
noteNum),
ch);
311 expectEquals (channelAssigner.findMidiChannelForExistingNote (
noteNum),
ch);
317 channelAssigner.noteOff (60);
318 expectEquals (channelAssigner.findMidiChannelForNewNote (60), 2);
319 expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 2);
321 channelAssigner.noteOff (61);
322 expectEquals (channelAssigner.findMidiChannelForNewNote (61), 3);
323 expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 3);
326 channelAssigner.noteOff (65);
327 channelAssigner.noteOff (66);
328 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 8);
329 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7);
330 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8);
331 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7);
334 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
335 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2);
336 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
337 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2);
340 channelAssigner.allNotesOff();
343 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 8);
344 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7);
345 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
346 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2);
347 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8);
348 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7);
349 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
350 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2);
353 expectEquals (channelAssigner.findMidiChannelForNewNote (101), 3);
354 expectEquals (channelAssigner.findMidiChannelForNewNote (20), 4);
355 expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 3);
356 expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 4);
361 layout.setUpperZone (15);
364 MPEChannelAssigner channelAssigner (layout.getUpperZone());
368 for (
int ch = 15;
ch >= 1; --
ch)
370 expectEquals (channelAssigner.findMidiChannelForNewNote (
noteNum),
ch);
371 expectEquals (channelAssigner.findMidiChannelForExistingNote (
noteNum),
ch);
377 channelAssigner.noteOff (60);
378 expectEquals (channelAssigner.findMidiChannelForNewNote (60), 15);
379 expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 15);
381 channelAssigner.noteOff (61);
382 expectEquals (channelAssigner.findMidiChannelForNewNote (61), 14);
383 expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 14);
386 channelAssigner.noteOff (65);
387 channelAssigner.noteOff (66);
388 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 9);
389 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10);
390 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9);
391 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10);
394 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1);
395 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15);
396 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1);
397 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15);
400 channelAssigner.allNotesOff();
403 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 9);
404 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10);
405 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1);
406 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15);
407 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9);
408 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10);
409 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1);
410 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15);
413 expectEquals (channelAssigner.findMidiChannelForNewNote (101), 14);
414 expectEquals (channelAssigner.findMidiChannelForNewNote (20), 13);
415 expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 14);
416 expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 13);
421 MPEChannelAssigner channelAssigner;
425 for (
int ch = 1;
ch <= 16; ++
ch)
427 expectEquals (channelAssigner.findMidiChannelForNewNote (
noteNum),
ch);
428 expectEquals (channelAssigner.findMidiChannelForExistingNote (
noteNum),
ch);
434 channelAssigner.noteOff (60);
435 expectEquals (channelAssigner.findMidiChannelForNewNote (60), 1);
436 expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 1);
438 channelAssigner.noteOff (61);
439 expectEquals (channelAssigner.findMidiChannelForNewNote (61), 2);
440 expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 2);
443 channelAssigner.noteOff (65);
444 channelAssigner.noteOff (66);
445 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 7);
446 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6);
447 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7);
448 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6);
451 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
452 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1);
453 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
454 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1);
457 channelAssigner.allNotesOff();
460 expectEquals (channelAssigner.findMidiChannelForNewNote (66), 7);
461 expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6);
462 expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
463 expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1);
464 expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7);
465 expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6);
466 expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
467 expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1);
470 expectEquals (channelAssigner.findMidiChannelForNewNote (101), 2);
471 expectEquals (channelAssigner.findMidiChannelForNewNote (20), 3);
472 expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 2);
473 expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 3);
477 beginTest (
"MPEChannelRemapper");
484 MPEZoneLayout layout;
487 layout.setLowerZone (15);
493 for (
int ch = 2;
ch <= 16; ++
ch)
498 expectEquals (noteOn.getChannel(),
ch);
505 expectEquals (noteOn.getChannel(), 2);
509 expectEquals (noteOn.getChannel(), 3);
514 expectEquals (noteOff.getChannel(), 3);
518 layout.setUpperZone (15);
524 for (
int ch = 15;
ch >= 1; --
ch)
529 expectEquals (noteOn.getChannel(),
ch);
536 expectEquals (noteOn.getChannel(), 15);
540 expectEquals (noteOn.getChannel(), 14);
545 expectEquals (noteOff.getChannel(), 14);
int findMidiChannelForExistingNote(int initialNoteOnNumber) noexcept
If a note has been added using findMidiChannelForNewNote() this will return the channel to which it w...
MPEChannelAssigner(MPEZoneLayout::Zone zoneToUse)
Constructor.
int findMidiChannelForNewNote(int noteNumber) noexcept
This method will use a set of rules recommended in the MPE specification to determine which member ch...
void noteOff(int noteNumber, int midiChannel=-1)
You must call this method for all note-offs that you receive so that this class can keep track of the...
void allNotesOff()
Call this to clear all currently playing notes.
void reset() noexcept
Resets all the source & channel combinations.
void remapMidiChannelIfNeeded(MidiMessage &message, uint32 mpeSourceID) noexcept
Remaps the MIDI channel of the specified MIDI message (if necessary).
static const uint32 notMPE
Used to indicate that a particular source & channel combination is not currently using MPE.
void clearChannel(int channel) noexcept
Clears a specified channel of this MPE zone.
void clearSource(uint32 mpeSourceID)
Clears all channels in use by a specified source.
MPEChannelRemapper(MPEZoneLayout::Zone zoneToRemap)
Constructor.
This class represents the current MPE zone layout of a device capable of handling MPE.
Encapsulates a MIDI message.
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
Creates a key-down message (using a floating-point velocity).
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
Creates a key-up message.
A general-purpose range object, that simply represents any linear range with a start and end point.
constexpr bool isEmpty() const noexcept
Returns true if the range has a length of zero.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
unsigned int uint32
A platform-independent 32-bit unsigned integer type.