28 channelIncrement (zone->isLowerZone() ? 1 : -1),
29 numChannels (zone->numMemberChannels),
30 firstChannel (zone->getFirstMemberChannel()),
31 lastChannel (zone->getLastMemberChannel()),
32 midiChannelLastAssigned (firstChannel - channelIncrement)
35 jassert (numChannels > 0);
41 numChannels (channelRange.getLength()),
42 firstChannel (channelRange.getStart()),
43 lastChannel (channelRange.getEnd() - 1),
44 midiChannelLastAssigned (firstChannel - channelIncrement)
47 jassert (! channelRange.isEmpty());
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;
101 if (
ch.notes.removeAllInstancesOf (
noteNum) > 0)
110 if (midiChannel >= 0 && midiChannel <= 16)
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
153 return channelWithClosestNote;
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
254 for (
int chan = firstChannel; (zone.isLowerZone() ? chan <= lastChannel : chan >= lastChannel); chan += channelIncrement)
255 if (sourceAndChannel[chan] ==
notMPE)
258 auto bestChan = firstChannel;
259 auto bestLastUse = counter;
261 for (
int chan = firstChannel; (zone.isLowerZone() ? chan <= lastChannel : chan >= lastChannel); chan += channelIncrement)
263 if (lastUsed[chan] < bestLastUse)
265 bestLastUse = lastUsed[chan];
273void MPEChannelRemapper::zeroArrays()
275 for (
int i = 0; i < 17; ++i)
277 sourceAndChannel[i] = 0;
287struct MPEUtilsUnitTests final :
public UnitTest
290 : UnitTest (
"MPE Utilities", UnitTestCategories::midi)
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");
480 const int sourceID1 = 0;
481 const int sourceID2 = 1;
482 const int sourceID3 = 2;
484 MPEZoneLayout layout;
487 layout.setLowerZone (15);
490 MPEChannelRemapper channelRemapper (layout.getLowerZone());
493 for (
int ch = 2; ch <= 16; ++ch)
497 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID1);
498 expectEquals (noteOn.getChannel(), ch);
504 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID2);
505 expectEquals (noteOn.getChannel(), 2);
508 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID3);
509 expectEquals (noteOn.getChannel(), 3);
513 channelRemapper.remapMidiChannelIfNeeded (noteOff, sourceID3);
514 expectEquals (noteOff.getChannel(), 3);
518 layout.setUpperZone (15);
521 MPEChannelRemapper channelRemapper (layout.getUpperZone());
524 for (
int ch = 15; ch >= 1; --ch)
528 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID1);
529 expectEquals (noteOn.getChannel(), ch);
535 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID2);
536 expectEquals (noteOn.getChannel(), 15);
539 channelRemapper.remapMidiChannelIfNeeded (noteOn, sourceID3);
540 expectEquals (noteOn.getChannel(), 14);
544 channelRemapper.remapMidiChannelIfNeeded (noteOff, sourceID3);
545 expectEquals (noteOff.getChannel(), 14);
551static MPEUtilsUnitTests MPEUtilsUnitTests;
int findMidiChannelForExistingNote(int initialNoteOnNumber) noexcept
MPEChannelAssigner(MPEZoneLayout::Zone zoneToUse)
int findMidiChannelForNewNote(int noteNumber) noexcept
void noteOff(int noteNumber, int midiChannel=-1)
void remapMidiChannelIfNeeded(MidiMessage &message, uint32 mpeSourceID) noexcept
static const uint32 notMPE
void clearChannel(int channel) noexcept
void clearSource(uint32 mpeSourceID)
MPEChannelRemapper(MPEZoneLayout::Zone zoneToRemap)
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept