28 const uint8 noLSBValueReceived = 0xff;
29 const Range<int> allChannels { 1, 17 };
31 template <
typename Range,
typename Value>
32 void mpeInstrumentFill (Range& range,
const Value& value)
34 std::fill (std::begin (range), std::end (range), value);
41 mpeInstrumentFill (lastPressureLowerBitReceivedOnChannel, noLSBValueReceived);
42 mpeInstrumentFill (lastTimbreLowerBitReceivedOnChannel, noLSBValueReceived);
43 mpeInstrumentFill (isMemberChannelSustained,
false);
49 resetLastReceivedValues();
51 legacyMode.channelRange = allChannels;
68void MPEInstrument::resetLastReceivedValues()
72 MPEDimension& dimension;
81 mpeInstrumentFill (pair.dimension.lastValueReceivedOnChannel, pair.defaultValue);
90 legacyMode.isEnabled =
false;
92 if (zoneLayout != newLayout)
94 zoneLayout = newLayout;
102 if (legacyMode.isEnabled)
109 legacyMode.isEnabled =
true;
110 legacyMode.pitchbendRange = pitchbendRange;
111 legacyMode.channelRange = channelRange;
119 return legacyMode.isEnabled;
124 return legacyMode.channelRange;
129 jassert (allChannels.contains (channelRange));
134 if (legacyMode.channelRange != channelRange)
136 legacyMode.channelRange = channelRange;
143 return legacyMode.pitchbendRange;
148 jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
153 if (legacyMode.pitchbendRange != pitchbendRange)
155 legacyMode.pitchbendRange = pitchbendRange;
163 pressureDimension.trackingMode = modeToUse;
168 pitchbendDimension.trackingMode = modeToUse;
173 timbreDimension.trackingMode = modeToUse;
179 listeners.add (listenerToAdd);
184 listeners.remove (listenerToRemove);
192 if (message.
isNoteOn (
true)) processMidiNoteOnMessage (message);
193 else if (message.
isNoteOff (
false)) processMidiNoteOffMessage (message);
195 || message.
isAllNotesOff()) processMidiResetAllControllersMessage (message);
196 else if (message.
isPitchWheel()) processMidiPitchWheelMessage (message);
197 else if (message.
isChannelPressure()) processMidiChannelPressureMessage (message);
198 else if (message.
isController()) processMidiControllerMessage (message);
199 else if (message.
isAftertouch()) processMidiAfterTouchMessage (message);
203void MPEInstrument::processMidiNoteOnMessage (
const MidiMessage& message)
224void MPEInstrument::processMidiNoteOffMessage (
const MidiMessage& message)
227 message.getNoteNumber(),
232void MPEInstrument::processMidiPitchWheelMessage (
const MidiMessage& message)
239void MPEInstrument::processMidiChannelPressureMessage (
const MidiMessage& message)
246void MPEInstrument::processMidiControllerMessage (
const MidiMessage& message)
248 switch (message.getControllerNumber())
250 case 64:
sustainPedal (message.getChannel(), message.isSustainPedalOn());
break;
251 case 66:
sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn());
break;
252 case 70: handlePressureMSB (message.getChannel(), message.getControllerValue());
break;
253 case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue());
break;
254 case 102: handlePressureLSB (message.getChannel(), message.getControllerValue());
break;
255 case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue());
break;
261void MPEInstrument::processMidiResetAllControllersMessage (
const MidiMessage& message)
266 if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
268 for (
int i = notes.size(); --i >= 0;)
270 auto& note = notes.getReference (i);
272 if (note.midiChannel == message.getChannel())
276 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
283 auto zone = (message.getChannel() == 1 ? zoneLayout.
getLowerZone()
286 for (
int i = notes.size(); --i >= 0;)
288 auto& note = notes.getReference (i);
290 if (zone.isUsing (note.midiChannel))
294 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
301void MPEInstrument::processMidiAfterTouchMessage (
const MidiMessage& message)
311void MPEInstrument::handlePressureMSB (
int midiChannel,
int value)
noexcept
313 auto lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1];
319void MPEInstrument::handlePressureLSB (
int midiChannel,
int value)
noexcept
321 lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
324void MPEInstrument::handleTimbreMSB (
int midiChannel,
int value)
noexcept
326 auto lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1];
332void MPEInstrument::handleTimbreLSB (
int midiChannel,
int value)
noexcept
334 lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
348 getInitialValueForNewNote (midiChannel, pitchbendDimension),
349 getInitialValueForNewNote (midiChannel, pressureDimension),
350 getInitialValueForNewNote (midiChannel, timbreDimension),
354 updateNoteTotalPitchbend (newNote);
356 if (
auto* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber))
362 notes.remove (alreadyPlayingNote);
379 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
382 note->noteOffVelocity = midiNoteOffVelocity;
385 if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) ==
nullptr)
408 updateDimension (midiChannel, pitchbendDimension, value);
414 updateDimension (midiChannel, pressureDimension, value);
420 updateDimension (midiChannel, timbreDimension, value);
427 for (
int i = notes.size(); --i >= 0;)
429 auto& note = notes.getReference (i);
431 if (note.midiChannel == midiChannel
432 && note.initialNote == midiNoteNumber
433 && pressureDimension.getValue (note) != value)
435 pressureDimension.getValue (note) = value;
436 callListenersDimensionChanged (note, pressureDimension);
441MPEValue MPEInstrument::getInitialValueForNewNote (
int midiChannel, MPEDimension& dimension)
const
443 if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) !=
nullptr)
446 return dimension.lastValueReceivedOnChannel[midiChannel - 1];
450void MPEInstrument::updateDimension (
int midiChannel, MPEDimension& dimension, MPEValue value)
452 dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
461 for (
int i = notes.size(); --i >= 0;)
463 auto& note = notes.getReference (i);
465 if (note.midiChannel == midiChannel)
466 updateDimensionForNote (note, dimension, value);
471 if (
auto* note = getNotePtr (midiChannel, dimension.trackingMode))
472 updateDimensionForNote (*note, dimension, value);
477 updateDimensionMaster (midiChannel == 1, dimension, value);
482void MPEInstrument::updateDimensionMaster (
bool isLowerZone, MPEDimension& dimension, MPEValue value)
487 if (! zone.isActive())
490 for (
int i = notes.size(); --i >= 0;)
492 auto& note = notes.getReference (i);
494 if (! zone.isUsing (note.midiChannel))
497 if (&dimension == &pitchbendDimension)
501 updateNoteTotalPitchbend (note);
502 listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
504 else if (dimension.getValue (note) != value)
506 dimension.getValue (note) = value;
507 callListenersDimensionChanged (note, dimension);
513void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value)
515 if (dimension.getValue (note) != value)
517 dimension.getValue (note) = value;
519 if (&dimension == &pitchbendDimension)
520 updateNoteTotalPitchbend (note);
522 callListenersDimensionChanged (note, dimension);
527void MPEInstrument::callListenersDimensionChanged (
const MPENote& note,
const MPEDimension& dimension)
529 if (&dimension == &pressureDimension) { listeners.call ([&] (Listener& l) { l.notePressureChanged (note); });
return; }
530 if (&dimension == &timbreDimension) { listeners.call ([&] (Listener& l) { l.noteTimbreChanged (note); });
return; }
531 if (&dimension == &pitchbendDimension) { listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
return; }
535void MPEInstrument::updateNoteTotalPitchbend (MPENote& note)
537 if (legacyMode.isEnabled)
539 note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) legacyMode.pitchbendRange;
545 if (! zone.isActive() || ! zone.isUsing (note.midiChannel))
549 if (upperZone.isActive() && upperZone.isUsing (note.midiChannel))
561 auto notePitchbendInSemitones = 0.0f;
563 if (zone.isUsingChannelAsMemberChannel (note.midiChannel))
564 notePitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) zone.perNotePitchbendRange;
566 auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1]
568 * (float) zone.masterPitchbendRange;
570 note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones;
578 handleSustainOrSostenuto (midiChannel, isDown,
false);
584 handleSustainOrSostenuto (midiChannel, isDown,
true);
588void MPEInstrument::handleSustainOrSostenuto (
int midiChannel,
bool isDown,
bool isSostenuto)
593 if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (!
isMasterChannel (midiChannel)))
596 auto zone = (midiChannel == 1 ? zoneLayout.
getLowerZone()
599 for (
int i = notes.size(); --i >= 0;)
601 auto& note = notes.getReference (i);
603 if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsing (note.midiChannel))
614 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
619 listeners.call ([&] (Listener& l) { l.noteKeyStateChanged (note); });
626 isMemberChannelSustained[midiChannel - 1] = isDown;
628 if (! legacyMode.isEnabled)
630 if (zone.isLowerZone())
632 for (
int i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
633 isMemberChannelSustained[i - 1] = isDown;
637 for (
int i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
638 isMemberChannelSustained[i - 1] = isDown;
647 if (legacyMode.isEnabled)
648 return legacyMode.channelRange.contains (midiChannel);
650 return zoneLayout.getLowerZone().isUsingChannelAsMemberChannel (midiChannel)
651 || zoneLayout.getUpperZone().isUsingChannelAsMemberChannel (midiChannel);
656 if (legacyMode.isEnabled)
659 const auto lowerZone = zoneLayout.getLowerZone();
660 const auto upperZone = zoneLayout.getUpperZone();
662 return (lowerZone.isActive() && midiChannel == lowerZone.getMasterChannel())
663 || (upperZone.isActive() && midiChannel == upperZone.getMasterChannel());
668 if (legacyMode.isEnabled)
669 return legacyMode.channelRange.contains (midiChannel);
671 return zoneLayout.getLowerZone().isUsing (midiChannel)
672 || zoneLayout.getUpperZone().isUsing (midiChannel);
683 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
698 for (
auto& note : notes)
699 if (note.noteID == noteID)
708 if (
auto* note = getLastNotePlayedPtr (midiChannel))
716 for (
auto i = notes.size(); --i >= 0;)
718 auto& note = notes.getReference (i);
720 if (note != otherThanThisNote)
728const MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber)
const noexcept
730 for (
int i = 0; i < notes.size(); ++i)
732 auto& note = notes.getReference (i);
734 if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber)
741MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber)
noexcept
743 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, midiNoteNumber));
747const MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode)
const noexcept
751 jassert (mode != allNotesOnChannel);
753 if (mode == lastNotePlayedOnChannel)
return getLastNotePlayedPtr (midiChannel);
754 if (mode == lowestNoteOnChannel)
return getLowestNotePtr (midiChannel);
755 if (mode == highestNoteOnChannel)
return getHighestNotePtr (midiChannel);
760MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode)
noexcept
762 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, mode));
766const MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel)
const noexcept
768 const ScopedLock sl (lock);
770 for (
auto i = notes.size(); --i >= 0;)
772 auto& note = notes.getReference (i);
774 if (note.midiChannel == midiChannel
782MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel)
noexcept
784 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLastNotePlayedPtr (midiChannel));
788const MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel)
const noexcept
790 int initialNoteMax = -1;
791 const MPENote* result =
nullptr;
793 for (
auto i = notes.size(); --i >= 0;)
795 auto& note = notes.getReference (i);
797 if (note.midiChannel == midiChannel
799 && note.initialNote > initialNoteMax)
802 initialNoteMax = note.initialNote;
809MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel)
noexcept
811 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getHighestNotePtr (midiChannel));
814const MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel)
const noexcept
816 int initialNoteMin = 128;
817 const MPENote* result =
nullptr;
819 for (
auto i = notes.size(); --i >= 0;)
821 auto& note = notes.getReference (i);
823 if (note.midiChannel == midiChannel
825 && note.initialNote < initialNoteMin)
828 initialNoteMin = note.initialNote;
835MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel)
noexcept
837 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLowestNotePtr (midiChannel));
845 for (
auto i = notes.size(); --i >= 0;)
847 auto& note = notes.getReference (i);
869class MPEInstrumentTests final :
public UnitTest
873 :
UnitTest (
"MPEInstrument class", UnitTestCategories::midi)
880 testLayout.setLowerZone (5);
881 testLayout.setUpperZone (6);
884 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
885 void runTest()
override
887 beginTest (
"initial zone layout");
894 beginTest (
"get/setZoneLayout");
897 test.setZoneLayout (testLayout);
899 auto newLayout = test.getZoneLayout();
901 expect (test.getZoneLayout().getLowerZone().isActive());
902 expect (test.getZoneLayout().getUpperZone().isActive());
903 expectEquals (newLayout.getLowerZone().getMasterChannel(), 1);
904 expectEquals (newLayout.getLowerZone().numMemberChannels, 5);
905 expectEquals (newLayout.getUpperZone().getMasterChannel(), 16);
906 expectEquals (newLayout.getUpperZone().numMemberChannels, 6);
909 beginTest (
"noteOn / noteOff");
913 test.setZoneLayout (testLayout);
914 expectEquals (test.getNumPlayingNotes(), 0);
917 UnitTestInstrument test;
918 test.setZoneLayout (testLayout);
922 expectEquals (test.getNumPlayingNotes(), 0);
923 expectEquals (test.noteAddedCallCounter, 0);
927 expectEquals (test.getNumPlayingNotes(), 1);
928 expectEquals (test.noteAddedCallCounter, 1);
933 expectEquals (test.getNumPlayingNotes(), 0);
934 expectEquals (test.noteReleasedCallCounter, 1);
935 expectHasFinishedNote (test, 3, 60, 33);
940 expectEquals (test.getNumPlayingNotes(), 1);
941 expectEquals (test.noteAddedCallCounter, 2);
946 expectEquals (test.getNumPlayingNotes(), 0);
947 expectEquals (test.noteReleasedCallCounter, 2);
948 expectHasFinishedNote (test, 1, 62, 33);
952 UnitTestInstrument test;
953 test.setZoneLayout (testLayout);
958 expectEquals (test.getNumPlayingNotes(), 1);
960 expectEquals (test.noteReleasedCallCounter, 0);
964 expectEquals (test.getNumPlayingNotes(), 1);
966 expectEquals (test.noteReleasedCallCounter, 0);
971 UnitTestInstrument test;
972 test.setZoneLayout (testLayout);
976 expectEquals (test.getNumPlayingNotes(), 3);
983 UnitTestInstrument test;
984 test.setZoneLayout (testLayout);
987 expectEquals (test.getNumPlayingNotes(), 1);
992 beginTest (
"noteReleased after setZoneLayout");
994 UnitTestInstrument test;
995 test.setZoneLayout (testLayout);
1000 expectEquals (test.getNumPlayingNotes(), 3);
1001 expectEquals (test.noteReleasedCallCounter, 0);
1003 test.setZoneLayout (testLayout);
1004 expectEquals (test.getNumPlayingNotes(), 0);
1005 expectEquals (test.noteReleasedCallCounter, 3);
1008 beginTest (
"releaseAllNotes");
1010 UnitTestInstrument test;
1011 test.setZoneLayout (testLayout);
1015 expectEquals (test.getNumPlayingNotes(), 3);
1017 test.releaseAllNotes();
1018 expectEquals (test.getNumPlayingNotes(), 0);
1021 beginTest (
"sustainPedal");
1023 UnitTestInstrument test;
1024 test.setZoneLayout (testLayout);
1029 test.sustainPedal (3,
true);
1034 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1037 test.sustainPedal (7,
true);
1040 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1043 test.sustainPedal (1,
true);
1046 expectEquals (test.noteKeyStateChangedCallCounter, 1);
1049 test.sustainPedal (1,
false);
1052 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1055 test.sustainPedal (1,
true);
1056 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1059 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1062 test.sustainPedal (11,
true);
1066 expectEquals (test.noteReleasedCallCounter, 1);
1072 expectEquals (test.getNumPlayingNotes(), 2);
1073 expectEquals (test.noteReleasedCallCounter, 2);
1074 expectEquals (test.noteKeyStateChangedCallCounter, 5);
1079 test.sustainPedal (1,
false);
1080 expectEquals (test.getNumPlayingNotes(), 0);
1081 expectEquals (test.noteReleasedCallCounter, 4);
1084 beginTest (
"sostenutoPedal");
1086 UnitTestInstrument test;
1087 test.setZoneLayout (testLayout);
1092 test.sostenutoPedal (3,
true);
1095 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1098 test.sostenutoPedal (9,
true);
1101 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1104 test.sostenutoPedal (1,
true);
1107 expectEquals (test.noteKeyStateChangedCallCounter, 1);
1110 test.sostenutoPedal (1,
false);
1113 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1116 test.sostenutoPedal (1,
true);
1117 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1119 expectEquals (test.getNumPlayingNotes(), 3);
1123 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1130 expectEquals (test.getNumPlayingNotes(), 1);
1132 expectEquals (test.noteReleasedCallCounter, 2);
1133 expectEquals (test.noteKeyStateChangedCallCounter, 4);
1136 test.sustainPedal (1,
false);
1137 expectEquals (test.getNumPlayingNotes(), 0);
1138 expectEquals (test.noteReleasedCallCounter, 3);
1141 beginTest (
"getMostRecentNote");
1144 test.setZoneLayout (testLayout);
1150 auto note = test.getMostRecentNote (2);
1151 expect (! note.isValid());
1154 auto note = test.getMostRecentNote (3);
1155 expect (note.isValid());
1156 expectEquals (
int (note.midiChannel), 3);
1157 expectEquals (
int (note.initialNote), 61);
1160 test.sustainPedal (1,
true);
1164 auto note = test.getMostRecentNote (3);
1165 expect (note.isValid());
1166 expectEquals (
int (note.midiChannel), 3);
1167 expectEquals (
int (note.initialNote), 60);
1170 test.sustainPedal (1,
false);
1174 auto note = test.getMostRecentNote (3);
1175 expect (! note.isValid());
1179 beginTest (
"getMostRecentNoteOtherThan");
1181 MPENote testNote (3, 60,
1189 test.setZoneLayout (testLayout);
1190 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1193 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1196 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1197 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1198 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1204 test.setZoneLayout (testLayout);
1205 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1208 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1209 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1210 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1213 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1214 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1215 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1219 beginTest (
"pressure");
1222 UnitTestInstrument test;
1223 test.setZoneLayout (testLayout);
1234 expectEquals (test.notePressureChangedCallCounter, 1);
1241 expectEquals (test.notePressureChangedCallCounter, 3);
1248 expectEquals (test.notePressureChangedCallCounter, 3);
1251 UnitTestInstrument test;
1252 test.setZoneLayout (testLayout);
1260 expectEquals (test.notePressureChangedCallCounter, 1);
1263 UnitTestInstrument test;
1264 test.setZoneLayout (testLayout);
1272 expectEquals (test.getNumPlayingNotes(), 1);
1274 expectEquals (test.notePressureChangedCallCounter, 1);
1277 UnitTestInstrument test;
1278 test.setZoneLayout (testLayout);
1285 UnitTestInstrument test;
1286 test.setZoneLayout (testLayout);
1294 UnitTestInstrument test;
1295 test.setZoneLayout (testLayout);
1306 UnitTestInstrument test;
1307 test.setZoneLayout (testLayout);
1321 UnitTestInstrument test;
1322 test.setZoneLayout (testLayout);
1337 beginTest (
"pitchbend");
1340 UnitTestInstrument test;
1341 test.setZoneLayout (testLayout);
1352 expectEquals (test.notePitchbendChangedCallCounter, 1);
1362 expectEquals (test.notePitchbendChangedCallCounter, 3);
1369 expectEquals (test.notePitchbendChangedCallCounter, 3);
1372 UnitTestInstrument test;
1373 test.setZoneLayout (testLayout);
1381 expectEquals (test.notePitchbendChangedCallCounter, 1);
1384 UnitTestInstrument test;
1385 test.setZoneLayout (testLayout);
1393 expectEquals (test.getNumPlayingNotes(), 1);
1395 expectEquals (test.notePitchbendChangedCallCounter, 1);
1398 UnitTestInstrument test;
1399 test.setZoneLayout (testLayout);
1410 test.sustainPedal (1,
true);
1412 expectEquals (test.getNumPlayingNotes(), 1);
1414 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1418 expectEquals (test.getNumPlayingNotes(), 2);
1421 expectEquals (test.notePitchbendChangedCallCounter, 1);
1424 UnitTestInstrument test;
1425 test.setZoneLayout (testLayout);
1446 UnitTestInstrument test;
1448 MPEZoneLayout layout = testLayout;
1449 test.setZoneLayout (layout);
1452 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01);
1454 layout.setLowerZone (5, 96);
1455 test.setZoneLayout (layout);
1458 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1460 layout.setLowerZone (5, 1);
1461 test.setZoneLayout (layout);
1464 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1466 layout.setLowerZone (5, 0);
1467 test.setZoneLayout (layout);
1470 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1475 UnitTestInstrument test;
1477 MPEZoneLayout layout = testLayout;
1478 test.setZoneLayout (layout);
1481 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01);
1483 layout.setLowerZone (5, 48, 96);
1484 test.setZoneLayout (layout);
1487 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1489 layout.setLowerZone (5, 48, 1);
1490 test.setZoneLayout (layout);
1493 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1495 layout.setLowerZone (5, 48, 0);
1496 test.setZoneLayout (layout);
1499 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1505 UnitTestInstrument test;
1507 MPEZoneLayout layout = testLayout;
1508 layout.setLowerZone (5, 12, 1);
1509 test.setZoneLayout (layout);
1516 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01);
1520 beginTest (
"timbre");
1523 UnitTestInstrument test;
1524 test.setZoneLayout (testLayout);
1535 expectEquals (test.noteTimbreChangedCallCounter, 1);
1542 expectEquals (test.noteTimbreChangedCallCounter, 3);
1549 expectEquals (test.noteTimbreChangedCallCounter, 3);
1552 UnitTestInstrument test;
1553 test.setZoneLayout (testLayout);
1561 expectEquals (test.noteTimbreChangedCallCounter, 1);
1564 UnitTestInstrument test;
1565 test.setZoneLayout (testLayout);
1573 expectEquals (test.getNumPlayingNotes(), 1);
1575 expectEquals (test.noteTimbreChangedCallCounter, 1);
1578 UnitTestInstrument test;
1579 test.setZoneLayout (testLayout);
1592 beginTest (
"setPressureTrackingMode");
1596 UnitTestInstrument test;
1597 test.setZoneLayout (testLayout);
1607 expectEquals (test.notePressureChangedCallCounter, 1);
1611 UnitTestInstrument test;
1612 test.setZoneLayout (testLayout);
1622 expectEquals (test.notePressureChangedCallCounter, 1);
1626 UnitTestInstrument test;
1627 test.setZoneLayout (testLayout);
1637 expectEquals (test.notePressureChangedCallCounter, 1);
1641 UnitTestInstrument test;
1642 test.setZoneLayout (testLayout);
1652 expectEquals (test.notePressureChangedCallCounter, 3);
1656 beginTest (
"setPitchbendTrackingMode");
1660 UnitTestInstrument test;
1661 test.setZoneLayout (testLayout);
1671 expectEquals (test.notePitchbendChangedCallCounter, 1);
1675 UnitTestInstrument test;
1676 test.setZoneLayout (testLayout);
1686 expectEquals (test.notePitchbendChangedCallCounter, 1);
1690 UnitTestInstrument test;
1691 test.setZoneLayout (testLayout);
1701 expectEquals (test.notePitchbendChangedCallCounter, 1);
1705 UnitTestInstrument test;
1706 test.setZoneLayout (testLayout);
1716 expectEquals (test.notePitchbendChangedCallCounter, 3);
1720 beginTest (
"setTimbreTrackingMode");
1724 UnitTestInstrument test;
1725 test.setZoneLayout (testLayout);
1735 expectEquals (test.noteTimbreChangedCallCounter, 1);
1739 UnitTestInstrument test;
1740 test.setZoneLayout (testLayout);
1750 expectEquals (test.noteTimbreChangedCallCounter, 1);
1754 UnitTestInstrument test;
1755 test.setZoneLayout (testLayout);
1765 expectEquals (test.noteTimbreChangedCallCounter, 1);
1769 UnitTestInstrument test;
1770 test.setZoneLayout (testLayout);
1780 expectEquals (test.noteTimbreChangedCallCounter, 3);
1784 beginTest (
"processNextMidiEvent");
1786 UnitTestInstrument test;
1791 expectEquals (test.noteOnCallCounter, 1);
1792 expectEquals (test.lastMidiChannelReceived, 3);
1793 expectEquals (test.lastMidiNoteNumberReceived, 42);
1794 expectEquals (test.lastMPEValueReceived.as7BitInt(), 92);
1799 expectEquals (test.noteOffCallCounter, 1);
1800 expectEquals (test.lastMidiChannelReceived, 4);
1801 expectEquals (test.lastMidiNoteNumberReceived, 12);
1802 expectEquals (test.lastMPEValueReceived.as7BitInt(), 33);
1808 expectEquals (test.noteOffCallCounter, 2);
1809 expectEquals (test.lastMidiChannelReceived, 5);
1810 expectEquals (test.lastMidiNoteNumberReceived, 11);
1811 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1816 expectEquals (test.pitchbendCallCounter, 1);
1817 expectEquals (test.lastMidiChannelReceived, 1);
1818 expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333);
1824 expectEquals (test.pressureCallCounter, 1);
1825 expectEquals (test.lastMidiChannelReceived, 10);
1826 expectEquals (test.lastMPEValueReceived.as7BitInt(), 35);
1833 expectEquals (test.pressureCallCounter, 2);
1834 expectEquals (test.lastMidiChannelReceived, 3);
1835 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1839 expectEquals (test.pressureCallCounter, 2);
1841 expectEquals (test.pressureCallCounter, 2);
1843 expectEquals (test.pressureCallCounter, 3);
1844 expectEquals (test.lastMidiChannelReceived, 4);
1845 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1847 expectEquals (test.pressureCallCounter, 4);
1848 expectEquals (test.lastMidiChannelReceived, 5);
1849 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1851 expectEquals (test.pressureCallCounter, 5);
1852 expectEquals (test.lastMidiChannelReceived, 5);
1853 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1857 expectEquals (test.timbreCallCounter, 1);
1858 expectEquals (test.lastMidiChannelReceived, 3);
1859 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1861 expectEquals (test.timbreCallCounter, 1);
1863 expectEquals (test.timbreCallCounter, 1);
1865 expectEquals (test.timbreCallCounter, 2);
1866 expectEquals (test.lastMidiChannelReceived, 4);
1867 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1869 expectEquals (test.timbreCallCounter, 3);
1870 expectEquals (test.lastMidiChannelReceived, 5);
1871 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1873 expectEquals (test.timbreCallCounter, 4);
1874 expectEquals (test.lastMidiChannelReceived, 5);
1875 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1879 expectEquals (test.sustainPedalCallCounter, 1);
1880 expectEquals (test.lastMidiChannelReceived, 1);
1881 expect (test.lastSustainPedalValueReceived);
1883 expectEquals (test.sustainPedalCallCounter, 2);
1884 expectEquals (test.lastMidiChannelReceived, 16);
1885 expect (! test.lastSustainPedalValueReceived);
1889 expectEquals (test.sostenutoPedalCallCounter, 1);
1890 expectEquals (test.lastMidiChannelReceived, 1);
1891 expect (test.lastSostenutoPedalValueReceived);
1893 expectEquals (test.sostenutoPedalCallCounter, 2);
1894 expectEquals (test.lastMidiChannelReceived, 16);
1895 expect (! test.lastSostenutoPedalValueReceived);
1908 for (
const auto metadata : buffer)
1909 test.processNextMidiEvent (metadata.getMessage());
1911 expect (test.getZoneLayout().getLowerZone().isActive());
1912 expect (test.getZoneLayout().getUpperZone().isActive());
1913 expectEquals (test.getZoneLayout().getLowerZone().getMasterChannel(), 1);
1914 expectEquals (test.getZoneLayout().getLowerZone().numMemberChannels, 5);
1915 expectEquals (test.getZoneLayout().getUpperZone().getMasterChannel(), 16);
1916 expectEquals (test.getZoneLayout().getUpperZone().numMemberChannels, 6);
1919 beginTest (
"MIDI all notes off");
1921 UnitTestInstrument test;
1922 test.setZoneLayout (testLayout);
1927 expectEquals (test.getNumPlayingNotes(), 4);
1931 expectEquals (test.getNumPlayingNotes(), 4);
1935 expectEquals (test.getNumPlayingNotes(), 4);
1939 expectEquals (test.getNumPlayingNotes(), 2);
1941 expectEquals (test.getNumPlayingNotes(), 0);
1944 beginTest (
"MIDI all notes off (legacy mode)");
1946 UnitTestInstrument test;
1947 test.enableLegacyMode();
1952 expectEquals (test.getNumPlayingNotes(), 4);
1955 expectEquals (test.getNumPlayingNotes(), 3);
1958 expectEquals (test.getNumPlayingNotes(), 1);
1961 expectEquals (test.getNumPlayingNotes(), 0);
1964 beginTest (
"default initial values for pitchbend and timbre");
1967 test.setZoneLayout (testLayout);
1979 expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66,
MPENote::keyDown);
1982 beginTest (
"Legacy mode");
1987 expect (! test.isLegacyModeEnabled());
1989 test.setZoneLayout (testLayout);
1990 expect (! test.isLegacyModeEnabled());
1992 test.enableLegacyMode();
1993 expect (test.isLegacyModeEnabled());
1995 test.setZoneLayout (testLayout);
1996 expect (! test.isLegacyModeEnabled());
2001 test.enableLegacyMode (0, Range<int> (1, 11));
2002 expectEquals (test.getLegacyModePitchbendRange(), 0);
2003 expect (test.getLegacyModeChannelRange() == Range<int> (1, 11));
2008 test.enableLegacyMode();
2010 expectEquals (test.getLegacyModePitchbendRange(), 2);
2011 expect (test.getLegacyModeChannelRange() == Range<int> (1, 17));
2013 test.setLegacyModePitchbendRange (96);
2014 expectEquals (test.getLegacyModePitchbendRange(), 96);
2016 test.setLegacyModeChannelRange (Range<int> (10, 12));
2017 expect (test.getLegacyModeChannelRange() == Range<int> (10, 12));
2022 UnitTestInstrument test;
2023 test.enableLegacyMode();
2029 expectEquals (test.getNumPlayingNotes(), 4);
2048 expectEquals (test.getNumPlayingNotes(), 0);
2053 UnitTestInstrument test;
2054 test.enableLegacyMode (2, Range<int> (3, 8));
2065 expectEquals (test.getNumPlayingNotes(), 4);
2074 UnitTestInstrument test;
2075 test.enableLegacyMode();
2087 UnitTestInstrument test;
2088 test.enableLegacyMode();
2100 UnitTestInstrument test;
2101 test.enableLegacyMode();
2113 UnitTestInstrument test;
2114 test.enableLegacyMode();
2128 UnitTestInstrument test;
2129 test.enableLegacyMode (11);
2133 expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
2137 UnitTestInstrument test;
2138 test.enableLegacyMode();
2140 test.sustainPedal (1,
true);
2146 expectEquals (test.getNumPlayingNotes(), 1);
2149 test.sustainPedal (1,
false);
2150 expectEquals (test.getNumPlayingNotes(), 0);
2153 test.sustainPedal (1,
true);
2155 expectEquals (test.getNumPlayingNotes(), 0);
2160 UnitTestInstrument test;
2161 test.enableLegacyMode();
2164 test.sostenutoPedal (1,
true);
2169 expectEquals (test.getNumPlayingNotes(), 1);
2172 test.sostenutoPedal (1,
false);
2173 expectEquals (test.getNumPlayingNotes(), 0);
2176 test.sostenutoPedal (1,
true);
2178 expectEquals (test.getNumPlayingNotes(), 0);
2182 UnitTestInstrument test;
2183 test.setZoneLayout (testLayout);
2185 expectEquals (test.getNumPlayingNotes(), 1);
2187 test.enableLegacyMode();
2188 expectEquals (test.getNumPlayingNotes(), 0);
2190 expectEquals (test.getNumPlayingNotes(), 1);
2192 test.setZoneLayout (testLayout);
2193 expectEquals (test.getNumPlayingNotes(), 0);
2197 JUCE_END_IGNORE_WARNINGS_MSVC
2205 private MPEInstrument::Listener
2210 UnitTestInstrument()
2211 : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0),
2212 pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0),
2213 sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0),
2214 notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0),
2215 noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0),
2216 lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1),
2217 lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false)
2222 void noteOn (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOnVelocity)
override
2224 Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity);
2226 noteOnCallCounter++;
2227 lastMidiChannelReceived = midiChannel;
2228 lastMidiNoteNumberReceived = midiNoteNumber;
2229 lastMPEValueReceived = midiNoteOnVelocity;
2232 void noteOff (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOffVelocity)
override
2234 Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity);
2236 noteOffCallCounter++;
2237 lastMidiChannelReceived = midiChannel;
2238 lastMidiNoteNumberReceived = midiNoteNumber;
2239 lastMPEValueReceived = midiNoteOffVelocity;
2242 void pitchbend (
int midiChannel, MPEValue value)
override
2244 Base::pitchbend (midiChannel, value);
2246 pitchbendCallCounter++;
2247 lastMidiChannelReceived = midiChannel;
2248 lastMPEValueReceived = value;
2251 void pressure (
int midiChannel, MPEValue value)
override
2253 Base::pressure (midiChannel, value);
2255 pressureCallCounter++;
2256 lastMidiChannelReceived = midiChannel;
2257 lastMPEValueReceived = value;
2260 void timbre (
int midiChannel, MPEValue value)
override
2262 Base::timbre (midiChannel, value);
2264 timbreCallCounter++;
2265 lastMidiChannelReceived = midiChannel;
2266 lastMPEValueReceived = value;
2269 void sustainPedal (
int midiChannel,
bool value)
override
2271 Base::sustainPedal (midiChannel, value);
2273 sustainPedalCallCounter++;
2274 lastMidiChannelReceived = midiChannel;
2275 lastSustainPedalValueReceived = value;
2280 Base::sostenutoPedal (midiChannel, value);
2282 sostenutoPedalCallCounter++;
2283 lastMidiChannelReceived = midiChannel;
2284 lastSostenutoPedalValueReceived = value;
2287 void aftertouch (
int midiChannel,
int midiNoteNumber, MPEValue value)
2293 int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter,
2294 pressureCallCounter, timbreCallCounter, sustainPedalCallCounter,
2295 sostenutoPedalCallCounter, noteAddedCallCounter,
2296 notePressureChangedCallCounter, notePitchbendChangedCallCounter,
2297 noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter,
2298 noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived;
2300 bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived;
2301 MPEValue lastMPEValueReceived;
2302 std::unique_ptr<MPENote> lastNoteFinished;
2306 void noteAdded (MPENote)
override { noteAddedCallCounter++; }
2308 void notePressureChanged (MPENote)
override { notePressureChangedCallCounter++; }
2309 void notePitchbendChanged (MPENote)
override { notePitchbendChangedCallCounter++; }
2310 void noteTimbreChanged (MPENote)
override { noteTimbreChangedCallCounter++; }
2311 void noteKeyStateChanged (MPENote)
override { noteKeyStateChangedCallCounter++; }
2313 void noteReleased (MPENote finishedNote)
override
2315 noteReleasedCallCounter++;
2316 lastNoteFinished.reset (
new MPENote (finishedNote));
2321 void expectNote (MPENote noteToTest,
2322 int noteOnVelocity7Bit,
2328 expect (noteToTest.isValid());
2329 expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit);
2330 expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit);
2331 expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit);
2332 expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit);
2333 expect (noteToTest.keyState == keyState);
2336 void expectHasFinishedNote (
const UnitTestInstrument& test,
2337 int channel,
int noteNumber,
int noteOffVelocity7Bit)
2339 expect (test.lastNoteFinished !=
nullptr);
2340 expectEquals (
int (test.lastNoteFinished->midiChannel), channel);
2341 expectEquals (
int (test.lastNoteFinished->initialNote), noteNumber);
2342 expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit);
2343 expect (test.lastNoteFinished->keyState ==
MPENote::off);
2346 void expectDoubleWithinRelativeError (
double actual,
double expected,
double maxRelativeError)
2348 const double maxAbsoluteError = jmax (1.0, std::abs (expected)) * maxRelativeError;
2349 expect (std::abs (expected - actual) < maxAbsoluteError);
2353 MPEZoneLayout testLayout;
2356static MPEInstrumentTests MPEInstrumentUnitTests;
virtual void noteAdded(MPENote newNote)
virtual void noteReleased(MPENote finishedNote)
virtual void zoneLayoutChanged()
virtual void noteKeyStateChanged(MPENote changedNote)
virtual void notePitchbendChanged(MPENote changedNote)
virtual void notePressureChanged(MPENote changedNote)
virtual void noteTimbreChanged(MPENote changedNote)
void setPitchbendTrackingMode(TrackingMode modeToUse)
void setLegacyModeChannelRange(Range< int > channelRange)
MPENote getMostRecentNoteOtherThan(MPENote otherThanThisNote) const noexcept
MPEZoneLayout getZoneLayout() const noexcept
virtual void sostenutoPedal(int midiChannel, bool isDown)
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
bool isMemberChannel(int midiChannel) const noexcept
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
MPENote getNote(int index) const noexcept
@ lastNotePlayedOnChannel
void setZoneLayout(MPEZoneLayout newLayout)
virtual void polyAftertouch(int midiChannel, int midiNoteNumber, MPEValue value)
virtual void processNextMidiEvent(const MidiMessage &message)
void setLegacyModePitchbendRange(int pitchbendRange)
bool isLegacyModeEnabled() const noexcept
void setPressureTrackingMode(TrackingMode modeToUse)
void addListener(Listener *listenerToAdd)
void removeListener(Listener *listenerToRemove)
MPENote getNoteWithID(uint16 noteID) const noexcept
virtual void sustainPedal(int midiChannel, bool isDown)
void setTimbreTrackingMode(TrackingMode modeToUse)
int getNumPlayingNotes() const noexcept
virtual void timbre(int midiChannel, MPEValue value)
MPENote getMostRecentNote(int midiChannel) const noexcept
Range< int > getLegacyModeChannelRange() const noexcept
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
int getLegacyModePitchbendRange() const noexcept
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
virtual void pressure(int midiChannel, MPEValue value)
bool isMasterChannel(int midiChannel) const noexcept
bool isUsingChannel(int midiChannel) const noexcept
static MidiBuffer setUpperZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static MidiBuffer setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static MPEValue centreValue() noexcept
static MPEValue from14BitInt(int value) noexcept
static MPEValue minValue() noexcept
static MPEValue from7BitInt(int value) noexcept
MPEZone getUpperZone() const noexcept
void processNextMidiEvent(const MidiMessage &message)
MPEZone getLowerZone() const noexcept
bool isAftertouch() const noexcept
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
int getChannel() const noexcept
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
bool isController() const noexcept
static MidiMessage pitchWheel(int channel, int position) noexcept
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
bool isPitchWheel() const noexcept
int getNoteNumber() const noexcept
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
bool isResetAllControllers() const noexcept
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
bool isAllNotesOff() const noexcept
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
uint8 getVelocity() const noexcept
bool isChannelPressure() const noexcept
static MidiMessage allControllersOff(int channel) noexcept