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);
154 else if (message.
isAftertouch()) processMidiAfterTouchMessage (message);
158 void MPEInstrument::processMidiNoteOnMessage (
const MidiMessage& message)
179 void MPEInstrument::processMidiNoteOffMessage (
const MidiMessage& message)
182 message.getNoteNumber(),
187 void MPEInstrument::processMidiPitchWheelMessage (
const MidiMessage& message)
194 void MPEInstrument::processMidiChannelPressureMessage (
const MidiMessage& message)
201 void MPEInstrument::processMidiControllerMessage (
const MidiMessage& message)
203 switch (message.getControllerNumber())
205 case 64:
sustainPedal (message.getChannel(), message.isSustainPedalOn());
break;
206 case 66:
sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn());
break;
207 case 70: handlePressureMSB (message.getChannel(), message.getControllerValue());
break;
208 case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue());
break;
209 case 102: handlePressureLSB (message.getChannel(), message.getControllerValue());
break;
210 case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue());
break;
216 void MPEInstrument::processMidiResetAllControllersMessage (
const MidiMessage& message)
221 if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
223 for (
auto i = notes.size(); --i >= 0;)
225 auto& note = notes.getReference (i);
227 if (note.midiChannel == message.getChannel())
231 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
238 auto zone = (message.getChannel() == 1 ? zoneLayout.
getLowerZone()
241 for (
auto i = notes.size(); --i >= 0;)
243 auto& note = notes.getReference (i);
245 if (zone.isUsing (note.midiChannel))
249 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
256 void MPEInstrument::processMidiAfterTouchMessage (
const MidiMessage& message)
266 void MPEInstrument::handlePressureMSB (
int midiChannel,
int value) noexcept
268 auto lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1];
274 void MPEInstrument::handlePressureLSB (
int midiChannel,
int value) noexcept
276 lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
279 void MPEInstrument::handleTimbreMSB (
int midiChannel,
int value) noexcept
281 auto lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1];
287 void MPEInstrument::handleTimbreLSB (
int midiChannel,
int value) noexcept
289 lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
303 getInitialValueForNewNote (midiChannel, pitchbendDimension),
304 getInitialValueForNewNote (midiChannel, pressureDimension),
305 getInitialValueForNewNote (midiChannel, timbreDimension),
309 updateNoteTotalPitchbend (newNote);
311 if (
auto* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber))
317 notes.remove (alreadyPlayingNote);
334 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
337 note->noteOffVelocity = midiNoteOffVelocity;
340 if (getLastNotePlayedPtr (midiChannel) ==
nullptr)
363 updateDimension (midiChannel, pitchbendDimension, value);
369 updateDimension (midiChannel, pressureDimension, value);
375 updateDimension (midiChannel, timbreDimension, value);
382 for (
auto i = notes.size(); --i >= 0;)
384 auto& note = notes.getReference (i);
386 if (note.midiChannel == midiChannel
387 && note.initialNote == midiNoteNumber
388 && pressureDimension.getValue (note) != value)
390 pressureDimension.getValue (note) = value;
391 callListenersDimensionChanged (note, pressureDimension);
396 MPEValue MPEInstrument::getInitialValueForNewNote (
int midiChannel, MPEDimension& dimension)
const
398 if (getLastNotePlayedPtr (midiChannel) !=
nullptr)
401 return dimension.lastValueReceivedOnChannel[midiChannel - 1];
405 void MPEInstrument::updateDimension (
int midiChannel, MPEDimension& dimension, MPEValue value)
407 dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
416 for (
auto i = notes.size(); --i >= 0;)
418 auto& note = notes.getReference (i);
420 if (note.midiChannel == midiChannel)
421 updateDimensionForNote (note, dimension, value);
426 if (
auto* note = getNotePtr (midiChannel, dimension.trackingMode))
427 updateDimensionForNote (*note, dimension, value);
432 updateDimensionMaster (midiChannel == 1, dimension, value);
437 void MPEInstrument::updateDimensionMaster (
bool isLowerZone, MPEDimension& dimension, MPEValue value)
442 if (! zone.isActive())
445 for (
auto i = notes.size(); --i >= 0;)
447 auto& note = notes.getReference (i);
449 if (! zone.isUsing (note.midiChannel))
452 if (&dimension == &pitchbendDimension)
456 updateNoteTotalPitchbend (note);
457 listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
459 else if (dimension.getValue (note) != value)
461 dimension.getValue (note) = value;
462 callListenersDimensionChanged (note, dimension);
468 void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value)
470 if (dimension.getValue (note) != value)
472 dimension.getValue (note) = value;
474 if (&dimension == &pitchbendDimension)
475 updateNoteTotalPitchbend (note);
477 callListenersDimensionChanged (note, dimension);
482 void MPEInstrument::callListenersDimensionChanged (
const MPENote& note,
const MPEDimension& dimension)
484 if (&dimension == &pressureDimension) { listeners.call ([&] (Listener& l) { l.notePressureChanged (note); });
return; }
485 if (&dimension == &timbreDimension) { listeners.call ([&] (Listener& l) { l.noteTimbreChanged (note); });
return; }
486 if (&dimension == &pitchbendDimension) { listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
return; }
490 void MPEInstrument::updateNoteTotalPitchbend (MPENote& note)
492 if (legacyMode.isEnabled)
494 note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange;
500 if (! zone.isUsing (note.midiChannel))
502 if (zoneLayout.
getUpperZone().isUsing (note.midiChannel))
514 auto notePitchbendInSemitones = 0.0f;
516 if (zone.isUsingChannelAsMemberChannel (note.midiChannel))
517 notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone.perNotePitchbendRange;
519 auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1]
521 * zone.masterPitchbendRange;
523 note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones;
531 handleSustainOrSostenuto (midiChannel, isDown,
false);
537 handleSustainOrSostenuto (midiChannel, isDown,
true);
541 void MPEInstrument::handleSustainOrSostenuto (
int midiChannel,
bool isDown,
bool isSostenuto)
546 if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (!
isMasterChannel (midiChannel)))
549 auto zone = (midiChannel == 1 ? zoneLayout.
getLowerZone()
552 for (
auto i = notes.size(); --i >= 0;)
554 auto& note = notes.getReference (i);
556 if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsing (note.midiChannel))
567 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
572 listeners.call ([&] (Listener& l) { l.noteKeyStateChanged (note); });
579 if (legacyMode.isEnabled)
581 isMemberChannelSustained[midiChannel - 1] = isDown;
585 if (zone.isLowerZone())
586 for (
auto i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
587 isMemberChannelSustained[i - 1] = isDown;
589 for (
auto i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
590 isMemberChannelSustained[i - 1] = isDown;
598 if (legacyMode.isEnabled)
599 return legacyMode.channelRange.contains (midiChannel);
601 return zoneLayout.getLowerZone().isUsingChannelAsMemberChannel (midiChannel)
602 || zoneLayout.getUpperZone().isUsingChannelAsMemberChannel (midiChannel);
607 if (legacyMode.isEnabled)
610 const auto lowerZone = zoneLayout.getLowerZone();
611 const auto upperZone = zoneLayout.getUpperZone();
613 return (lowerZone.isActive() && midiChannel == lowerZone.getMasterChannel())
614 || (upperZone.isActive() && midiChannel == upperZone.getMasterChannel());
619 if (legacyMode.isEnabled)
620 return legacyMode.channelRange.contains (midiChannel);
622 return zoneLayout.getLowerZone().isUsing (midiChannel)
623 || zoneLayout.getUpperZone().isUsing (midiChannel);
634 if (
auto* note = getNotePtr (midiChannel, midiNoteNumber))
648 if (
auto* note = getLastNotePlayedPtr (midiChannel))
656 for (
auto i = notes.size(); --i >= 0;)
658 auto& note = notes.getReference (i);
660 if (note != otherThanThisNote)
668 const MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber)
const noexcept
670 for (
int i = 0; i < notes.size(); ++i)
672 auto& note = notes.getReference (i);
674 if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber)
681 MPENote* MPEInstrument::getNotePtr (
int midiChannel,
int midiNoteNumber) noexcept
683 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, midiNoteNumber));
687 const MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode)
const noexcept
691 jassert (mode != allNotesOnChannel);
693 if (mode == lastNotePlayedOnChannel)
return getLastNotePlayedPtr (midiChannel);
694 if (mode == lowestNoteOnChannel)
return getLowestNotePtr (midiChannel);
695 if (mode == highestNoteOnChannel)
return getHighestNotePtr (midiChannel);
700 MPENote* MPEInstrument::getNotePtr (
int midiChannel, TrackingMode mode) noexcept
702 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getNotePtr (midiChannel, mode));
706 const MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel)
const noexcept
708 for (
auto i = notes.size(); --i >= 0;)
710 auto& note = notes.getReference (i);
712 if (note.midiChannel == midiChannel
720 MPENote* MPEInstrument::getLastNotePlayedPtr (
int midiChannel) noexcept
722 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLastNotePlayedPtr (midiChannel));
726 const MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel)
const noexcept
728 int initialNoteMax = -1;
729 const MPENote* result =
nullptr;
731 for (
auto i = notes.size(); --i >= 0;)
733 auto& note = notes.getReference (i);
735 if (note.midiChannel == midiChannel
737 && note.initialNote > initialNoteMax)
740 initialNoteMax = note.initialNote;
747 MPENote* MPEInstrument::getHighestNotePtr (
int midiChannel) noexcept
749 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getHighestNotePtr (midiChannel));
752 const MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel)
const noexcept
754 int initialNoteMin = 128;
755 const MPENote* result =
nullptr;
757 for (
auto i = notes.size(); --i >= 0;)
759 auto& note = notes.getReference (i);
761 if (note.midiChannel == midiChannel
763 && note.initialNote < initialNoteMin)
766 initialNoteMin = note.initialNote;
773 MPENote* MPEInstrument::getLowestNotePtr (
int midiChannel) noexcept
775 return const_cast<MPENote*
> (
static_cast<const MPEInstrument&
> (*this).getLowestNotePtr (midiChannel));
783 for (
auto i = notes.size(); --i >= 0;)
785 auto& note = notes.getReference (i);
799 class MPEInstrumentTests :
public UnitTest
803 :
UnitTest (
"MPEInstrument class", UnitTestCategories::midi)
810 testLayout.setLowerZone (5);
811 testLayout.setUpperZone (6);
814 void runTest()
override
816 beginTest (
"initial zone layout");
819 expect (! test.getZoneLayout().getLowerZone().isActive());
820 expect (! test.getZoneLayout().getUpperZone().isActive());
823 beginTest (
"get/setZoneLayout");
826 test.setZoneLayout (testLayout);
828 auto newLayout = test.getZoneLayout();
830 expect (test.getZoneLayout().getLowerZone().isActive());
831 expect (test.getZoneLayout().getUpperZone().isActive());
832 expectEquals (newLayout.getLowerZone().getMasterChannel(), 1);
833 expectEquals (newLayout.getLowerZone().numMemberChannels, 5);
834 expectEquals (newLayout.getUpperZone().getMasterChannel(), 16);
835 expectEquals (newLayout.getUpperZone().numMemberChannels, 6);
838 beginTest (
"noteOn / noteOff");
842 test.setZoneLayout (testLayout);
843 expectEquals (test.getNumPlayingNotes(), 0);
846 UnitTestInstrument test;
847 test.setZoneLayout (testLayout);
851 expectEquals (test.getNumPlayingNotes(), 0);
852 expectEquals (test.noteAddedCallCounter, 0);
856 expectEquals (test.getNumPlayingNotes(), 1);
857 expectEquals (test.noteAddedCallCounter, 1);
862 expectEquals (test.getNumPlayingNotes(), 0);
863 expectEquals (test.noteReleasedCallCounter, 1);
864 expectHasFinishedNote (test, 3, 60, 33);
869 expectEquals (test.getNumPlayingNotes(), 1);
870 expectEquals (test.noteAddedCallCounter, 2);
875 expectEquals (test.getNumPlayingNotes(), 0);
876 expectEquals (test.noteReleasedCallCounter, 2);
877 expectHasFinishedNote (test, 1, 62, 33);
881 UnitTestInstrument test;
882 test.setZoneLayout (testLayout);
887 expectEquals (test.getNumPlayingNotes(), 1);
889 expectEquals (test.noteReleasedCallCounter, 0);
893 expectEquals (test.getNumPlayingNotes(), 1);
895 expectEquals (test.noteReleasedCallCounter, 0);
900 UnitTestInstrument test;
901 test.setZoneLayout (testLayout);
905 expectEquals (test.getNumPlayingNotes(), 3);
912 UnitTestInstrument test;
913 test.setZoneLayout (testLayout);
916 expectEquals (test.getNumPlayingNotes(), 1);
921 beginTest (
"noteReleased after setZoneLayout");
923 UnitTestInstrument test;
924 test.setZoneLayout (testLayout);
929 expectEquals (test.getNumPlayingNotes(), 3);
930 expectEquals (test.noteReleasedCallCounter, 0);
932 test.setZoneLayout (testLayout);
933 expectEquals (test.getNumPlayingNotes(), 0);
934 expectEquals (test.noteReleasedCallCounter, 3);
937 beginTest (
"releaseAllNotes");
939 UnitTestInstrument test;
940 test.setZoneLayout (testLayout);
944 expectEquals (test.getNumPlayingNotes(), 3);
946 test.releaseAllNotes();
947 expectEquals (test.getNumPlayingNotes(), 0);
950 beginTest (
"sustainPedal");
952 UnitTestInstrument test;
953 test.setZoneLayout (testLayout);
958 test.sustainPedal (3,
true);
963 expectEquals (test.noteKeyStateChangedCallCounter, 0);
966 test.sustainPedal (7,
true);
969 expectEquals (test.noteKeyStateChangedCallCounter, 0);
972 test.sustainPedal (1,
true);
975 expectEquals (test.noteKeyStateChangedCallCounter, 1);
978 test.sustainPedal (1,
false);
981 expectEquals (test.noteKeyStateChangedCallCounter, 2);
984 test.sustainPedal (1,
true);
985 expectEquals (test.noteKeyStateChangedCallCounter, 3);
988 expectEquals (test.noteKeyStateChangedCallCounter, 3);
991 test.sustainPedal (11,
true);
995 expectEquals (test.noteReleasedCallCounter, 1);
1001 expectEquals (test.getNumPlayingNotes(), 2);
1002 expectEquals (test.noteReleasedCallCounter, 2);
1003 expectEquals (test.noteKeyStateChangedCallCounter, 5);
1008 test.sustainPedal (1,
false);
1009 expectEquals (test.getNumPlayingNotes(), 0);
1010 expectEquals (test.noteReleasedCallCounter, 4);
1013 beginTest (
"sostenutoPedal");
1015 UnitTestInstrument test;
1016 test.setZoneLayout (testLayout);
1021 test.sostenutoPedal (3,
true);
1024 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1027 test.sostenutoPedal (9,
true);
1030 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1033 test.sostenutoPedal (1,
true);
1036 expectEquals (test.noteKeyStateChangedCallCounter, 1);
1039 test.sostenutoPedal (1,
false);
1042 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1045 test.sostenutoPedal (1,
true);
1046 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1048 expectEquals (test.getNumPlayingNotes(), 3);
1052 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1059 expectEquals (test.getNumPlayingNotes(), 1);
1061 expectEquals (test.noteReleasedCallCounter, 2);
1062 expectEquals (test.noteKeyStateChangedCallCounter, 4);
1065 test.sustainPedal (1,
false);
1066 expectEquals (test.getNumPlayingNotes(), 0);
1067 expectEquals (test.noteReleasedCallCounter, 3);
1070 beginTest (
"getMostRecentNote");
1073 test.setZoneLayout (testLayout);
1079 auto note = test.getMostRecentNote (2);
1080 expect (! note.isValid());
1083 auto note = test.getMostRecentNote (3);
1084 expect (note.isValid());
1085 expectEquals (
int (note.midiChannel), 3);
1086 expectEquals (
int (note.initialNote), 61);
1089 test.sustainPedal (1,
true);
1093 auto note = test.getMostRecentNote (3);
1094 expect (note.isValid());
1095 expectEquals (
int (note.midiChannel), 3);
1096 expectEquals (
int (note.initialNote), 60);
1099 test.sustainPedal (1,
false);
1103 auto note = test.getMostRecentNote (3);
1104 expect (! note.isValid());
1108 beginTest (
"getMostRecentNoteOtherThan");
1110 MPENote testNote (3, 60,
1118 test.setZoneLayout (testLayout);
1119 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1122 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1125 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1126 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1127 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1133 test.setZoneLayout (testLayout);
1134 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1137 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1138 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1139 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1142 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1143 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1144 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1148 beginTest (
"pressure");
1151 UnitTestInstrument test;
1152 test.setZoneLayout (testLayout);
1163 expectEquals (test.notePressureChangedCallCounter, 1);
1170 expectEquals (test.notePressureChangedCallCounter, 3);
1177 expectEquals (test.notePressureChangedCallCounter, 3);
1180 UnitTestInstrument test;
1181 test.setZoneLayout (testLayout);
1189 expectEquals (test.notePressureChangedCallCounter, 1);
1192 UnitTestInstrument test;
1193 test.setZoneLayout (testLayout);
1201 expectEquals (test.getNumPlayingNotes(), 1);
1203 expectEquals (test.notePressureChangedCallCounter, 1);
1206 UnitTestInstrument test;
1207 test.setZoneLayout (testLayout);
1214 UnitTestInstrument test;
1215 test.setZoneLayout (testLayout);
1223 UnitTestInstrument test;
1224 test.setZoneLayout (testLayout);
1235 UnitTestInstrument test;
1236 test.setZoneLayout (testLayout);
1250 UnitTestInstrument test;
1251 test.setZoneLayout (testLayout);
1266 beginTest (
"pitchbend");
1269 UnitTestInstrument test;
1270 test.setZoneLayout (testLayout);
1281 expectEquals (test.notePitchbendChangedCallCounter, 1);
1291 expectEquals (test.notePitchbendChangedCallCounter, 3);
1298 expectEquals (test.notePitchbendChangedCallCounter, 3);
1301 UnitTestInstrument test;
1302 test.setZoneLayout (testLayout);
1310 expectEquals (test.notePitchbendChangedCallCounter, 1);
1313 UnitTestInstrument test;
1314 test.setZoneLayout (testLayout);
1322 expectEquals (test.getNumPlayingNotes(), 1);
1324 expectEquals (test.notePitchbendChangedCallCounter, 1);
1327 UnitTestInstrument test;
1328 test.setZoneLayout (testLayout);
1339 test.sustainPedal (1,
true);
1341 expectEquals (test.getNumPlayingNotes(), 1);
1343 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1347 expectEquals (test.getNumPlayingNotes(), 2);
1350 expectEquals (test.notePitchbendChangedCallCounter, 1);
1353 UnitTestInstrument test;
1354 test.setZoneLayout (testLayout);
1375 UnitTestInstrument test;
1377 MPEZoneLayout layout = testLayout;
1378 test.setZoneLayout (layout);
1381 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01);
1383 layout.setLowerZone (5, 96);
1384 test.setZoneLayout (layout);
1387 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1389 layout.setLowerZone (5, 1);
1390 test.setZoneLayout (layout);
1393 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1395 layout.setLowerZone (5, 0);
1396 test.setZoneLayout (layout);
1399 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1404 UnitTestInstrument test;
1406 MPEZoneLayout layout = testLayout;
1407 test.setZoneLayout (layout);
1410 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01);
1412 layout.setLowerZone (5, 48, 96);
1413 test.setZoneLayout (layout);
1416 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1418 layout.setLowerZone (5, 48, 1);
1419 test.setZoneLayout (layout);
1422 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1424 layout.setLowerZone (5, 48, 0);
1425 test.setZoneLayout (layout);
1428 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1434 UnitTestInstrument test;
1436 MPEZoneLayout layout = testLayout;
1437 layout.setLowerZone (5, 12, 1);
1438 test.setZoneLayout (layout);
1445 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01);
1449 beginTest (
"timbre");
1452 UnitTestInstrument test;
1453 test.setZoneLayout (testLayout);
1464 expectEquals (test.noteTimbreChangedCallCounter, 1);
1471 expectEquals (test.noteTimbreChangedCallCounter, 3);
1478 expectEquals (test.noteTimbreChangedCallCounter, 3);
1481 UnitTestInstrument test;
1482 test.setZoneLayout (testLayout);
1490 expectEquals (test.noteTimbreChangedCallCounter, 1);
1493 UnitTestInstrument test;
1494 test.setZoneLayout (testLayout);
1502 expectEquals (test.getNumPlayingNotes(), 1);
1504 expectEquals (test.noteTimbreChangedCallCounter, 1);
1507 UnitTestInstrument test;
1508 test.setZoneLayout (testLayout);
1521 beginTest (
"setPressureTrackingMode");
1525 UnitTestInstrument test;
1526 test.setZoneLayout (testLayout);
1536 expectEquals (test.notePressureChangedCallCounter, 1);
1540 UnitTestInstrument test;
1541 test.setZoneLayout (testLayout);
1551 expectEquals (test.notePressureChangedCallCounter, 1);
1555 UnitTestInstrument test;
1556 test.setZoneLayout (testLayout);
1566 expectEquals (test.notePressureChangedCallCounter, 1);
1570 UnitTestInstrument test;
1571 test.setZoneLayout (testLayout);
1581 expectEquals (test.notePressureChangedCallCounter, 3);
1585 beginTest (
"setPitchbendTrackingMode");
1589 UnitTestInstrument test;
1590 test.setZoneLayout (testLayout);
1600 expectEquals (test.notePitchbendChangedCallCounter, 1);
1604 UnitTestInstrument test;
1605 test.setZoneLayout (testLayout);
1615 expectEquals (test.notePitchbendChangedCallCounter, 1);
1619 UnitTestInstrument test;
1620 test.setZoneLayout (testLayout);
1630 expectEquals (test.notePitchbendChangedCallCounter, 1);
1634 UnitTestInstrument test;
1635 test.setZoneLayout (testLayout);
1645 expectEquals (test.notePitchbendChangedCallCounter, 3);
1649 beginTest (
"setTimbreTrackingMode");
1653 UnitTestInstrument test;
1654 test.setZoneLayout (testLayout);
1664 expectEquals (test.noteTimbreChangedCallCounter, 1);
1668 UnitTestInstrument test;
1669 test.setZoneLayout (testLayout);
1679 expectEquals (test.noteTimbreChangedCallCounter, 1);
1683 UnitTestInstrument test;
1684 test.setZoneLayout (testLayout);
1694 expectEquals (test.noteTimbreChangedCallCounter, 1);
1698 UnitTestInstrument test;
1699 test.setZoneLayout (testLayout);
1709 expectEquals (test.noteTimbreChangedCallCounter, 3);
1713 beginTest (
"processNextMidiEvent");
1715 UnitTestInstrument test;
1720 expectEquals (test.noteOnCallCounter, 1);
1721 expectEquals (test.lastMidiChannelReceived, 3);
1722 expectEquals (test.lastMidiNoteNumberReceived, 42);
1723 expectEquals (test.lastMPEValueReceived.as7BitInt(), 92);
1728 expectEquals (test.noteOffCallCounter, 1);
1729 expectEquals (test.lastMidiChannelReceived, 4);
1730 expectEquals (test.lastMidiNoteNumberReceived, 12);
1731 expectEquals (test.lastMPEValueReceived.as7BitInt(), 33);
1737 expectEquals (test.noteOffCallCounter, 2);
1738 expectEquals (test.lastMidiChannelReceived, 5);
1739 expectEquals (test.lastMidiNoteNumberReceived, 11);
1740 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1745 expectEquals (test.pitchbendCallCounter, 1);
1746 expectEquals (test.lastMidiChannelReceived, 1);
1747 expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333);
1753 expectEquals (test.pressureCallCounter, 1);
1754 expectEquals (test.lastMidiChannelReceived, 10);
1755 expectEquals (test.lastMPEValueReceived.as7BitInt(), 35);
1762 expectEquals (test.pressureCallCounter, 2);
1763 expectEquals (test.lastMidiChannelReceived, 3);
1764 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1768 expectEquals (test.pressureCallCounter, 2);
1770 expectEquals (test.pressureCallCounter, 2);
1772 expectEquals (test.pressureCallCounter, 3);
1773 expectEquals (test.lastMidiChannelReceived, 4);
1774 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1776 expectEquals (test.pressureCallCounter, 4);
1777 expectEquals (test.lastMidiChannelReceived, 5);
1778 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1780 expectEquals (test.pressureCallCounter, 5);
1781 expectEquals (test.lastMidiChannelReceived, 5);
1782 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1786 expectEquals (test.timbreCallCounter, 1);
1787 expectEquals (test.lastMidiChannelReceived, 3);
1788 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1790 expectEquals (test.timbreCallCounter, 1);
1792 expectEquals (test.timbreCallCounter, 1);
1794 expectEquals (test.timbreCallCounter, 2);
1795 expectEquals (test.lastMidiChannelReceived, 4);
1796 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1798 expectEquals (test.timbreCallCounter, 3);
1799 expectEquals (test.lastMidiChannelReceived, 5);
1800 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1802 expectEquals (test.timbreCallCounter, 4);
1803 expectEquals (test.lastMidiChannelReceived, 5);
1804 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1808 expectEquals (test.sustainPedalCallCounter, 1);
1809 expectEquals (test.lastMidiChannelReceived, 1);
1810 expect (test.lastSustainPedalValueReceived);
1812 expectEquals (test.sustainPedalCallCounter, 2);
1813 expectEquals (test.lastMidiChannelReceived, 16);
1814 expect (! test.lastSustainPedalValueReceived);
1818 expectEquals (test.sostenutoPedalCallCounter, 1);
1819 expectEquals (test.lastMidiChannelReceived, 1);
1820 expect (test.lastSostenutoPedalValueReceived);
1822 expectEquals (test.sostenutoPedalCallCounter, 2);
1823 expectEquals (test.lastMidiChannelReceived, 16);
1824 expect (! test.lastSostenutoPedalValueReceived);
1837 MidiBuffer::Iterator iter (buffer);
1838 MidiMessage message;
1841 while (iter.getNextEvent (message, samplePosition))
1842 test.processNextMidiEvent (message);
1844 expect (test.getZoneLayout().getLowerZone().isActive());
1845 expect (test.getZoneLayout().getUpperZone().isActive());
1846 expectEquals (test.getZoneLayout().getLowerZone().getMasterChannel(), 1);
1847 expectEquals (test.getZoneLayout().getLowerZone().numMemberChannels, 5);
1848 expectEquals (test.getZoneLayout().getUpperZone().getMasterChannel(), 16);
1849 expectEquals (test.getZoneLayout().getUpperZone().numMemberChannels, 6);
1852 beginTest (
"MIDI all notes off");
1854 UnitTestInstrument test;
1855 test.setZoneLayout (testLayout);
1860 expectEquals (test.getNumPlayingNotes(), 4);
1864 expectEquals (test.getNumPlayingNotes(), 4);
1868 expectEquals (test.getNumPlayingNotes(), 4);
1872 expectEquals (test.getNumPlayingNotes(), 2);
1874 expectEquals (test.getNumPlayingNotes(), 0);
1877 beginTest (
"MIDI all notes off (legacy mode)");
1879 UnitTestInstrument test;
1880 test.enableLegacyMode();
1885 expectEquals (test.getNumPlayingNotes(), 4);
1888 expectEquals (test.getNumPlayingNotes(), 3);
1891 expectEquals (test.getNumPlayingNotes(), 1);
1894 expectEquals (test.getNumPlayingNotes(), 0);
1897 beginTest (
"default initial values for pitchbend and timbre");
1900 test.setZoneLayout (testLayout);
1912 expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66,
MPENote::keyDown);
1915 beginTest (
"Legacy mode");
1920 expect (! test.isLegacyModeEnabled());
1922 test.setZoneLayout (testLayout);
1923 expect (! test.isLegacyModeEnabled());
1925 test.enableLegacyMode();
1926 expect (test.isLegacyModeEnabled());
1928 test.setZoneLayout (testLayout);
1929 expect (! test.isLegacyModeEnabled());
1934 test.enableLegacyMode (0, Range<int> (1, 11));
1935 expectEquals (test.getLegacyModePitchbendRange(), 0);
1936 expect (test.getLegacyModeChannelRange() == Range<int> (1, 11));
1941 test.enableLegacyMode();
1943 expectEquals (test.getLegacyModePitchbendRange(), 2);
1944 expect (test.getLegacyModeChannelRange() == Range<int> (1, 17));
1946 test.setLegacyModePitchbendRange (96);
1947 expectEquals (test.getLegacyModePitchbendRange(), 96);
1949 test.setLegacyModeChannelRange (Range<int> (10, 12));
1950 expect (test.getLegacyModeChannelRange() == Range<int> (10, 12));
1955 UnitTestInstrument test;
1956 test.enableLegacyMode();
1962 expectEquals (test.getNumPlayingNotes(), 4);
1981 expectEquals (test.getNumPlayingNotes(), 0);
1986 UnitTestInstrument test;
1987 test.enableLegacyMode (2, Range<int> (3, 8));
1998 expectEquals (test.getNumPlayingNotes(), 4);
2007 UnitTestInstrument test;
2008 test.enableLegacyMode();
2020 UnitTestInstrument test;
2021 test.enableLegacyMode();
2033 UnitTestInstrument test;
2034 test.enableLegacyMode();
2046 UnitTestInstrument test;
2047 test.enableLegacyMode();
2061 UnitTestInstrument test;
2062 test.enableLegacyMode (11);
2066 expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
2070 UnitTestInstrument test;
2071 test.enableLegacyMode();
2073 test.sustainPedal (1,
true);
2079 expectEquals (test.getNumPlayingNotes(), 1);
2082 test.sustainPedal (1,
false);
2083 expectEquals (test.getNumPlayingNotes(), 0);
2086 test.sustainPedal (1,
true);
2088 expectEquals (test.getNumPlayingNotes(), 0);
2093 UnitTestInstrument test;
2094 test.enableLegacyMode();
2097 test.sostenutoPedal (1,
true);
2102 expectEquals (test.getNumPlayingNotes(), 1);
2105 test.sostenutoPedal (1,
false);
2106 expectEquals (test.getNumPlayingNotes(), 0);
2109 test.sostenutoPedal (1,
true);
2111 expectEquals (test.getNumPlayingNotes(), 0);
2115 UnitTestInstrument test;
2116 test.setZoneLayout (testLayout);
2118 expectEquals (test.getNumPlayingNotes(), 1);
2120 test.enableLegacyMode();
2121 expectEquals (test.getNumPlayingNotes(), 0);
2123 expectEquals (test.getNumPlayingNotes(), 1);
2125 test.setZoneLayout (testLayout);
2126 expectEquals (test.getNumPlayingNotes(), 0);
2136 class UnitTestInstrument :
public MPEInstrument,
2137 private MPEInstrument::Listener
2139 using Base = MPEInstrument;
2142 UnitTestInstrument()
2143 : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0),
2144 pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0),
2145 sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0),
2146 notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0),
2147 noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0),
2148 lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1),
2149 lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false)
2154 void noteOn (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOnVelocity)
override
2156 Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity);
2158 noteOnCallCounter++;
2159 lastMidiChannelReceived = midiChannel;
2160 lastMidiNoteNumberReceived = midiNoteNumber;
2161 lastMPEValueReceived = midiNoteOnVelocity;
2164 void noteOff (
int midiChannel,
int midiNoteNumber, MPEValue midiNoteOffVelocity)
override
2166 Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity);
2168 noteOffCallCounter++;
2169 lastMidiChannelReceived = midiChannel;
2170 lastMidiNoteNumberReceived = midiNoteNumber;
2171 lastMPEValueReceived = midiNoteOffVelocity;
2174 void pitchbend (
int midiChannel, MPEValue value)
override
2176 Base::pitchbend (midiChannel, value);
2178 pitchbendCallCounter++;
2179 lastMidiChannelReceived = midiChannel;
2180 lastMPEValueReceived = value;
2183 void pressure (
int midiChannel, MPEValue value)
override
2185 Base::pressure (midiChannel, value);
2187 pressureCallCounter++;
2188 lastMidiChannelReceived = midiChannel;
2189 lastMPEValueReceived = value;
2192 void timbre (
int midiChannel, MPEValue value)
override
2194 Base::timbre (midiChannel, value);
2196 timbreCallCounter++;
2197 lastMidiChannelReceived = midiChannel;
2198 lastMPEValueReceived = value;
2201 void sustainPedal (
int midiChannel,
bool value)
override
2203 Base::sustainPedal (midiChannel, value);
2205 sustainPedalCallCounter++;
2206 lastMidiChannelReceived = midiChannel;
2207 lastSustainPedalValueReceived = value;
2210 void sostenutoPedal (
int midiChannel,
bool value)
override
2212 Base::sostenutoPedal (midiChannel, value);
2214 sostenutoPedalCallCounter++;
2215 lastMidiChannelReceived = midiChannel;
2216 lastSostenutoPedalValueReceived = value;
2219 void aftertouch (
int midiChannel,
int midiNoteNumber, MPEValue value)
2222 processNextMidiEvent (message);
2225 int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter,
2226 pressureCallCounter, timbreCallCounter, sustainPedalCallCounter,
2227 sostenutoPedalCallCounter, noteAddedCallCounter,
2228 notePressureChangedCallCounter, notePitchbendChangedCallCounter,
2229 noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter,
2230 noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived;
2232 bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived;
2233 MPEValue lastMPEValueReceived;
2234 std::unique_ptr<MPENote> lastNoteFinished;
2238 void noteAdded (MPENote)
override { noteAddedCallCounter++; }
2240 void notePressureChanged (MPENote)
override { notePressureChangedCallCounter++; }
2241 void notePitchbendChanged (MPENote)
override { notePitchbendChangedCallCounter++; }
2242 void noteTimbreChanged (MPENote)
override { noteTimbreChangedCallCounter++; }
2243 void noteKeyStateChanged (MPENote)
override { noteKeyStateChangedCallCounter++; }
2245 void noteReleased (MPENote finishedNote)
override
2247 noteReleasedCallCounter++;
2248 lastNoteFinished.reset (
new MPENote (finishedNote));
2253 void expectNote (MPENote noteToTest,
2254 int noteOnVelocity7Bit,
2260 expect (noteToTest.isValid());
2261 expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit);
2262 expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit);
2263 expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit);
2264 expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit);
2265 expect (noteToTest.keyState == keyState);
2268 void expectHasFinishedNote (
const UnitTestInstrument& test,
2269 int channel,
int noteNumber,
int noteOffVelocity7Bit)
2271 expect (test.lastNoteFinished !=
nullptr);
2272 expectEquals (
int (test.lastNoteFinished->midiChannel), channel);
2273 expectEquals (
int (test.lastNoteFinished->initialNote), noteNumber);
2274 expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit);
2275 expect (test.lastNoteFinished->keyState ==
MPENote::off);
2278 void expectDoubleWithinRelativeError (
double actual,
double expected,
double maxRelativeError)
2280 const double maxAbsoluteError = jmax (1.0, std::abs (expected)) * maxRelativeError;
2281 expect (std::abs (expected - actual) < maxAbsoluteError);
2285 MPEZoneLayout testLayout;
2288 static MPEInstrumentTests MPEInstrumentUnitTests;
Automatically locks and unlocks a mutex object.
Derive from this class to be informed about any changes in the expressive MIDI notes played by this i...
virtual void noteAdded(MPENote newNote)
Implement this callback to be informed whenever a new expressive MIDI note is triggered.
virtual void noteReleased(MPENote finishedNote)
Implement this callback to be informed whenever an MPE note is released (either by a note-off message...
virtual void noteKeyStateChanged(MPENote changedNote)
Implement this callback to be informed whether a currently playing MPE note's key state (whether the ...
void setPitchbendTrackingMode(TrackingMode modeToUse)
Set the MPE tracking mode for the pitchbend dimension.
void setLegacyModeChannelRange(Range< int > channelRange)
Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode.
MPENote getMostRecentNoteOtherThan(MPENote otherThanThisNote) const noexcept
Returns the most recent note that is not the note passed in.
MPEZoneLayout getZoneLayout() const noexcept
Returns the current zone layout of the instrument.
virtual void sostenutoPedal(int midiChannel, bool isDown)
Request a sostenuto pedal press or release.
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
Request a pitchbend on the given channel with the given value (in units of MIDI pitchwheel position).
bool isMemberChannel(int midiChannel) const noexcept
Returns true if the given MIDI channel (1-16) is a note channel in any of the MPEInstrument's MPE zon...
virtual ~MPEInstrument()
Destructor.
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
Puts the instrument into legacy mode.
MPENote getNote(int index) const noexcept
Returns the note at the given index.
TrackingMode
The MPE note tracking mode.
@ highestNoteOnChannel
The highest note (by initialNote) on the channel with the note key still down.
@ lowestNoteOnChannel
The lowest note (by initialNote) on the channel with the note key still down.
@ lastNotePlayedOnChannel
The most recent note on the channel that is still played (key down and/or sustained).
@ allNotesOnChannel
All notes on the channel (key down and/or sustained).
MPEInstrument() noexcept
Constructor.
void setZoneLayout(MPEZoneLayout newLayout)
Re-sets the zone layout of the instrument to the one passed in.
virtual void polyAftertouch(int midiChannel, int midiNoteNumber, MPEValue value)
Request a poly-aftertouch change for a given note number.
virtual void processNextMidiEvent(const MidiMessage &message)
Process a MIDI message and trigger the appropriate method calls (noteOn, noteOff etc....
void setLegacyModePitchbendRange(int pitchbendRange)
Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode.
bool isLegacyModeEnabled() const noexcept
Returns true if the instrument is in legacy mode, false otherwise.
void setPressureTrackingMode(TrackingMode modeToUse)
Set the MPE tracking mode for the pressure dimension.
void addListener(Listener *listenerToAdd)
Adds a listener.
void removeListener(Listener *listenerToRemove)
Removes a listener.
virtual void sustainPedal(int midiChannel, bool isDown)
Request a sustain pedal press or release.
void setTimbreTrackingMode(TrackingMode modeToUse)
Set the MPE tracking mode for the timbre dimension.
int getNumPlayingNotes() const noexcept
Returns the number of MPE notes currently played by the instrument.
void releaseAllNotes()
Discard all currently playing notes.
virtual void timbre(int midiChannel, MPEValue value)
Request a third dimension (timbre) change on the given channel with the given value.
MPENote getMostRecentNote(int midiChannel) const noexcept
Returns the most recent note that is playing on the given midiChannel (this will be the note which ha...
Range< int > getLegacyModeChannelRange() const noexcept
Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode.
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
Request a note-on on the given channel, with the given initial note number and velocity.
int getLegacyModePitchbendRange() const noexcept
Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode.
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
Request a note-off.
virtual void pressure(int midiChannel, MPEValue value)
Request a pressure change on the given channel with the given value.
bool isMasterChannel(int midiChannel) const noexcept
Returns true if the given MIDI channel (1-16) is a master channel (channel 1 or 16).
bool isUsingChannel(int midiChannel) const noexcept
Returns true if the given MIDI channel (1-16) is used by any of the MPEInstrument's MPE zones; false ...
static MidiBuffer setUpperZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
Returns the sequence of MIDI messages that, if sent to an Expressive MIDI device, will set the upper ...
static MidiBuffer setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
Returns the sequence of MIDI messages that, if sent to an Expressive MIDI device, will set the lower ...
This class represents a single value for any of the MPE dimensions of control.
static MPEValue centreValue() noexcept
Constructs an MPEValue corresponding to the centre value.
static MPEValue from14BitInt(int value) noexcept
Constructs an MPEValue from an integer between 0 and 16383 (using 14-bit precision).
static MPEValue minValue() noexcept
Constructs an MPEValue corresponding to the minimum value.
static MPEValue from7BitInt(int value) noexcept
Constructs an MPEValue from an integer between 0 and 127 (using 7-bit precision).
This class represents the current MPE zone layout of a device capable of handling MPE.
void clearAllZones()
Clears the lower and upper zones of this layout, making them both inactive and disabling MPE mode.
const Zone getUpperZone() const noexcept
Returns a struct representing the upper MPE zone.
const Zone getLowerZone() const noexcept
Returns a struct representing the lower MPE zone.
void processNextMidiEvent(const MidiMessage &message)
Pass incoming MIDI messages to an object of this class if you want the zone layout to properly react ...
Encapsulates a MIDI message.
bool isAftertouch() const noexcept
Returns true if the message is an aftertouch event.
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
Returns true if this message is a 'key-down' event.
int getChannel() const noexcept
Returns the midi channel associated with the message.
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
Creates an aftertouch message.
bool isController() const noexcept
Returns true if this is a midi controller message.
static MidiMessage pitchWheel(int channel, int position) noexcept
Creates a pitch-wheel move message.
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
Returns true if this message is a 'key-up' event.
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
Creates a key-down message (using a floating-point velocity).
bool isPitchWheel() const noexcept
Returns true if the message is a pitch-wheel move.
int getNoteNumber() const noexcept
Returns the midi note number for note-on and note-off messages.
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
Creates a controller message.
bool isResetAllControllers() const noexcept
Checks whether this message is a reset all controllers message.
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
Creates a channel-pressure change event.
bool isAllNotesOff() const noexcept
Checks whether this message is an all-notes-off message.
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
Creates a key-up message.
uint8 getVelocity() const noexcept
Returns the velocity of a note-on or note-off message.
bool isChannelPressure() const noexcept
Returns true if the message is a channel-pressure change event.
static MidiMessage allControllersOff(int channel) noexcept
Creates an all-controllers-off message.
This is a base class for classes that perform a unit test.
This struct represents a playing MPE note.
KeyState
Possible values for the note key state.
@ keyDown
The note key is currently down (pressed).
@ off
The key is up (off).
@ keyDownAndSustained
The note key is down and sustained (by a sustain or sostenuto pedal).
@ sustained
The note is sustained (by a sustain or sostenuto pedal).
MPEValue timbre
Current value of the note's third expressive dimension, typically encoding some kind of timbre parame...
MPEValue pitchbend
Current per-note pitchbend of the note (in units of MIDI pitchwheel position).
MPEValue pressure
Current pressure with which the note is held down.