28 const uint8 noLSBValueReceived = 0xff;
29 const Range<int> allChannels { 1, 17 };
35 std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived);
36 std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived);
37 std::fill_n (isMemberChannelSustained, 16,
false);
46 legacyMode.isEnabled =
false;
47 legacyMode.pitchbendRange = 2;
48 legacyMode.channelRange = allChannels;
66 legacyMode.isEnabled =
false;
67 zoneLayout = newLayout;
76 legacyMode.isEnabled =
true;
77 legacyMode.pitchbendRange = pitchbendRange;
78 legacyMode.channelRange = channelRange;
84 return legacyMode.isEnabled;
89 return legacyMode.channelRange;
94 jassert (allChannels.contains (channelRange));
98 legacyMode.channelRange = channelRange;
103 return legacyMode.pitchbendRange;
108 jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
112 legacyMode.pitchbendRange = pitchbendRange;
118 pressureDimension.trackingMode = modeToUse;
123 pitchbendDimension.trackingMode = modeToUse;
128 timbreDimension.trackingMode = modeToUse;
134 listeners.add (listenerToAdd);
139 listeners.remove (listenerToRemove);
147 if (message.
isNoteOn (
true)) processMidiNoteOnMessage (message);
148 else if (message.
isNoteOff (
false)) processMidiNoteOffMessage (message);
150 || message.
isAllNotesOff()) processMidiResetAllControllersMessage (message);
151 else if (message.
isPitchWheel()) processMidiPitchWheelMessage (message);
152 else if (message.
isChannelPressure()) processMidiChannelPressureMessage (message);
153 else if (message.
isController()) processMidiControllerMessage (message);
157 void MPEInstrument::processMidiNoteOnMessage (
const MidiMessage& message)
178 void MPEInstrument::processMidiNoteOffMessage (
const MidiMessage& message)
181 message.getNoteNumber(),
186 void MPEInstrument::processMidiPitchWheelMessage (
const MidiMessage& message)
193 void MPEInstrument::processMidiChannelPressureMessage (
const MidiMessage& message)
200 void MPEInstrument::processMidiControllerMessage (
const MidiMessage& message)
202 switch (message.getControllerNumber())
204 case 64:
sustainPedal (message.getChannel(), message.isSustainPedalOn());
break;
205 case 66:
sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn());
break;
206 case 70: handlePressureMSB (message.getChannel(), message.getControllerValue());
break;
207 case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue());
break;
208 case 102: handlePressureLSB (message.getChannel(), message.getControllerValue());
break;
209 case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue());
break;
215 void MPEInstrument::processMidiResetAllControllersMessage (
const MidiMessage& message)
220 if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
222 for (
auto i = notes.size(); --i >= 0;)
224 auto& note = notes.getReference (i);
226 if (note.midiChannel == message.getChannel())
230 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
237 auto zone = (message.getChannel() == 1 ? zoneLayout.
getLowerZone()
240 for (
auto i = notes.size(); --i >= 0;)
242 auto& note = notes.getReference (i);
244 if (zone.isUsingChannelAsMemberChannel (note.midiChannel))
248 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
256 void MPEInstrument::handlePressureMSB (
int midiChannel,
int value) noexcept
258 auto lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1];
264 void MPEInstrument::handlePressureLSB (
int midiChannel,
int value) noexcept
266 lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
269 void MPEInstrument::handleTimbreMSB (
int midiChannel,
int value) noexcept
271 auto lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1];
277 void MPEInstrument::handleTimbreLSB (
int midiChannel,
int value) noexcept
279 lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
293 getInitialValueForNewNote (midiChannel, pitchbendDimension),
294 getInitialValueForNewNote (midiChannel, pressureDimension),
295 getInitialValueForNewNote (midiChannel, timbreDimension),
299 updateNoteTotalPitchbend (newNote);
301 if (
auto* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber))
307 notes.remove (alreadyPlayingNote);
324 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
327 note->noteOffVelocity = midiNoteOffVelocity;
351 updateDimension (midiChannel, pitchbendDimension, value);
357 updateDimension (midiChannel, pressureDimension, value);
363 updateDimension (midiChannel, timbreDimension, value);
366 MPEValue MPEInstrument::getInitialValueForNewNote (
int midiChannel, MPEDimension& dimension)
const
368 if (getLastNotePlayedPtr (midiChannel) !=
nullptr)
371 return dimension.lastValueReceivedOnChannel[midiChannel - 1];
375 void MPEInstrument::updateDimension (
int midiChannel, MPEDimension& dimension, MPEValue value)
377 dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
386 for (
auto i = notes.size(); --i >= 0;)
388 auto& note = notes.getReference (i);
390 if (note.midiChannel == midiChannel)
391 updateDimensionForNote (note, dimension, value);
396 if (
auto* note = getNotePtr (midiChannel, dimension.trackingMode))
397 updateDimensionForNote (*note, dimension, value);
402 updateDimensionMaster (midiChannel == 1, dimension, value);
407 void MPEInstrument::updateDimensionMaster (
bool isLowerZone, MPEDimension& dimension, MPEValue value)
412 if (! zone.isActive())
415 for (
auto i = notes.size(); --i >= 0;)
417 auto& note = notes.getReference (i);
419 if (! zone.isUsingChannelAsMemberChannel (note.midiChannel))
422 if (&dimension == &pitchbendDimension)
426 updateNoteTotalPitchbend (note);
427 listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
429 else if (dimension.getValue (note) != value)
431 dimension.getValue (note) = value;
432 callListenersDimensionChanged (note, dimension);
438 void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value)
440 if (dimension.getValue (note) != value)
442 dimension.getValue (note) = value;
444 if (&dimension == &pitchbendDimension)
445 updateNoteTotalPitchbend (note);
447 callListenersDimensionChanged (note, dimension);
452 void MPEInstrument::callListenersDimensionChanged (
const MPENote& note,
const MPEDimension& dimension)
454 if (&dimension == &pressureDimension) { listeners.call ([&] (Listener& l) { l.notePressureChanged (note); });
return; }
455 if (&dimension == &timbreDimension) { listeners.call ([&] (Listener& l) { l.noteTimbreChanged (note); });
return; }
456 if (&dimension == &pitchbendDimension) { listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
return; }
460 void MPEInstrument::updateNoteTotalPitchbend (MPENote& note)
462 if (legacyMode.isEnabled)
464 note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange;
470 if (! zone.isUsingChannelAsMemberChannel (note.midiChannel))
472 if (zoneLayout.
getUpperZone().isUsingChannelAsMemberChannel (note.midiChannel))
484 auto notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone.perNotePitchbendRange;
486 auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1]
488 * zone.masterPitchbendRange;
490 note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones;
498 handleSustainOrSostenuto (midiChannel, isDown,
false);
504 handleSustainOrSostenuto (midiChannel, isDown,
true);
508 void MPEInstrument::handleSustainOrSostenuto (
int midiChannel,
bool isDown,
bool isSostenuto)
513 if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (!
isMasterChannel (midiChannel)))
516 auto zone = (midiChannel == 1 ? zoneLayout.
getLowerZone()
519 for (
auto i = notes.size(); --i >= 0;)
521 auto& note = notes.getReference (i);
523 if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsingChannelAsMemberChannel (note.midiChannel))
534 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
539 listeners.call ([&] (Listener& l) { l.noteKeyStateChanged (note); });
546 if (legacyMode.isEnabled)
548 isMemberChannelSustained[midiChannel - 1] = isDown;
552 if (zone.isLowerZone())
553 for (
auto i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
554 isMemberChannelSustained[i - 1] = isDown;
556 for (
auto i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
557 isMemberChannelSustained[i - 1] = isDown;
565 if (legacyMode.isEnabled)
566 return legacyMode.channelRange.contains (midiChannel);
568 return zoneLayout.getLowerZone().isUsingChannelAsMemberChannel (midiChannel)
569 || zoneLayout.getUpperZone().isUsingChannelAsMemberChannel (midiChannel);
574 if (legacyMode.isEnabled)
577 return (midiChannel == 1 || midiChannel == 16);
587 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
601 if (
auto* note = getLastNotePlayedPtr (midiChannel))
609 for (
auto i = notes.size(); --i >= 0;)
611 auto& note = notes.getReference (i);
613 if (note != otherThanThisNote)
621 const MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber)
const noexcept
623 for (
int i = 0; i < notes.size(); ++i)
625 auto& note = notes.getReference (i);
627 if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber)
634 MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber) noexcept
636 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, midiNoteNumber));
640 const MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode)
const noexcept
644 jassert (mode != allNotesOnChannel);
646 if (mode == lastNotePlayedOnChannel)
return getLastNotePlayedPtr (midiChannel);
647 if (mode == lowestNoteOnChannel)
return getLowestNotePtr (midiChannel);
648 if (mode == highestNoteOnChannel)
return getHighestNotePtr (midiChannel);
653 MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode) noexcept
655 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, mode));
659 const MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel)
const noexcept
661 for (
auto i = notes.size(); --i >= 0;)
663 auto& note = notes.getReference (i);
665 if (note.midiChannel == midiChannel
673 MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel) noexcept
675 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLastNotePlayedPtr (midiChannel));
679 const MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel)
const noexcept
681 int initialNoteMax = -1;
682 MPENote* result =
nullptr;
684 for (
auto i = notes.size(); --i >= 0;)
686 auto& note = notes.getReference (i);
688 if (note.midiChannel == midiChannel
690 && note.initialNote > initialNoteMax)
693 initialNoteMax = note.initialNote;
700 MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel) noexcept
702 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getHighestNotePtr (midiChannel));
705 const MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel)
const noexcept
707 int initialNoteMin = 128;
708 MPENote* result =
nullptr;
710 for (
auto i = notes.size(); --i >= 0;)
712 auto& note = notes.getReference (i);
714 if (note.midiChannel == midiChannel
716 && note.initialNote < initialNoteMin)
719 initialNoteMin = note.initialNote;
726 MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel) noexcept
728 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLowestNotePtr (midiChannel));
736 for (
auto i = notes.size(); --i >= 0;)
738 auto& note = notes.getReference (i);
751 class MPEInstrumentTests :
public UnitTest
755 :
UnitTest (
"MPEInstrument class",
"MIDI/MPE")
762 testLayout.setLowerZone (5);
763 testLayout.setUpperZone (6);
766 void runTest()
override
768 beginTest (
"initial zone layout");
771 expect (! test.getZoneLayout().getLowerZone().isActive());
772 expect (! test.getZoneLayout().getUpperZone().isActive());
775 beginTest (
"get/setZoneLayout");
778 test.setZoneLayout (testLayout);
780 auto newLayout = test.getZoneLayout();
782 expect (test.getZoneLayout().getLowerZone().isActive());
783 expect (test.getZoneLayout().getUpperZone().isActive());
784 expectEquals (newLayout.getLowerZone().getMasterChannel(), 1);
785 expectEquals (newLayout.getLowerZone().numMemberChannels, 5);
786 expectEquals (newLayout.getUpperZone().getMasterChannel(), 16);
787 expectEquals (newLayout.getUpperZone().numMemberChannels, 6);
790 beginTest (
"noteOn / noteOff");
794 test.setZoneLayout (testLayout);
795 expectEquals (test.getNumPlayingNotes(), 0);
798 UnitTestInstrument test;
799 test.setZoneLayout (testLayout);
803 expectEquals (test.getNumPlayingNotes(), 0);
804 expectEquals (test.noteAddedCallCounter, 0);
808 expectEquals (test.getNumPlayingNotes(), 0);
809 expectEquals (test.noteAddedCallCounter, 0);
813 expectEquals (test.getNumPlayingNotes(), 1);
814 expectEquals (test.noteAddedCallCounter, 1);
819 expectEquals (test.getNumPlayingNotes(), 0);
820 expectEquals (test.noteReleasedCallCounter, 1);
821 expectHasFinishedNote (test, 3, 60, 33);
824 UnitTestInstrument test;
825 test.setZoneLayout (testLayout);
830 expectEquals (test.getNumPlayingNotes(), 1);
832 expectEquals (test.noteReleasedCallCounter, 0);
836 expectEquals (test.getNumPlayingNotes(), 1);
838 expectEquals (test.noteReleasedCallCounter, 0);
842 UnitTestInstrument test;
843 test.setZoneLayout (testLayout);
847 expectEquals (test.getNumPlayingNotes(), 3);
854 UnitTestInstrument test;
855 test.setZoneLayout (testLayout);
858 expectEquals (test.getNumPlayingNotes(), 1);
863 beginTest (
"noteReleased after setZoneLayout");
865 UnitTestInstrument test;
866 test.setZoneLayout (testLayout);
871 expectEquals (test.getNumPlayingNotes(), 3);
872 expectEquals (test.noteReleasedCallCounter, 0);
874 test.setZoneLayout (testLayout);
875 expectEquals (test.getNumPlayingNotes(), 0);
876 expectEquals (test.noteReleasedCallCounter, 3);
879 beginTest (
"releaseAllNotes");
881 UnitTestInstrument test;
882 test.setZoneLayout (testLayout);
886 expectEquals (test.getNumPlayingNotes(), 3);
888 test.releaseAllNotes();
889 expectEquals (test.getNumPlayingNotes(), 0);
892 beginTest (
"sustainPedal");
894 UnitTestInstrument test;
895 test.setZoneLayout (testLayout);
900 test.sustainPedal (3,
true);
905 expectEquals (test.noteKeyStateChangedCallCounter, 0);
908 test.sustainPedal (7,
true);
911 expectEquals (test.noteKeyStateChangedCallCounter, 0);
914 test.sustainPedal (1,
true);
917 expectEquals (test.noteKeyStateChangedCallCounter, 1);
920 test.sustainPedal (1,
false);
923 expectEquals (test.noteKeyStateChangedCallCounter, 2);
926 test.sustainPedal (1,
true);
927 expectEquals (test.noteKeyStateChangedCallCounter, 3);
930 expectEquals (test.noteKeyStateChangedCallCounter, 3);
933 test.sustainPedal (11,
true);
937 expectEquals (test.noteReleasedCallCounter, 1);
943 expectEquals (test.getNumPlayingNotes(), 2);
944 expectEquals (test.noteReleasedCallCounter, 2);
945 expectEquals (test.noteKeyStateChangedCallCounter, 5);
950 test.sustainPedal (1,
false);
951 expectEquals (test.getNumPlayingNotes(), 0);
952 expectEquals (test.noteReleasedCallCounter, 4);
955 beginTest (
"sostenutoPedal");
957 UnitTestInstrument test;
958 test.setZoneLayout (testLayout);
963 test.sostenutoPedal (3,
true);
966 expectEquals (test.noteKeyStateChangedCallCounter, 0);
969 test.sostenutoPedal (9,
true);
972 expectEquals (test.noteKeyStateChangedCallCounter, 0);
975 test.sostenutoPedal (1,
true);
978 expectEquals (test.noteKeyStateChangedCallCounter, 1);
981 test.sostenutoPedal (1,
false);
984 expectEquals (test.noteKeyStateChangedCallCounter, 2);
987 test.sostenutoPedal (1,
true);
988 expectEquals (test.noteKeyStateChangedCallCounter, 3);
990 expectEquals (test.getNumPlayingNotes(), 3);
994 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1001 expectEquals (test.getNumPlayingNotes(), 1);
1003 expectEquals (test.noteReleasedCallCounter, 2);
1004 expectEquals (test.noteKeyStateChangedCallCounter, 4);
1007 test.sustainPedal (1,
false);
1008 expectEquals (test.getNumPlayingNotes(), 0);
1009 expectEquals (test.noteReleasedCallCounter, 3);
1012 beginTest (
"getMostRecentNote");
1015 test.setZoneLayout (testLayout);
1021 auto note = test.getMostRecentNote (2);
1022 expect (! note.isValid());
1025 auto note = test.getMostRecentNote (3);
1026 expect (note.isValid());
1027 expectEquals (
int (note.midiChannel), 3);
1028 expectEquals (
int (note.initialNote), 61);
1031 test.sustainPedal (1,
true);
1035 auto note = test.getMostRecentNote (3);
1036 expect (note.isValid());
1037 expectEquals (
int (note.midiChannel), 3);
1038 expectEquals (
int (note.initialNote), 60);
1041 test.sustainPedal (1,
false);
1045 auto note = test.getMostRecentNote (3);
1046 expect (! note.isValid());
1050 beginTest (
"getMostRecentNoteOtherThan");
1052 MPENote testNote (3, 60,
1060 test.setZoneLayout (testLayout);
1061 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1064 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1067 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1068 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1069 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1075 test.setZoneLayout (testLayout);
1076 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1079 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1080 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1081 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1084 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1085 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1086 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1090 beginTest (
"pressure");
1093 UnitTestInstrument test;
1094 test.setZoneLayout (testLayout);
1105 expectEquals (test.notePressureChangedCallCounter, 1);
1112 expectEquals (test.notePressureChangedCallCounter, 3);
1119 expectEquals (test.notePressureChangedCallCounter, 3);
1122 UnitTestInstrument test;
1123 test.setZoneLayout (testLayout);
1131 expectEquals (test.notePressureChangedCallCounter, 1);
1134 UnitTestInstrument test;
1135 test.setZoneLayout (testLayout);
1143 expectEquals (test.getNumPlayingNotes(), 1);
1145 expectEquals (test.notePressureChangedCallCounter, 1);
1148 UnitTestInstrument test;
1149 test.setZoneLayout (testLayout);
1156 UnitTestInstrument test;
1157 test.setZoneLayout (testLayout);
1165 UnitTestInstrument test;
1166 test.setZoneLayout (testLayout);
1177 UnitTestInstrument test;
1178 test.setZoneLayout (testLayout);
1192 beginTest (
"pitchbend");
1195 UnitTestInstrument test;
1196 test.setZoneLayout (testLayout);
1207 expectEquals (test.notePitchbendChangedCallCounter, 1);
1217 expectEquals (test.notePitchbendChangedCallCounter, 3);
1224 expectEquals (test.notePitchbendChangedCallCounter, 3);
1227 UnitTestInstrument test;
1228 test.setZoneLayout (testLayout);
1236 expectEquals (test.notePitchbendChangedCallCounter, 1);
1239 UnitTestInstrument test;
1240 test.setZoneLayout (testLayout);
1248 expectEquals (test.getNumPlayingNotes(), 1);
1250 expectEquals (test.notePitchbendChangedCallCounter, 1);
1253 UnitTestInstrument test;
1254 test.setZoneLayout (testLayout);
1265 test.sustainPedal (1,
true);
1267 expectEquals (test.getNumPlayingNotes(), 1);
1269 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1273 expectEquals (test.getNumPlayingNotes(), 2);
1276 expectEquals (test.notePitchbendChangedCallCounter, 1);
1279 UnitTestInstrument test;
1280 test.setZoneLayout (testLayout);
1301 UnitTestInstrument test;
1303 MPEZoneLayout layout = testLayout;
1304 test.setZoneLayout (layout);
1307 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01);
1309 layout.setLowerZone (5, 96);
1310 test.setZoneLayout (layout);
1313 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1315 layout.setLowerZone (5, 1);
1316 test.setZoneLayout (layout);
1319 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1321 layout.setLowerZone (5, 0);
1322 test.setZoneLayout (layout);
1325 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1330 UnitTestInstrument test;
1332 MPEZoneLayout layout = testLayout;
1333 test.setZoneLayout (layout);
1336 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01);
1338 layout.setLowerZone (5, 48, 96);
1339 test.setZoneLayout (layout);
1342 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1344 layout.setLowerZone (5, 48, 1);
1345 test.setZoneLayout (layout);
1348 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1350 layout.setLowerZone (5, 48, 0);
1351 test.setZoneLayout (layout);
1354 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1360 UnitTestInstrument test;
1362 MPEZoneLayout layout = testLayout;
1363 layout.setLowerZone (5, 12, 1);
1364 test.setZoneLayout (layout);
1371 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01);
1375 beginTest (
"timbre");
1378 UnitTestInstrument test;
1379 test.setZoneLayout (testLayout);
1390 expectEquals (test.noteTimbreChangedCallCounter, 1);
1397 expectEquals (test.noteTimbreChangedCallCounter, 3);
1404 expectEquals (test.noteTimbreChangedCallCounter, 3);
1407 UnitTestInstrument test;
1408 test.setZoneLayout (testLayout);
1416 expectEquals (test.noteTimbreChangedCallCounter, 1);
1419 UnitTestInstrument test;
1420 test.setZoneLayout (testLayout);
1428 expectEquals (test.getNumPlayingNotes(), 1);
1430 expectEquals (test.noteTimbreChangedCallCounter, 1);
1433 UnitTestInstrument test;
1434 test.setZoneLayout (testLayout);
1447 beginTest (
"setPressureTrackingMode");
1451 UnitTestInstrument test;
1452 test.setZoneLayout (testLayout);
1462 expectEquals (test.notePressureChangedCallCounter, 1);
1466 UnitTestInstrument test;
1467 test.setZoneLayout (testLayout);
1477 expectEquals (test.notePressureChangedCallCounter, 1);
1481 UnitTestInstrument test;
1482 test.setZoneLayout (testLayout);
1492 expectEquals (test.notePressureChangedCallCounter, 1);
1496 UnitTestInstrument test;
1497 test.setZoneLayout (testLayout);
1507 expectEquals (test.notePressureChangedCallCounter, 3);
1511 beginTest (
"setPitchbendTrackingMode");
1515 UnitTestInstrument test;
1516 test.setZoneLayout (testLayout);
1526 expectEquals (test.notePitchbendChangedCallCounter, 1);
1530 UnitTestInstrument test;
1531 test.setZoneLayout (testLayout);
1541 expectEquals (test.notePitchbendChangedCallCounter, 1);
1545 UnitTestInstrument test;
1546 test.setZoneLayout (testLayout);
1556 expectEquals (test.notePitchbendChangedCallCounter, 1);
1560 UnitTestInstrument test;
1561 test.setZoneLayout (testLayout);
1571 expectEquals (test.notePitchbendChangedCallCounter, 3);
1575 beginTest (
"setTimbreTrackingMode");
1579 UnitTestInstrument test;
1580 test.setZoneLayout (testLayout);
1590 expectEquals (test.noteTimbreChangedCallCounter, 1);
1594 UnitTestInstrument test;
1595 test.setZoneLayout (testLayout);
1605 expectEquals (test.noteTimbreChangedCallCounter, 1);
1609 UnitTestInstrument test;
1610 test.setZoneLayout (testLayout);
1620 expectEquals (test.noteTimbreChangedCallCounter, 1);
1624 UnitTestInstrument test;
1625 test.setZoneLayout (testLayout);
1635 expectEquals (test.noteTimbreChangedCallCounter, 3);
1639 beginTest (
"processNextMidiEvent");
1641 UnitTestInstrument test;
1646 expectEquals (test.noteOnCallCounter, 1);
1647 expectEquals (test.lastMidiChannelReceived, 3);
1648 expectEquals (test.lastMidiNoteNumberReceived, 42);
1649 expectEquals (test.lastMPEValueReceived.as7BitInt(), 92);
1654 expectEquals (test.noteOffCallCounter, 1);
1655 expectEquals (test.lastMidiChannelReceived, 4);
1656 expectEquals (test.lastMidiNoteNumberReceived, 12);
1657 expectEquals (test.lastMPEValueReceived.as7BitInt(), 33);
1663 expectEquals (test.noteOffCallCounter, 2);
1664 expectEquals (test.lastMidiChannelReceived, 5);
1665 expectEquals (test.lastMidiNoteNumberReceived, 11);
1666 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1671 expectEquals (test.pitchbendCallCounter, 1);
1672 expectEquals (test.lastMidiChannelReceived, 1);
1673 expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333);
1679 expectEquals (test.pressureCallCounter, 1);
1680 expectEquals (test.lastMidiChannelReceived, 10);
1681 expectEquals (test.lastMPEValueReceived.as7BitInt(), 35);
1688 expectEquals (test.pressureCallCounter, 2);
1689 expectEquals (test.lastMidiChannelReceived, 3);
1690 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1694 expectEquals (test.pressureCallCounter, 2);
1696 expectEquals (test.pressureCallCounter, 2);
1698 expectEquals (test.pressureCallCounter, 3);
1699 expectEquals (test.lastMidiChannelReceived, 4);
1700 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1702 expectEquals (test.pressureCallCounter, 4);
1703 expectEquals (test.lastMidiChannelReceived, 5);
1704 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1706 expectEquals (test.pressureCallCounter, 5);
1707 expectEquals (test.lastMidiChannelReceived, 5);
1708 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1712 expectEquals (test.timbreCallCounter, 1);
1713 expectEquals (test.lastMidiChannelReceived, 3);
1714 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1716 expectEquals (test.timbreCallCounter, 1);
1718 expectEquals (test.timbreCallCounter, 1);
1720 expectEquals (test.timbreCallCounter, 2);
1721 expectEquals (test.lastMidiChannelReceived, 4);
1722 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1724 expectEquals (test.timbreCallCounter, 3);
1725 expectEquals (test.lastMidiChannelReceived, 5);
1726 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1728 expectEquals (test.timbreCallCounter, 4);
1729 expectEquals (test.lastMidiChannelReceived, 5);
1730 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1734 expectEquals (test.sustainPedalCallCounter, 1);
1735 expectEquals (test.lastMidiChannelReceived, 1);
1736 expect (test.lastSustainPedalValueReceived);
1738 expectEquals (test.sustainPedalCallCounter, 2);
1739 expectEquals (test.lastMidiChannelReceived, 16);
1740 expect (! test.lastSustainPedalValueReceived);
1744 expectEquals (test.sostenutoPedalCallCounter, 1);
1745 expectEquals (test.lastMidiChannelReceived, 1);
1746 expect (test.lastSostenutoPedalValueReceived);
1748 expectEquals (test.sostenutoPedalCallCounter, 2);
1749 expectEquals (test.lastMidiChannelReceived, 16);
1750 expect (! test.lastSostenutoPedalValueReceived);
1763 MidiBuffer::Iterator iter (buffer);
1764 MidiMessage message;
1767 while (iter.getNextEvent (message, samplePosition))
1768 test.processNextMidiEvent (message);
1770 expect (test.getZoneLayout().getLowerZone().isActive());
1771 expect (test.getZoneLayout().getUpperZone().isActive());
1772 expectEquals (test.getZoneLayout().getLowerZone().getMasterChannel(), 1);
1773 expectEquals (test.getZoneLayout().getLowerZone().numMemberChannels, 5);
1774 expectEquals (test.getZoneLayout().getUpperZone().getMasterChannel(), 16);
1775 expectEquals (test.getZoneLayout().getUpperZone().numMemberChannels, 6);
1778 beginTest (
"MIDI all notes off");
1780 UnitTestInstrument test;
1781 test.setZoneLayout (testLayout);
1786 expectEquals (test.getNumPlayingNotes(), 4);
1790 expectEquals (test.getNumPlayingNotes(), 4);
1794 expectEquals (test.getNumPlayingNotes(), 4);
1798 expectEquals (test.getNumPlayingNotes(), 2);
1800 expectEquals (test.getNumPlayingNotes(), 0);
1803 beginTest (
"MIDI all notes off (legacy mode)");
1805 UnitTestInstrument test;
1806 test.enableLegacyMode();
1811 expectEquals (test.getNumPlayingNotes(), 4);
1814 expectEquals (test.getNumPlayingNotes(), 3);
1817 expectEquals (test.getNumPlayingNotes(), 1);
1820 expectEquals (test.getNumPlayingNotes(), 0);
1823 beginTest (
"default initial values for pitchbend and timbre");
1826 test.setZoneLayout (testLayout);
1838 expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66,
MPENote::keyDown);
1841 beginTest (
"Legacy mode");
1846 expect (! test.isLegacyModeEnabled());
1848 test.setZoneLayout (testLayout);
1849 expect (! test.isLegacyModeEnabled());
1851 test.enableLegacyMode();
1852 expect (test.isLegacyModeEnabled());
1854 test.setZoneLayout (testLayout);
1855 expect (! test.isLegacyModeEnabled());
1860 test.enableLegacyMode (0, Range<int> (1, 11));
1861 expectEquals (test.getLegacyModePitchbendRange(), 0);
1862 expect (test.getLegacyModeChannelRange() == Range<int> (1, 11));
1867 test.enableLegacyMode();
1869 expectEquals (test.getLegacyModePitchbendRange(), 2);
1870 expect (test.getLegacyModeChannelRange() == Range<int> (1, 17));
1872 test.setLegacyModePitchbendRange (96);
1873 expectEquals (test.getLegacyModePitchbendRange(), 96);
1875 test.setLegacyModeChannelRange (Range<int> (10, 12));
1876 expect (test.getLegacyModeChannelRange() == Range<int> (10, 12));
1881 UnitTestInstrument test;
1882 test.enableLegacyMode();
1888 expectEquals (test.getNumPlayingNotes(), 4);
1907 expectEquals (test.getNumPlayingNotes(), 0);
1912 UnitTestInstrument test;
1913 test.enableLegacyMode (2, Range<int> (3, 8));
1924 expectEquals (test.getNumPlayingNotes(), 4);
1933 UnitTestInstrument test;
1934 test.enableLegacyMode();
1946 UnitTestInstrument test;
1947 test.enableLegacyMode();
1959 UnitTestInstrument test;
1960 test.enableLegacyMode();
1972 UnitTestInstrument test;
1973 test.enableLegacyMode();
1987 UnitTestInstrument test;
1988 test.enableLegacyMode (11);
1992 expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
1996 UnitTestInstrument test;
1997 test.enableLegacyMode();
1999 test.sustainPedal (1,
true);
2005 expectEquals (test.getNumPlayingNotes(), 1);
2008 test.sustainPedal (1,
false);
2009 expectEquals (test.getNumPlayingNotes(), 0);
2012 test.sustainPedal (1,
true);
2014 expectEquals (test.getNumPlayingNotes(), 0);
2019 UnitTestInstrument test;
2020 test.enableLegacyMode();
2023 test.sostenutoPedal (1,
true);
2028 expectEquals (test.getNumPlayingNotes(), 1);
2031 test.sostenutoPedal (1,
false);
2032 expectEquals (test.getNumPlayingNotes(), 0);
2035 test.sostenutoPedal (1,
true);
2037 expectEquals (test.getNumPlayingNotes(), 0);
2041 UnitTestInstrument test;
2042 test.setZoneLayout (testLayout);
2044 expectEquals (test.getNumPlayingNotes(), 1);
2046 test.enableLegacyMode();
2047 expectEquals (test.getNumPlayingNotes(), 0);
2049 expectEquals (test.getNumPlayingNotes(), 1);
2051 test.setZoneLayout (testLayout);
2052 expectEquals (test.getNumPlayingNotes(), 0);
2062 class UnitTestInstrument :
public MPEInstrument,
2063 private MPEInstrument::Listener
2065 using Base = MPEInstrument;
2068 UnitTestInstrument()
2069 : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0),
2070 pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0),
2071 sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0),
2072 notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0),
2073 noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0),
2074 lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1),
2075 lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false)
2080 void noteOn (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOnVelocity)
override
2082 Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity);
2084 noteOnCallCounter++;
2085 lastMidiChannelReceived = midiChannel;
2086 lastMidiNoteNumberReceived = midiNoteNumber;
2087 lastMPEValueReceived = midiNoteOnVelocity;
2090 void noteOff (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOffVelocity)
override
2092 Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity);
2094 noteOffCallCounter++;
2095 lastMidiChannelReceived = midiChannel;
2096 lastMidiNoteNumberReceived = midiNoteNumber;
2097 lastMPEValueReceived = midiNoteOffVelocity;
2100 void pitchbend (
int midiChannel, MPEValue value)
override
2102 Base::pitchbend (midiChannel, value);
2104 pitchbendCallCounter++;
2105 lastMidiChannelReceived = midiChannel;
2106 lastMPEValueReceived = value;
2109 void pressure (
int midiChannel, MPEValue value)
override
2111 Base::pressure (midiChannel, value);
2113 pressureCallCounter++;
2114 lastMidiChannelReceived = midiChannel;
2115 lastMPEValueReceived = value;
2118 void timbre (
int midiChannel, MPEValue value)
override
2120 Base::timbre (midiChannel, value);
2122 timbreCallCounter++;
2123 lastMidiChannelReceived = midiChannel;
2124 lastMPEValueReceived = value;
2127 void sustainPedal (
int midiChannel,
bool value)
override
2129 Base::sustainPedal (midiChannel, value);
2131 sustainPedalCallCounter++;
2132 lastMidiChannelReceived = midiChannel;
2133 lastSustainPedalValueReceived = value;
2136 void sostenutoPedal (
int midiChannel,
bool value)
override
2138 Base::sostenutoPedal (midiChannel, value);
2140 sostenutoPedalCallCounter++;
2141 lastMidiChannelReceived = midiChannel;
2142 lastSostenutoPedalValueReceived = value;
2145 int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter,
2146 pressureCallCounter, timbreCallCounter, sustainPedalCallCounter,
2147 sostenutoPedalCallCounter, noteAddedCallCounter,
2148 notePressureChangedCallCounter, notePitchbendChangedCallCounter,
2149 noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter,
2150 noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived;
2152 bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived;
2153 MPEValue lastMPEValueReceived;
2154 std::unique_ptr<MPENote> lastNoteFinished;
2158 void noteAdded (MPENote)
override { noteAddedCallCounter++; }
2160 void notePressureChanged (MPENote)
override { notePressureChangedCallCounter++; }
2161 void notePitchbendChanged (MPENote)
override { notePitchbendChangedCallCounter++; }
2162 void noteTimbreChanged (MPENote)
override { noteTimbreChangedCallCounter++; }
2163 void noteKeyStateChanged (MPENote)
override { noteKeyStateChangedCallCounter++; }
2165 void noteReleased (MPENote finishedNote)
override
2167 noteReleasedCallCounter++;
2168 lastNoteFinished.reset (
new MPENote (finishedNote));
2173 void expectNote (MPENote noteToTest,
2174 int noteOnVelocity7Bit,
2180 expect (noteToTest.isValid());
2181 expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit);
2182 expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit);
2183 expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit);
2184 expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit);
2185 expect (noteToTest.keyState == keyState);
2188 void expectHasFinishedNote (
const UnitTestInstrument& test,
2189 int channel,
int noteNumber,
int noteOffVelocity7Bit)
2191 expect (test.lastNoteFinished !=
nullptr);
2192 expectEquals (
int (test.lastNoteFinished->midiChannel), channel);
2193 expectEquals (
int (test.lastNoteFinished->initialNote), noteNumber);
2194 expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit);
2195 expect (test.lastNoteFinished->keyState ==
MPENote::off);
2198 void expectDoubleWithinRelativeError (
double actual,
double expected,
double maxRelativeError)
2200 const double maxAbsoluteError = jmax (1.0, std::abs (expected)) * maxRelativeError;
2201 expect (std::abs (expected - actual) < maxAbsoluteError);
2205 MPEZoneLayout testLayout;
2208 static MPEInstrumentTests MPEInstrumentUnitTests;
2210 #endif // JUCE_UNIT_TESTS