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[
ch].isFree() && midiChannels[
ch].lastNotePlayed ==
noteNumber)
59 midiChannelLastAssigned =
ch;
65 for (
auto ch = midiChannelLastAssigned + channelIncrement; ;
ch += channelIncrement)
67 if (
ch == lastChannel + channelIncrement)
70 if (midiChannels[
ch].isFree())
72 midiChannelLastAssigned =
ch;
77 if (
ch == midiChannelLastAssigned)
81 midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (
noteNumber);
82 midiChannels[midiChannelLastAssigned].notes.add (
noteNumber);
84 return midiChannelLastAssigned;
100 if (midiChannel >= 0 && midiChannel < 17)
106 for (
auto&
ch : midiChannels)
115 for (
auto&
ch : midiChannels)
124int MPEChannelAssigner::findMidiChannelPlayingClosestNonequalNote (
int noteNumber)
noexcept
131 for (
auto note : midiChannels[
ch].notes)
143 return channelWithClosestNote;
149 channelIncrement (zone.isLowerZone() ? 1 : -1),
150 firstChannel (zone.getFirstMemberChannel()),
151 lastChannel (zone.getLastMemberChannel())
154 jassert (zone.numMemberChannels > 0);
160 auto channel = message.getChannel();
162 if (! zone.isUsingChannelAsMemberChannel (channel))
165 if (channel == zone.getMasterChannel() && (message.isResetAllControllers() || message.isAllNotesOff()))
173 if (messageIsNoteData (message))
187 if (sourceAndChannel[channel] == notMPE)
189 lastUsed[channel] = counter;
195 auto chan = getBestChanToReuse();
198 lastUsed[
chan] = counter;
199 message.setChannel (
chan);
205 for (
auto& s : sourceAndChannel)
211 sourceAndChannel[channel] = notMPE;
216 for (
auto& s : sourceAndChannel)
231 sourceAndChannel[channel] = notMPE;
233 lastUsed[channel] = counter;
235 m.setChannel (channel);
242int MPEChannelRemapper::getBestChanToReuse() const noexcept
244 for (
int chan = firstChannel; (zone.isLowerZone() ? chan <= lastChannel : chan >= lastChannel); chan += channelIncrement)
245 if (sourceAndChannel[chan] ==
notMPE)
248 auto bestChan = firstChannel;
249 auto bestLastUse = counter;
251 for (
int chan = firstChannel; (zone.isLowerZone() ? chan <= lastChannel : chan >= lastChannel); chan += channelIncrement)
253 if (lastUsed[chan] < bestLastUse)
255 bestLastUse = lastUsed[chan];
263void MPEChannelRemapper::zeroArrays()
265 for (
int i = 0; i < 17; ++i)
267 sourceAndChannel[i] = 0;
277struct MPEUtilsUnitTests :
public UnitTest
280 : UnitTest (
"MPE Utilities", UnitTestCategories::midi)
283 void runTest()
override
285 beginTest (
"MPEChannelAssigner");
298 for (
int ch = 2;
ch <= 16; ++
ch)
341 for (
int ch = 15;
ch >= 1; --
ch)
381 for (
int ch = 1;
ch <= 16; ++
ch)
416 beginTest (
"MPEChannelRemapper");
432 for (
int ch = 2;
ch <= 16; ++
ch)
437 expectEquals (noteOn.getChannel(),
ch);
444 expectEquals (noteOn.getChannel(), 2);
448 expectEquals (noteOn.getChannel(), 3);
453 expectEquals (noteOff.getChannel(), 3);
463 for (
int ch = 15;
ch >= 1; --
ch)
468 expectEquals (noteOn.getChannel(),
ch);
475 expectEquals (noteOn.getChannel(), 15);
479 expectEquals (noteOn.getChannel(), 14);
484 expectEquals (noteOff.getChannel(), 14);
490static MPEUtilsUnitTests MPEUtilsUnitTests;
bool isEmpty() const noexcept
int removeAllInstancesOf(ParameterType valueToRemove)
int size() const noexcept
void add(const ElementType &newElement)
ElementType getLast() const 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