LMMS
Loading...
Searching...
No Matches
juce_MPEInstrument.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26namespace
27{
28 const uint8 noLSBValueReceived = 0xff;
29 const Range<int> allChannels { 1, 17 };
30
31 template <typename Range, typename Value>
32 void mpeInstrumentFill (Range& range, const Value& value)
33 {
34 std::fill (std::begin (range), std::end (range), value);
35 }
36}
37
38//==============================================================================
40{
41 mpeInstrumentFill (lastPressureLowerBitReceivedOnChannel, noLSBValueReceived);
42 mpeInstrumentFill (lastTimbreLowerBitReceivedOnChannel, noLSBValueReceived);
43 mpeInstrumentFill (isMemberChannelSustained, false);
44
48
50
51 legacyMode.channelRange = allChannels;
52}
53
59
61
62//==============================================================================
67
69{
70 struct Defaults
71 {
72 MPEDimension& dimension;
73 MPEValue defaultValue;
74 };
75
76 // The default value for pressure is 0, for all other dimensions it is centre
77 for (const auto& pair : { Defaults { pressureDimension, MPEValue::minValue() },
79 Defaults { timbreDimension, MPEValue::centreValue() } })
80 {
81 mpeInstrumentFill (pair.dimension.lastValueReceivedOnChannel, pair.defaultValue);
82 }
83}
84
86{
88
89 const ScopedLock sl (lock);
90 legacyMode.isEnabled = false;
91
92 if (zoneLayout != newLayout)
93 {
94 zoneLayout = newLayout;
95 listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
96 }
97}
98
99//==============================================================================
100void MPEInstrument::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
101{
102 if (legacyMode.isEnabled)
103 return;
104
106
107 const ScopedLock sl (lock);
108
109 legacyMode.isEnabled = true;
110 legacyMode.pitchbendRange = pitchbendRange;
111 legacyMode.channelRange = channelRange;
112
113 zoneLayout.clearAllZones();
114 listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
115}
116
118{
119 return legacyMode.isEnabled;
120}
121
126
128{
129 jassert (allChannels.contains (channelRange));
130
132 const ScopedLock sl (lock);
133
134 if (legacyMode.channelRange != channelRange)
135 {
136 legacyMode.channelRange = channelRange;
137 listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
138 }
139}
140
142{
143 return legacyMode.pitchbendRange;
144}
145
147{
148 jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
149
151 const ScopedLock sl (lock);
152
153 if (legacyMode.pitchbendRange != pitchbendRange)
154 {
155 legacyMode.pitchbendRange = pitchbendRange;
156 listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
157 }
158}
159
160//==============================================================================
162{
163 pressureDimension.trackingMode = modeToUse;
164}
165
167{
168 pitchbendDimension.trackingMode = modeToUse;
169}
170
172{
173 timbreDimension.trackingMode = modeToUse;
174}
175
176//==============================================================================
178{
179 listeners.add (listenerToAdd);
180}
181
183{
184 listeners.remove (listenerToRemove);
185}
186
187//==============================================================================
189{
190 zoneLayout.processNextMidiEvent (message);
191
192 if (message.isNoteOn (true)) processMidiNoteOnMessage (message);
193 else if (message.isNoteOff (false)) processMidiNoteOffMessage (message);
194 else if (message.isResetAllControllers()
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);
200}
201
202//==============================================================================
204{
205 // Note: If a note-on with velocity = 0 is used to convey a note-off,
206 // then the actual note-off velocity is not known. In this case,
207 // the MPE convention is to use note-off velocity = 64.
208
209 if (message.getVelocity() == 0)
210 {
211 noteOff (message.getChannel(),
212 message.getNoteNumber(),
214 }
215 else
216 {
217 noteOn (message.getChannel(),
218 message.getNoteNumber(),
219 MPEValue::from7BitInt (message.getVelocity()));
220 }
221}
222
223//==============================================================================
225{
226 noteOff (message.getChannel(),
227 message.getNoteNumber(),
228 MPEValue::from7BitInt (message.getVelocity()));
229}
230
231//==============================================================================
233{
234 pitchbend (message.getChannel(),
235 MPEValue::from14BitInt (message.getPitchWheelValue()));
236}
237
238//==============================================================================
240{
241 pressure (message.getChannel(),
242 MPEValue::from7BitInt (message.getChannelPressureValue()));
243}
244
245//==============================================================================
247{
248 switch (message.getControllerNumber())
249 {
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;
256 default: break;
257 }
258}
259
260//==============================================================================
262{
263 // in MPE mode, "reset all controllers" is per-zone and expected on the master channel;
264 // in legacy mode, it is per MIDI channel (within the channel range used).
265
266 if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
267 {
268 for (int i = notes.size(); --i >= 0;)
269 {
270 auto& note = notes.getReference (i);
271
272 if (note.midiChannel == message.getChannel())
273 {
274 note.keyState = MPENote::off;
275 note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
276 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
277 notes.remove (i);
278 }
279 }
280 }
281 else if (isMasterChannel (message.getChannel()))
282 {
283 auto zone = (message.getChannel() == 1 ? zoneLayout.getLowerZone()
284 : zoneLayout.getUpperZone());
285
286 for (int i = notes.size(); --i >= 0;)
287 {
288 auto& note = notes.getReference (i);
289
290 if (zone.isUsing (note.midiChannel))
291 {
292 note.keyState = MPENote::off;
293 note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
294 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
295 notes.remove (i);
296 }
297 }
298 }
299}
300
302{
303 if (! isMasterChannel (message.getChannel()))
304 return;
305
306 polyAftertouch (message.getChannel(), message.getNoteNumber(),
307 MPEValue::from7BitInt (message.getAfterTouchValue()));
308}
309
310//==============================================================================
311void MPEInstrument::handlePressureMSB (int midiChannel, int value) noexcept
312{
313 auto lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1];
314
315 pressure (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value)
316 : MPEValue::from14BitInt (lsb + (value << 7)));
317}
318
319void MPEInstrument::handlePressureLSB (int midiChannel, int value) noexcept
320{
322}
323
324void MPEInstrument::handleTimbreMSB (int midiChannel, int value) noexcept
325{
326 auto lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1];
327
328 timbre (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value)
329 : MPEValue::from14BitInt (lsb + (value << 7)));
330}
331
332void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept
333{
335}
336
337//==============================================================================
338void MPEInstrument::noteOn (int midiChannel,
339 int midiNoteNumber,
340 MPEValue midiNoteOnVelocity)
341{
342 if (! isUsingChannel (midiChannel))
343 return;
344
345 MPENote newNote (midiChannel,
346 midiNoteNumber,
347 midiNoteOnVelocity,
352
353 const ScopedLock sl (lock);
354 updateNoteTotalPitchbend (newNote);
355
356 if (auto* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber))
357 {
358 // pathological case: second note-on received for same note -> retrigger it
359 alreadyPlayingNote->keyState = MPENote::off;
360 alreadyPlayingNote->noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
361 listeners.call ([=] (Listener& l) { l.noteReleased (*alreadyPlayingNote); });
362 notes.remove (alreadyPlayingNote);
363 }
364
365 notes.add (newNote);
366 listeners.call ([&] (Listener& l) { l.noteAdded (newNote); });
367}
368
369//==============================================================================
370void MPEInstrument::noteOff (int midiChannel,
371 int midiNoteNumber,
372 MPEValue midiNoteOffVelocity)
373{
374 const ScopedLock sl (lock);
375
376 if (notes.isEmpty() || ! isUsingChannel (midiChannel))
377 return;
378
379 if (auto* note = getNotePtr (midiChannel, midiNoteNumber))
380 {
381 note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off;
382 note->noteOffVelocity = midiNoteOffVelocity;
383
384 // If no more notes are playing on this channel in mpe mode, reset the dimension values
385 if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) == nullptr)
386 {
387 pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue();
388 pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue();
389 timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue();
390 }
391
392 if (note->keyState == MPENote::off)
393 {
394 listeners.call ([=] (Listener& l) { l.noteReleased (*note); });
395 notes.remove (note);
396 }
397 else
398 {
399 listeners.call ([=] (Listener& l) { l.noteKeyStateChanged (*note); });
400 }
401 }
402}
403
404//==============================================================================
406{
407 const ScopedLock sl (lock);
409}
410
412{
413 const ScopedLock sl (lock);
415}
416
417void MPEInstrument::timbre (int midiChannel, MPEValue value)
418{
419 const ScopedLock sl (lock);
420 updateDimension (midiChannel, timbreDimension, value);
421}
422
423void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValue value)
424{
425 const ScopedLock sl (lock);
426
427 for (int i = notes.size(); --i >= 0;)
428 {
429 auto& note = notes.getReference (i);
430
431 if (note.midiChannel == midiChannel
432 && note.initialNote == midiNoteNumber
433 && pressureDimension.getValue (note) != value)
434 {
435 pressureDimension.getValue (note) = value;
437 }
438 }
439}
440
442{
443 if (! legacyMode.isEnabled && getLastNotePlayedPtr (midiChannel) != nullptr)
444 return &dimension == &pressureDimension ? MPEValue::minValue() : MPEValue::centreValue();
445
446 return dimension.lastValueReceivedOnChannel[midiChannel - 1];
447}
448
449//==============================================================================
451{
452 dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
453
454 if (notes.isEmpty())
455 return;
456
457 if (isMemberChannel (midiChannel))
458 {
459 if (dimension.trackingMode == allNotesOnChannel)
460 {
461 for (int i = notes.size(); --i >= 0;)
462 {
463 auto& note = notes.getReference (i);
464
465 if (note.midiChannel == midiChannel)
466 updateDimensionForNote (note, dimension, value);
467 }
468 }
469 else
470 {
471 if (auto* note = getNotePtr (midiChannel, dimension.trackingMode))
472 updateDimensionForNote (*note, dimension, value);
473 }
474 }
475 else if (isMasterChannel (midiChannel))
476 {
477 updateDimensionMaster (midiChannel == 1, dimension, value);
478 }
479}
480
481//==============================================================================
483{
484 auto zone = (isLowerZone ? zoneLayout.getLowerZone()
485 : zoneLayout.getUpperZone());
486
487 if (! zone.isActive())
488 return;
489
490 for (int i = notes.size(); --i >= 0;)
491 {
492 auto& note = notes.getReference (i);
493
494 if (! zone.isUsing (note.midiChannel))
495 continue;
496
497 if (&dimension == &pitchbendDimension)
498 {
499 // master pitchbend is a special case: we don't change the note's own pitchbend,
500 // instead we have to update its total (master + note) pitchbend.
502 listeners.call ([&] (Listener& l) { l.notePitchbendChanged (note); });
503 }
504 else if (dimension.getValue (note) != value)
505 {
506 dimension.getValue (note) = value;
507 callListenersDimensionChanged (note, dimension);
508 }
509 }
510}
511
512//==============================================================================
514{
515 if (dimension.getValue (note) != value)
516 {
517 dimension.getValue (note) = value;
518
519 if (&dimension == &pitchbendDimension)
521
522 callListenersDimensionChanged (note, dimension);
523 }
524}
525
526//==============================================================================
528{
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; }
532}
533
534//==============================================================================
536{
537 if (legacyMode.isEnabled)
538 {
539 note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) legacyMode.pitchbendRange;
540 }
541 else
542 {
543 auto zone = zoneLayout.getLowerZone();
544
545 if (! zone.isActive() || ! zone.isUsing (note.midiChannel))
546 {
547 auto upperZone = zoneLayout.getUpperZone();
548
549 if (upperZone.isActive() && upperZone.isUsing (note.midiChannel))
550 {
551 zone = upperZone;
552 }
553 else
554 {
555 // this note doesn't belong to any zone!
557 return;
558 }
559 }
560
561 auto notePitchbendInSemitones = 0.0f;
562
563 if (zone.isUsingChannelAsMemberChannel (note.midiChannel))
564 notePitchbendInSemitones = note.pitchbend.asSignedFloat() * (float) zone.perNotePitchbendRange;
565
566 auto masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone.getMasterChannel() - 1]
567 .asSignedFloat()
568 * (float) zone.masterPitchbendRange;
569
570 note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones;
571 }
572}
573
574//==============================================================================
575void MPEInstrument::sustainPedal (int midiChannel, bool isDown)
576{
577 const ScopedLock sl (lock);
578 handleSustainOrSostenuto (midiChannel, isDown, false);
579}
580
581void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown)
582{
583 const ScopedLock sl (lock);
584 handleSustainOrSostenuto (midiChannel, isDown, true);
585}
586
587//==============================================================================
588void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto)
589{
590 // in MPE mode, sustain/sostenuto is per-zone and expected on the master channel;
591 // in legacy mode, sustain/sostenuto is per MIDI channel (within the channel range used).
592
593 if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (! isMasterChannel (midiChannel)))
594 return;
595
596 auto zone = (midiChannel == 1 ? zoneLayout.getLowerZone()
597 : zoneLayout.getUpperZone());
598
599 for (int i = notes.size(); --i >= 0;)
600 {
601 auto& note = notes.getReference (i);
602
603 if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : zone.isUsing (note.midiChannel))
604 {
605 if (note.keyState == MPENote::keyDown && isDown)
606 note.keyState = MPENote::keyDownAndSustained;
607 else if (note.keyState == MPENote::sustained && ! isDown)
608 note.keyState = MPENote::off;
609 else if (note.keyState == MPENote::keyDownAndSustained && ! isDown)
610 note.keyState = MPENote::keyDown;
611
612 if (note.keyState == MPENote::off)
613 {
614 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
615 notes.remove (i);
616 }
617 else
618 {
619 listeners.call ([&] (Listener& l) { l.noteKeyStateChanged (note); });
620 }
621 }
622 }
623
624 if (! isSostenuto)
625 {
626 isMemberChannelSustained[midiChannel - 1] = isDown;
627
628 if (! legacyMode.isEnabled)
629 {
630 if (zone.isLowerZone())
631 {
632 for (int i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
633 isMemberChannelSustained[i - 1] = isDown;
634 }
635 else
636 {
637 for (int i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
638 isMemberChannelSustained[i - 1] = isDown;
639 }
640 }
641 }
642}
643
644//==============================================================================
645bool MPEInstrument::isMemberChannel (int midiChannel) const noexcept
646{
647 if (legacyMode.isEnabled)
648 return legacyMode.channelRange.contains (midiChannel);
649
650 return zoneLayout.getLowerZone().isUsingChannelAsMemberChannel (midiChannel)
651 || zoneLayout.getUpperZone().isUsingChannelAsMemberChannel (midiChannel);
652}
653
654bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept
655{
656 if (legacyMode.isEnabled)
657 return false;
658
659 const auto lowerZone = zoneLayout.getLowerZone();
660 const auto upperZone = zoneLayout.getUpperZone();
661
662 return (lowerZone.isActive() && midiChannel == lowerZone.getMasterChannel())
663 || (upperZone.isActive() && midiChannel == upperZone.getMasterChannel());
664}
665
666bool MPEInstrument::isUsingChannel (int midiChannel) const noexcept
667{
668 if (legacyMode.isEnabled)
669 return legacyMode.channelRange.contains (midiChannel);
670
671 return zoneLayout.getLowerZone().isUsing (midiChannel)
672 || zoneLayout.getUpperZone().isUsing (midiChannel);
673}
674
675//==============================================================================
677{
678 return notes.size();
679}
680
681MPENote MPEInstrument::getNote (int midiChannel, int midiNoteNumber) const noexcept
682{
683 if (auto* note = getNotePtr (midiChannel, midiNoteNumber))
684 return *note;
685
686 return {};
687}
688
689MPENote MPEInstrument::getNote (int index) const noexcept
690{
691 return notes[index];
692}
693
695{
696 const ScopedLock sl (lock);
697
698 for (auto& note : notes)
699 if (note.noteID == noteID)
700 return note;
701
702 return {};
703}
704
705//==============================================================================
706MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept
707{
708 if (auto* note = getLastNotePlayedPtr (midiChannel))
709 return *note;
710
711 return {};
712}
713
715{
716 for (auto i = notes.size(); --i >= 0;)
717 {
718 auto& note = notes.getReference (i);
719
720 if (note != otherThanThisNote)
721 return note;
722 }
723
724 return {};
725}
726
727//==============================================================================
728const MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) const noexcept
729{
730 for (int i = 0; i < notes.size(); ++i)
731 {
732 auto& note = notes.getReference (i);
733
734 if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber)
735 return &note;
736 }
737
738 return nullptr;
739}
740
741MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) noexcept
742{
743 return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getNotePtr (midiChannel, midiNoteNumber));
744}
745
746//==============================================================================
747const MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) const noexcept
748{
749 // for the "all notes" tracking mode, this method can never possibly
750 // work because it returns 0 or 1 note but there might be more than one!
752
753 if (mode == lastNotePlayedOnChannel) return getLastNotePlayedPtr (midiChannel);
754 if (mode == lowestNoteOnChannel) return getLowestNotePtr (midiChannel);
755 if (mode == highestNoteOnChannel) return getHighestNotePtr (midiChannel);
756
757 return nullptr;
758}
759
761{
762 return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getNotePtr (midiChannel, mode));
763}
764
765//==============================================================================
766const MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept
767{
768 const ScopedLock sl (lock);
769
770 for (auto i = notes.size(); --i >= 0;)
771 {
772 auto& note = notes.getReference (i);
773
774 if (note.midiChannel == midiChannel
775 && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained))
776 return &note;
777 }
778
779 return nullptr;
780}
781
783{
784 return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getLastNotePlayedPtr (midiChannel));
785}
786
787//==============================================================================
788const MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) const noexcept
789{
790 int initialNoteMax = -1;
791 const MPENote* result = nullptr;
792
793 for (auto i = notes.size(); --i >= 0;)
794 {
795 auto& note = notes.getReference (i);
796
797 if (note.midiChannel == midiChannel
798 && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)
799 && note.initialNote > initialNoteMax)
800 {
801 result = &note;
802 initialNoteMax = note.initialNote;
803 }
804 }
805
806 return result;
807}
808
809MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) noexcept
810{
811 return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getHighestNotePtr (midiChannel));
812}
813
814const MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) const noexcept
815{
816 int initialNoteMin = 128;
817 const MPENote* result = nullptr;
818
819 for (auto i = notes.size(); --i >= 0;)
820 {
821 auto& note = notes.getReference (i);
822
823 if (note.midiChannel == midiChannel
824 && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)
825 && note.initialNote < initialNoteMin)
826 {
827 result = &note;
828 initialNoteMin = note.initialNote;
829 }
830 }
831
832 return result;
833}
834
835MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) noexcept
836{
837 return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getLowestNotePtr (midiChannel));
838}
839
840//==============================================================================
842{
843 const ScopedLock sl (lock);
844
845 for (auto i = notes.size(); --i >= 0;)
846 {
847 auto& note = notes.getReference (i);
848 note.keyState = MPENote::off;
849 note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
850 listeners.call ([&] (Listener& l) { l.noteReleased (note); });
851 }
852
853 notes.clear();
854}
855
856
857//==============================================================================
858//==============================================================================
859#if JUCE_UNIT_TESTS
860
861class MPEInstrumentTests : public UnitTest
862{
863public:
864 MPEInstrumentTests()
865 : UnitTest ("MPEInstrument class", UnitTestCategories::midi)
866 {
867 // using lower and upper MPE zones with the following layout for testing
868 //
869 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
870 // * ...................| |........................ *
871
872 testLayout.setLowerZone (5);
873 testLayout.setUpperZone (6);
874 }
875
877 void runTest() override
878 {
879 beginTest ("initial zone layout");
880 {
881 MPEInstrument test;
882 expect (! test.getZoneLayout().getLowerZone().isActive());
883 expect (! test.getZoneLayout().getUpperZone().isActive());
884 }
885
886 beginTest ("get/setZoneLayout");
887 {
888 MPEInstrument test;
889 test.setZoneLayout (testLayout);
890
891 auto newLayout = test.getZoneLayout();
892
893 expect (test.getZoneLayout().getLowerZone().isActive());
894 expect (test.getZoneLayout().getUpperZone().isActive());
895 expectEquals (newLayout.getLowerZone().getMasterChannel(), 1);
896 expectEquals (newLayout.getLowerZone().numMemberChannels, 5);
897 expectEquals (newLayout.getUpperZone().getMasterChannel(), 16);
898 expectEquals (newLayout.getUpperZone().numMemberChannels, 6);
899 }
900
901 beginTest ("noteOn / noteOff");
902 {
903 {
904 MPEInstrument test;
905 test.setZoneLayout (testLayout);
906 expectEquals (test.getNumPlayingNotes(), 0);
907 }
908 {
909 UnitTestInstrument test;
910 test.setZoneLayout (testLayout);
911
912 // note-on on unused channel - ignore
913 test.noteOn (7, 60, MPEValue::from7BitInt (100));
914 expectEquals (test.getNumPlayingNotes(), 0);
915 expectEquals (test.noteAddedCallCounter, 0);
916
917 // note-on on member channel - create new note
918 test.noteOn (3, 60, MPEValue::from7BitInt (100));
919 expectEquals (test.getNumPlayingNotes(), 1);
920 expectEquals (test.noteAddedCallCounter, 1);
921 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
922
923 // note-off
924 test.noteOff (3, 60, MPEValue::from7BitInt (33));
925 expectEquals (test.getNumPlayingNotes(), 0);
926 expectEquals (test.noteReleasedCallCounter, 1);
927 expectHasFinishedNote (test, 3, 60, 33);
928
929
930 // note-on on master channel - create new note
931 test.noteOn (1, 62, MPEValue::from7BitInt (100));
932 expectEquals (test.getNumPlayingNotes(), 1);
933 expectEquals (test.noteAddedCallCounter, 2);
934 expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown);
935
936 // note-off
937 test.noteOff (1, 62, MPEValue::from7BitInt (33));
938 expectEquals (test.getNumPlayingNotes(), 0);
939 expectEquals (test.noteReleasedCallCounter, 2);
940 expectHasFinishedNote (test, 1, 62, 33);
941 }
942
943 {
944 UnitTestInstrument test;
945 test.setZoneLayout (testLayout);
946 test.noteOn (3, 60, MPEValue::from7BitInt (100));
947
948 // note off with non-matching note number shouldn't do anything
949 test.noteOff (3, 61, MPEValue::from7BitInt (33));
950 expectEquals (test.getNumPlayingNotes(), 1);
951 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
952 expectEquals (test.noteReleasedCallCounter, 0);
953
954 // note off with non-matching midi channel shouldn't do anything
955 test.noteOff (2, 60, MPEValue::from7BitInt (33));
956 expectEquals (test.getNumPlayingNotes(), 1);
957 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
958 expectEquals (test.noteReleasedCallCounter, 0);
959 }
960
961 {
962 // can have multiple notes on the same channel
963 UnitTestInstrument test;
964 test.setZoneLayout (testLayout);
965 test.noteOn (3, 0, MPEValue::from7BitInt (100));
966 test.noteOn (3, 1, MPEValue::from7BitInt (100));
967 test.noteOn (3, 2, MPEValue::from7BitInt (100));
968 expectEquals (test.getNumPlayingNotes(), 3);
969 expectNote (test.getNote (3, 0), 100, 0, 8192, 64, MPENote::keyDown);
970 expectNote (test.getNote (3, 1), 100, 0, 8192, 64, MPENote::keyDown);
971 expectNote (test.getNote (3, 2), 100, 0, 8192, 64, MPENote::keyDown);
972 }
973 {
974 // pathological case: second note-on for same note should retrigger it.
975 UnitTestInstrument test;
976 test.setZoneLayout (testLayout);
977 test.noteOn (3, 0, MPEValue::from7BitInt (100));
978 test.noteOn (3, 0, MPEValue::from7BitInt (60));
979 expectEquals (test.getNumPlayingNotes(), 1);
980 expectNote (test.getNote (3, 0), 60, 0, 8192, 64, MPENote::keyDown);
981 }
982 }
983
984 beginTest ("noteReleased after setZoneLayout");
985 {
986 UnitTestInstrument test;
987 test.setZoneLayout (testLayout);
988
989 test.noteOn (3, 60, MPEValue::from7BitInt (100));
990 test.noteOn (3, 61, MPEValue::from7BitInt (100));
991 test.noteOn (4, 61, MPEValue::from7BitInt (100));
992 expectEquals (test.getNumPlayingNotes(), 3);
993 expectEquals (test.noteReleasedCallCounter, 0);
994
995 test.setZoneLayout (testLayout);
996 expectEquals (test.getNumPlayingNotes(), 0);
997 expectEquals (test.noteReleasedCallCounter, 3);
998 }
999
1000 beginTest ("releaseAllNotes");
1001 {
1002 UnitTestInstrument test;
1003 test.setZoneLayout (testLayout);
1004 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1005 test.noteOn (4, 61, MPEValue::from7BitInt (100));
1006 test.noteOn (15, 62, MPEValue::from7BitInt (100));
1007 expectEquals (test.getNumPlayingNotes(), 3);
1008
1009 test.releaseAllNotes();
1010 expectEquals (test.getNumPlayingNotes(), 0);
1011 }
1012
1013 beginTest ("sustainPedal");
1014 {
1015 UnitTestInstrument test;
1016 test.setZoneLayout (testLayout);
1017 test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in lower zone
1018 test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in upper zone
1019
1020 // sustain pedal on per-note channel shouldn't do anything.
1021 test.sustainPedal (3, true);
1022 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1023
1024 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1025 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1026 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1027
1028 // sustain pedal on non-zone channel shouldn't do anything either.
1029 test.sustainPedal (7, true);
1030 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1031 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1032 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1033
1034 // sustain pedal on master channel should sustain notes on _that_ zone.
1035 test.sustainPedal (1, true);
1036 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
1037 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1038 expectEquals (test.noteKeyStateChangedCallCounter, 1);
1039
1040 // release
1041 test.sustainPedal (1, false);
1042 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1043 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1044 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1045
1046 // should also sustain new notes added after the press
1047 test.sustainPedal (1, true);
1048 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1049 test.noteOn (4, 51, MPEValue::from7BitInt (100));
1050 expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
1051 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1052
1053 // ...but only if that sustain came on the master channel of that zone!
1054 test.sustainPedal (11, true);
1055 test.noteOn (11, 52, MPEValue::from7BitInt (100));
1056 expectNote (test.getNote (11, 52), 100, 0, 8192, 64, MPENote::keyDown);
1057 test.noteOff (11, 52, MPEValue::from7BitInt (100));
1058 expectEquals (test.noteReleasedCallCounter, 1);
1059
1060 // note-off should not turn off sustained notes inside the same zone
1061 test.noteOff (3, 60, MPEValue::from7BitInt (100));
1062 test.noteOff (4, 51, MPEValue::from7BitInt (100));
1063 test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected!
1064 expectEquals (test.getNumPlayingNotes(), 2);
1065 expectEquals (test.noteReleasedCallCounter, 2);
1066 expectEquals (test.noteKeyStateChangedCallCounter, 5);
1067 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
1068 expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::sustained);
1069
1070 // notes should be turned off when pedal is released
1071 test.sustainPedal (1, false);
1072 expectEquals (test.getNumPlayingNotes(), 0);
1073 expectEquals (test.noteReleasedCallCounter, 4);
1074 }
1075
1076 beginTest ("sostenutoPedal");
1077 {
1078 UnitTestInstrument test;
1079 test.setZoneLayout (testLayout);
1080 test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in lower zone
1081 test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in upper zone
1082
1083 // sostenuto pedal on per-note channel shouldn't do anything.
1084 test.sostenutoPedal (3, true);
1085 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1086 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1087 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1088
1089 // sostenuto pedal on non-zone channel shouldn't do anything either.
1090 test.sostenutoPedal (9, true);
1091 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1092 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1093 expectEquals (test.noteKeyStateChangedCallCounter, 0);
1094
1095 // sostenuto pedal on master channel should sustain notes on *that* zone.
1096 test.sostenutoPedal (1, true);
1097 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
1098 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1099 expectEquals (test.noteKeyStateChangedCallCounter, 1);
1100
1101 // release
1102 test.sostenutoPedal (1, false);
1103 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1104 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1105 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1106
1107 // should only sustain notes turned on *before* the press (difference to sustain pedal)
1108 test.sostenutoPedal (1, true);
1109 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1110 test.noteOn (4, 51, MPEValue::from7BitInt (100));
1111 expectEquals (test.getNumPlayingNotes(), 3);
1112 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
1113 expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDown);
1114 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1115 expectEquals (test.noteKeyStateChangedCallCounter, 3);
1116
1117 // note-off should not turn off sustained notes inside the same zone,
1118 // but only if they were turned on *before* the sostenuto pedal (difference to sustain pedal)
1119 test.noteOff (3, 60, MPEValue::from7BitInt (100));
1120 test.noteOff (4, 51, MPEValue::from7BitInt (100));
1121 test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected!
1122 expectEquals (test.getNumPlayingNotes(), 1);
1123 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
1124 expectEquals (test.noteReleasedCallCounter, 2);
1125 expectEquals (test.noteKeyStateChangedCallCounter, 4);
1126
1127 // notes should be turned off when pedal is released
1128 test.sustainPedal (1, false);
1129 expectEquals (test.getNumPlayingNotes(), 0);
1130 expectEquals (test.noteReleasedCallCounter, 3);
1131 }
1132
1133 beginTest ("getMostRecentNote");
1134 {
1135 MPEInstrument test;
1136 test.setZoneLayout (testLayout);
1137
1138 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1139 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1140
1141 {
1142 auto note = test.getMostRecentNote (2);
1143 expect (! note.isValid());
1144 }
1145 {
1146 auto note = test.getMostRecentNote (3);
1147 expect (note.isValid());
1148 expectEquals (int (note.midiChannel), 3);
1149 expectEquals (int (note.initialNote), 61);
1150 }
1151
1152 test.sustainPedal (1, true);
1153 test.noteOff (3, 61, MPEValue::from7BitInt (100));
1154
1155 {
1156 auto note = test.getMostRecentNote (3);
1157 expect (note.isValid());
1158 expectEquals (int (note.midiChannel), 3);
1159 expectEquals (int (note.initialNote), 60);
1160 }
1161
1162 test.sustainPedal (1, false);
1163 test.noteOff (3, 60, MPEValue::from7BitInt (100));
1164
1165 {
1166 auto note = test.getMostRecentNote (3);
1167 expect (! note.isValid());
1168 }
1169 }
1170
1171 beginTest ("getMostRecentNoteOtherThan");
1172 {
1173 MPENote testNote (3, 60,
1174 MPEValue::centreValue(), MPEValue::centreValue(),
1175 MPEValue::centreValue(), MPEValue::centreValue());
1176
1177 {
1178 // case 1: the note to exclude is not the most recent one.
1179
1180 MPEInstrument test;
1181 test.setZoneLayout (testLayout);
1182 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1183
1184 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1185 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1186
1187 test.noteOn (4, 61, MPEValue::from7BitInt (100));
1188 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1189 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1190 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1191 }
1192 {
1193 // case 2: the note to exclude is the most recent one.
1194
1195 MPEInstrument test;
1196 test.setZoneLayout (testLayout);
1197 expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
1198
1199 test.noteOn (4, 61, MPEValue::from7BitInt (100));
1200 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1201 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1202 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1203
1204 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1205 expect (test.getMostRecentNoteOtherThan (testNote).isValid());
1206 expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
1207 expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
1208 }
1209 }
1210
1211 beginTest ("pressure");
1212 {
1213 {
1214 UnitTestInstrument test;
1215 test.setZoneLayout (testLayout);
1216
1217 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1218 test.noteOn (4, 60, MPEValue::from7BitInt (100));
1219 test.noteOn (10, 60, MPEValue::from7BitInt (100));
1220
1221 // applying pressure on a per-note channel should modulate one note
1222 test.pressure (3, MPEValue::from7BitInt (33));
1223 expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown);
1224 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
1225 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1226 expectEquals (test.notePressureChangedCallCounter, 1);
1227
1228 // applying pressure on a master channel should modulate all notes in this zone
1229 test.pressure (1, MPEValue::from7BitInt (44));
1230 expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
1231 expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
1232 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1233 expectEquals (test.notePressureChangedCallCounter, 3);
1234
1235 // applying pressure on an unrelated channel should be ignored
1236 test.pressure (8, MPEValue::from7BitInt (55));
1237 expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
1238 expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
1239 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1240 expectEquals (test.notePressureChangedCallCounter, 3);
1241 }
1242 {
1243 UnitTestInstrument test;
1244 test.setZoneLayout (testLayout);
1245
1246 // two notes on same channel - only last added should be modulated
1247 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1248 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1249 test.pressure (3, MPEValue::from7BitInt (66));
1250 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1251 expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown);
1252 expectEquals (test.notePressureChangedCallCounter, 1);
1253 }
1254 {
1255 UnitTestInstrument test;
1256 test.setZoneLayout (testLayout);
1257
1258 // edge case: two notes on same channel, one gets released,
1259 // then the other should be modulated
1260 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1261 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1262 test.noteOff (3, 61, MPEValue::from7BitInt (100));
1263 test.pressure (3, MPEValue::from7BitInt (77));
1264 expectEquals (test.getNumPlayingNotes(), 1);
1265 expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown);
1266 expectEquals (test.notePressureChangedCallCounter, 1);
1267 }
1268 {
1269 UnitTestInstrument test;
1270 test.setZoneLayout (testLayout);
1271
1272 // if no pressure is sent before note-on, default = 0 should be used
1273 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1274 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1275 }
1276 {
1277 UnitTestInstrument test;
1278 test.setZoneLayout (testLayout);
1279
1280 // if pressure is sent before note-on, use that
1281 test.pressure (3, MPEValue::from7BitInt (77));
1282 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1283 expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown);
1284 }
1285 {
1286 UnitTestInstrument test;
1287 test.setZoneLayout (testLayout);
1288
1289 // if pressure is sent before note-on, but it belonged to another note
1290 // on the same channel that has since been turned off, use default = 0
1291 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1292 test.pressure (3, MPEValue::from7BitInt (77));
1293 test.noteOff (3, 61, MPEValue::from7BitInt (100));
1294 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1295 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1296 }
1297 {
1298 UnitTestInstrument test;
1299 test.setZoneLayout (testLayout);
1300
1301 // edge case: two notes on the same channel simultaneously. the second one should use
1302 // pressure = 0 initially but then react to additional pressure messages
1303 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1304 test.pressure (3, MPEValue::from7BitInt (77));
1305 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1306 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1307 test.pressure (3, MPEValue::from7BitInt (78));
1308 expectNote (test.getNote (3, 60), 100, 78, 8192, 64, MPENote::keyDown);
1309 expectNote (test.getNote (3, 61), 100, 77, 8192, 64, MPENote::keyDown);
1310 }
1311
1312 {
1313 UnitTestInstrument test;
1314 test.setZoneLayout (testLayout);
1315
1316 // master channel will use poly-aftertouch for pressure
1317 test.noteOn (16, 60, MPEValue::from7BitInt (100));
1318 expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown);
1319 test.aftertouch (16, 60, MPEValue::from7BitInt (27));
1320 expectNote (test.getNote (16, 60), 100, 27, 8192, 64, MPENote::keyDown);
1321
1322 // member channels will not respond to poly-aftertouch
1323 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1324 test.aftertouch (3, 60, MPEValue::from7BitInt (50));
1325 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1326 }
1327 }
1328
1329 beginTest ("pitchbend");
1330 {
1331 {
1332 UnitTestInstrument test;
1333 test.setZoneLayout (testLayout);
1334
1335 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1336 test.noteOn (4, 60, MPEValue::from7BitInt (100));
1337 test.noteOn (10, 60, MPEValue::from7BitInt (100));
1338
1339 // applying pitchbend on a per-note channel should modulate one note
1340 test.pitchbend (3, MPEValue::from14BitInt (1111));
1341 expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
1342 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
1343 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1344 expectEquals (test.notePitchbendChangedCallCounter, 1);
1345
1346 // applying pitchbend on a master channel should be ignored for the
1347 // value of per-note pitchbend. Tests covering master pitchbend below.
1348 // Note: noteChanged will be called anyway for notes in that zone
1349 // because the total pitchbend for those notes has changed
1350 test.pitchbend (1, MPEValue::from14BitInt (2222));
1351 expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
1352 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
1353 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1354 expectEquals (test.notePitchbendChangedCallCounter, 3);
1355
1356 // applying pitchbend on an unrelated channel should do nothing.
1357 test.pitchbend (8, MPEValue::from14BitInt (3333));
1358 expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
1359 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
1360 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1361 expectEquals (test.notePitchbendChangedCallCounter, 3);
1362 }
1363 {
1364 UnitTestInstrument test;
1365 test.setZoneLayout (testLayout);
1366
1367 // two notes on same channel - only last added should be bent
1368 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1369 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1370 test.pitchbend (3, MPEValue::from14BitInt (4444));
1371 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1372 expectNote (test.getNote (3, 61), 100, 0, 4444, 64, MPENote::keyDown);
1373 expectEquals (test.notePitchbendChangedCallCounter, 1);
1374 }
1375 {
1376 UnitTestInstrument test;
1377 test.setZoneLayout (testLayout);
1378
1379 // edge case: two notes on same channel, one gets released,
1380 // then the other should be bent
1381 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1382 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1383 test.noteOff (3, 61, MPEValue::from7BitInt (100));
1384 test.pitchbend (3, MPEValue::from14BitInt (5555));
1385 expectEquals (test.getNumPlayingNotes(), 1);
1386 expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown);
1387 expectEquals (test.notePitchbendChangedCallCounter, 1);
1388 }
1389 {
1390 UnitTestInstrument test;
1391 test.setZoneLayout (testLayout);
1392
1393 // Richard's edge case:
1394 // - press one note
1395 // - press sustain (careful: must be sent on master channel)
1396 // - release first note (is still sustained!)
1397 // - press another note (happens to be on the same MIDI channel!)
1398 // - pitchbend that other note
1399 // - the first note should not be bent, only the second one.
1400
1401 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1402 test.sustainPedal (1, true);
1403 test.noteOff (3, 60, MPEValue::from7BitInt (64));
1404 expectEquals (test.getNumPlayingNotes(), 1);
1405 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
1406 expectEquals (test.noteKeyStateChangedCallCounter, 2);
1407
1408 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1409 test.pitchbend (3, MPEValue::from14BitInt (6666));
1410 expectEquals (test.getNumPlayingNotes(), 2);
1411 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
1412 expectNote (test.getNote (3, 61), 100, 0, 6666, 64, MPENote::keyDownAndSustained);
1413 expectEquals (test.notePitchbendChangedCallCounter, 1);
1414 }
1415 {
1416 UnitTestInstrument test;
1417 test.setZoneLayout (testLayout);
1418
1419 // Zsolt's edge case:
1420 // - press one note
1421 // - modulate pitchbend or timbre
1422 // - release the note
1423 // - press same note again without sending a pitchbend or timbre message before the note-on
1424 // - the note should be turned on with a default value for pitchbend/timbre,
1425 // and *not* the last value received on channel.
1426
1427 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1428 test.pitchbend (3, MPEValue::from14BitInt (5555));
1429 expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown);
1430
1431 test.noteOff (3, 60, MPEValue::from7BitInt (100));
1432 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1433 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1434 }
1435 {
1436 // applying per-note pitchbend should set the note's totalPitchbendInSemitones
1437 // correctly depending on the per-note pitchbend range of the zone.
1438 UnitTestInstrument test;
1439
1440 MPEZoneLayout layout = testLayout;
1441 test.setZoneLayout (layout); // default should be +/- 48 semitones
1442 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1443 test.pitchbend (3, MPEValue::from14BitInt (4096));
1444 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01);
1445
1446 layout.setLowerZone (5, 96);
1447 test.setZoneLayout (layout);
1448 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1449 test.pitchbend (3, MPEValue::from14BitInt (0)); // -max
1450 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1451
1452 layout.setLowerZone (5, 1);
1453 test.setZoneLayout (layout);
1454 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1455 test.pitchbend (3, MPEValue::from14BitInt (16383)); // +max
1456 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1457
1458 layout.setLowerZone (5, 0); // pitchbendrange = 0 --> no pitchbend at all
1459 test.setZoneLayout (layout);
1460 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1461 test.pitchbend (3, MPEValue::from14BitInt (12345));
1462 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1463 }
1464 {
1465 // applying master pitchbend should set the note's totalPitchbendInSemitones
1466 // correctly depending on the master pitchbend range of the zone.
1467 UnitTestInstrument test;
1468
1469 MPEZoneLayout layout = testLayout;
1470 test.setZoneLayout (layout); // default should be +/- 2 semitones
1471 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1472 test.pitchbend (1, MPEValue::from14BitInt (4096)); //halfway between -max and centre
1473 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01);
1474
1475 layout.setLowerZone (5, 48, 96);
1476 test.setZoneLayout (layout);
1477 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1478 test.pitchbend (1, MPEValue::from14BitInt (0)); // -max
1479 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
1480
1481 layout.setLowerZone (5, 48, 1);
1482 test.setZoneLayout (layout);
1483 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1484 test.pitchbend (1, MPEValue::from14BitInt (16383)); // +max
1485 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
1486
1487 layout.setLowerZone (5, 48, 0); // pitchbendrange = 0 --> no pitchbend at all
1488 test.setZoneLayout (layout);
1489 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1490 test.pitchbend (1, MPEValue::from14BitInt (12345));
1491 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
1492 }
1493 {
1494 // applying both per-note and master pitchbend simultaneously should set
1495 // the note's totalPitchbendInSemitones to the sum of both, correctly
1496 // weighted with the per-note and master pitchbend range, respectively.
1497 UnitTestInstrument test;
1498
1499 MPEZoneLayout layout = testLayout;
1500 layout.setLowerZone (5, 12, 1);
1501 test.setZoneLayout (layout);
1502
1503 test.pitchbend (1, MPEValue::from14BitInt (4096)); // master pitchbend 0.5 semitones down
1504 test.pitchbend (3, MPEValue::from14BitInt (0)); // per-note pitchbend 12 semitones down
1505 // additionally, note should react to both pitchbend messages
1506 // correctly even if they arrived before the note-on.
1507 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1508 expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01);
1509 }
1510 }
1511
1512 beginTest ("timbre");
1513 {
1514 {
1515 UnitTestInstrument test;
1516 test.setZoneLayout (testLayout);
1517
1518 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1519 test.noteOn (4, 60, MPEValue::from7BitInt (100));
1520 test.noteOn (10, 60, MPEValue::from7BitInt (100));
1521
1522 // modulating timbre on a per-note channel should modulate one note
1523 test.timbre (3, MPEValue::from7BitInt (33));
1524 expectNote (test.getNote (3, 60), 100, 0, 8192, 33, MPENote::keyDown);
1525 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
1526 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1527 expectEquals (test.noteTimbreChangedCallCounter, 1);
1528
1529 // modulating timbre on a master channel should modulate all notes in this zone
1530 test.timbre (1, MPEValue::from7BitInt (44));
1531 expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown);
1532 expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown);
1533 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1534 expectEquals (test.noteTimbreChangedCallCounter, 3);
1535
1536 // modulating timbre on an unrelated channel should be ignored
1537 test.timbre (9, MPEValue::from7BitInt (55));
1538 expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown);
1539 expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown);
1540 expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
1541 expectEquals (test.noteTimbreChangedCallCounter, 3);
1542 }
1543 {
1544 UnitTestInstrument test;
1545 test.setZoneLayout (testLayout);
1546
1547 // two notes on same channel - only last added should be modulated
1548 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1549 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1550 test.timbre (3, MPEValue::from7BitInt (66));
1551 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1552 expectNote (test.getNote (3, 61), 100, 0, 8192, 66, MPENote::keyDown);
1553 expectEquals (test.noteTimbreChangedCallCounter, 1);
1554 }
1555 {
1556 UnitTestInstrument test;
1557 test.setZoneLayout (testLayout);
1558
1559 // edge case: two notes on same channel, one gets released,
1560 // then the other should be modulated
1561 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1562 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1563 test.noteOff (3, 61, MPEValue::from7BitInt (100));
1564 test.timbre (3, MPEValue::from7BitInt (77));
1565 expectEquals (test.getNumPlayingNotes(), 1);
1566 expectNote (test.getNote (3, 60), 100, 0, 8192, 77, MPENote::keyDown);
1567 expectEquals (test.noteTimbreChangedCallCounter, 1);
1568 }
1569 {
1570 UnitTestInstrument test;
1571 test.setZoneLayout (testLayout);
1572
1573 // Zsolt's edge case for timbre
1574 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1575 test.timbre (3, MPEValue::from7BitInt (42));
1576 expectNote (test.getNote (3, 60), 100, 0, 8192, 42, MPENote::keyDown);
1577
1578 test.noteOff (3, 60, MPEValue::from7BitInt (100));
1579 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1580 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1581 }
1582 }
1583
1584 beginTest ("setPressureTrackingMode");
1585 {
1586 {
1587 // last note played (= default)
1588 UnitTestInstrument test;
1589 test.setZoneLayout (testLayout);
1590
1591 test.setPressureTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
1592 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1593 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1594 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1595 test.pressure (3, MPEValue::from7BitInt (99));
1596 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1597 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1598 expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown);
1599 expectEquals (test.notePressureChangedCallCounter, 1);
1600 }
1601 {
1602 // lowest note
1603 UnitTestInstrument test;
1604 test.setZoneLayout (testLayout);
1605
1606 test.setPressureTrackingMode (MPEInstrument::lowestNoteOnChannel);
1607 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1608 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1609 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1610 test.pressure (3, MPEValue::from7BitInt (99));
1611 expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown);
1612 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1613 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1614 expectEquals (test.notePressureChangedCallCounter, 1);
1615 }
1616 {
1617 // highest note
1618 UnitTestInstrument test;
1619 test.setZoneLayout (testLayout);
1620
1621 test.setPressureTrackingMode (MPEInstrument::highestNoteOnChannel);
1622 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1623 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1624 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1625 test.pressure (3, MPEValue::from7BitInt (99));
1626 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1627 expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown);
1628 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1629 expectEquals (test.notePressureChangedCallCounter, 1);
1630 }
1631 {
1632 // all notes
1633 UnitTestInstrument test;
1634 test.setZoneLayout (testLayout);
1635
1636 test.setPressureTrackingMode (MPEInstrument::allNotesOnChannel);
1637 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1638 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1639 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1640 test.pressure (3, MPEValue::from7BitInt (99));
1641 expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown);
1642 expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown);
1643 expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown);
1644 expectEquals (test.notePressureChangedCallCounter, 3);
1645 }
1646 }
1647
1648 beginTest ("setPitchbendTrackingMode");
1649 {
1650 {
1651 // last note played (= default)
1652 UnitTestInstrument test;
1653 test.setZoneLayout (testLayout);
1654
1655 test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
1656 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1657 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1658 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1659 test.pitchbend (3, MPEValue::from14BitInt (9999));
1660 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1661 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1662 expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown);
1663 expectEquals (test.notePitchbendChangedCallCounter, 1);
1664 }
1665 {
1666 // lowest note
1667 UnitTestInstrument test;
1668 test.setZoneLayout (testLayout);
1669
1670 test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
1671 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1672 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1673 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1674 test.pitchbend (3, MPEValue::from14BitInt (9999));
1675 expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown);
1676 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1677 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1678 expectEquals (test.notePitchbendChangedCallCounter, 1);
1679 }
1680 {
1681 // highest note
1682 UnitTestInstrument test;
1683 test.setZoneLayout (testLayout);
1684
1685 test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
1686 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1687 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1688 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1689 test.pitchbend (3, MPEValue::from14BitInt (9999));
1690 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1691 expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown);
1692 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1693 expectEquals (test.notePitchbendChangedCallCounter, 1);
1694 }
1695 {
1696 // all notes
1697 UnitTestInstrument test;
1698 test.setZoneLayout (testLayout);
1699
1700 test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
1701 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1702 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1703 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1704 test.pitchbend (3, MPEValue::from14BitInt (9999));
1705 expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown);
1706 expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown);
1707 expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown);
1708 expectEquals (test.notePitchbendChangedCallCounter, 3);
1709 }
1710 }
1711
1712 beginTest ("setTimbreTrackingMode");
1713 {
1714 {
1715 // last note played (= default)
1716 UnitTestInstrument test;
1717 test.setZoneLayout (testLayout);
1718
1719 test.setTimbreTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
1720 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1721 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1722 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1723 test.timbre (3, MPEValue::from7BitInt (99));
1724 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1725 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1726 expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown);
1727 expectEquals (test.noteTimbreChangedCallCounter, 1);
1728 }
1729 {
1730 // lowest note
1731 UnitTestInstrument test;
1732 test.setZoneLayout (testLayout);
1733
1734 test.setTimbreTrackingMode (MPEInstrument::lowestNoteOnChannel);
1735 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1736 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1737 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1738 test.timbre (3, MPEValue::from7BitInt (99));
1739 expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown);
1740 expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
1741 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1742 expectEquals (test.noteTimbreChangedCallCounter, 1);
1743 }
1744 {
1745 // highest note
1746 UnitTestInstrument test;
1747 test.setZoneLayout (testLayout);
1748
1749 test.setTimbreTrackingMode (MPEInstrument::highestNoteOnChannel);
1750 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1751 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1752 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1753 test.timbre (3, MPEValue::from7BitInt (99));
1754 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
1755 expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown);
1756 expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
1757 expectEquals (test.noteTimbreChangedCallCounter, 1);
1758 }
1759 {
1760 // all notes
1761 UnitTestInstrument test;
1762 test.setZoneLayout (testLayout);
1763
1764 test.setTimbreTrackingMode (MPEInstrument::allNotesOnChannel);
1765 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1766 test.noteOn (3, 62, MPEValue::from7BitInt (100));
1767 test.noteOn (3, 61, MPEValue::from7BitInt (100));
1768 test.timbre (3, MPEValue::from7BitInt (99));
1769 expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown);
1770 expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown);
1771 expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown);
1772 expectEquals (test.noteTimbreChangedCallCounter, 3);
1773 }
1774 }
1775
1776 beginTest ("processNextMidiEvent");
1777 {
1778 UnitTestInstrument test;
1779
1780 // note on should trigger noteOn method call
1781
1782 test.processNextMidiEvent (MidiMessage::noteOn (3, 42, uint8 (92)));
1783 expectEquals (test.noteOnCallCounter, 1);
1784 expectEquals (test.lastMidiChannelReceived, 3);
1785 expectEquals (test.lastMidiNoteNumberReceived, 42);
1786 expectEquals (test.lastMPEValueReceived.as7BitInt(), 92);
1787
1788 // note off should trigger noteOff method call
1789
1790 test.processNextMidiEvent (MidiMessage::noteOff (4, 12, uint8 (33)));
1791 expectEquals (test.noteOffCallCounter, 1);
1792 expectEquals (test.lastMidiChannelReceived, 4);
1793 expectEquals (test.lastMidiNoteNumberReceived, 12);
1794 expectEquals (test.lastMPEValueReceived.as7BitInt(), 33);
1795
1796 // note on with velocity = 0 should trigger noteOff method call
1797 // with a note off velocity of 64 (centre value)
1798
1799 test.processNextMidiEvent (MidiMessage::noteOn (5, 11, uint8 (0)));
1800 expectEquals (test.noteOffCallCounter, 2);
1801 expectEquals (test.lastMidiChannelReceived, 5);
1802 expectEquals (test.lastMidiNoteNumberReceived, 11);
1803 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1804
1805 // pitchwheel message should trigger pitchbend method call
1806
1807 test.processNextMidiEvent (MidiMessage::pitchWheel (1, 3333));
1808 expectEquals (test.pitchbendCallCounter, 1);
1809 expectEquals (test.lastMidiChannelReceived, 1);
1810 expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333);
1811
1812 // pressure using channel pressure message (7-bit value) should
1813 // trigger pressure method call
1814
1815 test.processNextMidiEvent (MidiMessage::channelPressureChange (10, 35));
1816 expectEquals (test.pressureCallCounter, 1);
1817 expectEquals (test.lastMidiChannelReceived, 10);
1818 expectEquals (test.lastMPEValueReceived.as7BitInt(), 35);
1819
1820 // pressure using 14-bit value over CC70 and CC102 should trigger
1821 // pressure method call after the MSB is sent
1822
1823 // a) sending only the MSB
1824 test.processNextMidiEvent (MidiMessage::controllerEvent (3, 70, 120));
1825 expectEquals (test.pressureCallCounter, 2);
1826 expectEquals (test.lastMidiChannelReceived, 3);
1827 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1828
1829 // b) sending LSB and MSB (only the MSB should trigger the call) - per MIDI channel!
1830 test.processNextMidiEvent (MidiMessage::controllerEvent (4, 102, 121));
1831 expectEquals (test.pressureCallCounter, 2);
1832 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 102, 122));
1833 expectEquals (test.pressureCallCounter, 2);
1834 test.processNextMidiEvent (MidiMessage::controllerEvent (4, 70, 123));
1835 expectEquals (test.pressureCallCounter, 3);
1836 expectEquals (test.lastMidiChannelReceived, 4);
1837 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1838 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 124));
1839 expectEquals (test.pressureCallCounter, 4);
1840 expectEquals (test.lastMidiChannelReceived, 5);
1841 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1842 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 64));
1843 expectEquals (test.pressureCallCounter, 5);
1844 expectEquals (test.lastMidiChannelReceived, 5);
1845 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1846
1847 // same for timbre 14-bit value over CC74 and CC106
1848 test.processNextMidiEvent (MidiMessage::controllerEvent (3, 74, 120));
1849 expectEquals (test.timbreCallCounter, 1);
1850 expectEquals (test.lastMidiChannelReceived, 3);
1851 expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
1852 test.processNextMidiEvent (MidiMessage::controllerEvent (4, 106, 121));
1853 expectEquals (test.timbreCallCounter, 1);
1854 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 106, 122));
1855 expectEquals (test.timbreCallCounter, 1);
1856 test.processNextMidiEvent (MidiMessage::controllerEvent (4, 74, 123));
1857 expectEquals (test.timbreCallCounter, 2);
1858 expectEquals (test.lastMidiChannelReceived, 4);
1859 expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
1860 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 124));
1861 expectEquals (test.timbreCallCounter, 3);
1862 expectEquals (test.lastMidiChannelReceived, 5);
1863 expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
1864 test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 64));
1865 expectEquals (test.timbreCallCounter, 4);
1866 expectEquals (test.lastMidiChannelReceived, 5);
1867 expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
1868
1869 // sustain pedal message (CC64) should trigger sustainPedal method call
1870 test.processNextMidiEvent (MidiMessage::controllerEvent (1, 64, 127));
1871 expectEquals (test.sustainPedalCallCounter, 1);
1872 expectEquals (test.lastMidiChannelReceived, 1);
1873 expect (test.lastSustainPedalValueReceived);
1874 test.processNextMidiEvent (MidiMessage::controllerEvent (16, 64, 0));
1875 expectEquals (test.sustainPedalCallCounter, 2);
1876 expectEquals (test.lastMidiChannelReceived, 16);
1877 expect (! test.lastSustainPedalValueReceived);
1878
1879 // sostenuto pedal message (CC66) should trigger sostenutoPedal method call
1880 test.processNextMidiEvent (MidiMessage::controllerEvent (1, 66, 127));
1881 expectEquals (test.sostenutoPedalCallCounter, 1);
1882 expectEquals (test.lastMidiChannelReceived, 1);
1883 expect (test.lastSostenutoPedalValueReceived);
1884 test.processNextMidiEvent (MidiMessage::controllerEvent (16, 66, 0));
1885 expectEquals (test.sostenutoPedalCallCounter, 2);
1886 expectEquals (test.lastMidiChannelReceived, 16);
1887 expect (! test.lastSostenutoPedalValueReceived);
1888 }
1889 {
1890 // MIDI messages modifying the zone layout should be correctly
1891 // forwarded to the internal zone layout and modify it.
1892 // (testing the actual logic of the zone layout is done in the
1893 // MPEZoneLayout unit tests)
1894 MPEInstrument test;
1895
1896 MidiBuffer buffer;
1897 buffer.addEvents (MPEMessages::setLowerZone (5), 0, -1, 0);
1898 buffer.addEvents (MPEMessages::setUpperZone (6), 0, -1, 0);
1899
1900 for (const auto metadata : buffer)
1901 test.processNextMidiEvent (metadata.getMessage());
1902
1903 expect (test.getZoneLayout().getLowerZone().isActive());
1904 expect (test.getZoneLayout().getUpperZone().isActive());
1905 expectEquals (test.getZoneLayout().getLowerZone().getMasterChannel(), 1);
1906 expectEquals (test.getZoneLayout().getLowerZone().numMemberChannels, 5);
1907 expectEquals (test.getZoneLayout().getUpperZone().getMasterChannel(), 16);
1908 expectEquals (test.getZoneLayout().getUpperZone().numMemberChannels, 6);
1909 }
1910
1911 beginTest ("MIDI all notes off");
1912 {
1913 UnitTestInstrument test;
1914 test.setZoneLayout (testLayout);
1915 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1916 test.noteOn (4, 61, MPEValue::from7BitInt (100));
1917 test.noteOn (15, 62, MPEValue::from7BitInt (100));
1918 test.noteOn (15, 63, MPEValue::from7BitInt (100));
1919 expectEquals (test.getNumPlayingNotes(), 4);
1920
1921 // on note channel: ignore.
1922 test.processNextMidiEvent (MidiMessage::allControllersOff (3));
1923 expectEquals (test.getNumPlayingNotes(), 4);
1924
1925 // on unused channel: ignore.
1926 test.processNextMidiEvent (MidiMessage::allControllersOff (9));
1927 expectEquals (test.getNumPlayingNotes(), 4);
1928
1929 // on master channel: release notes in that zone only.
1930 test.processNextMidiEvent (MidiMessage::allControllersOff (1));
1931 expectEquals (test.getNumPlayingNotes(), 2);
1932 test.processNextMidiEvent (MidiMessage::allControllersOff (16));
1933 expectEquals (test.getNumPlayingNotes(), 0);
1934 }
1935
1936 beginTest ("MIDI all notes off (legacy mode)");
1937 {
1938 UnitTestInstrument test;
1939 test.enableLegacyMode();
1940 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1941 test.noteOn (4, 61, MPEValue::from7BitInt (100));
1942 test.noteOn (15, 62, MPEValue::from7BitInt (100));
1943 test.noteOn (15, 63, MPEValue::from7BitInt (100));
1944 expectEquals (test.getNumPlayingNotes(), 4);
1945
1946 test.processNextMidiEvent (MidiMessage::allControllersOff (3));
1947 expectEquals (test.getNumPlayingNotes(), 3);
1948
1949 test.processNextMidiEvent (MidiMessage::allControllersOff (15));
1950 expectEquals (test.getNumPlayingNotes(), 1);
1951
1952 test.processNextMidiEvent (MidiMessage::allControllersOff (4));
1953 expectEquals (test.getNumPlayingNotes(), 0);
1954 }
1955
1956 beginTest ("default initial values for pitchbend and timbre");
1957 {
1958 MPEInstrument test;
1959 test.setZoneLayout (testLayout);
1960
1961 test.pitchbend (3, MPEValue::from14BitInt (3333)); // use for next note-on on ch. 3
1962 test.pitchbend (2, MPEValue::from14BitInt (4444)); // ignore
1963 test.pitchbend (2, MPEValue::from14BitInt (5555)); // ignore
1964
1965 test.timbre (3, MPEValue::from7BitInt (66)); // use for next note-on on ch. 3
1966 test.timbre (2, MPEValue::from7BitInt (77)); // ignore
1967 test.timbre (2, MPEValue::from7BitInt (88)); // ignore
1968
1969 test.noteOn (3, 60, MPEValue::from7BitInt (100));
1970
1971 expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66, MPENote::keyDown);
1972 }
1973
1974 beginTest ("Legacy mode");
1975 {
1976 {
1977 // basic check
1978 MPEInstrument test;
1979 expect (! test.isLegacyModeEnabled());
1980
1981 test.setZoneLayout (testLayout);
1982 expect (! test.isLegacyModeEnabled());
1983
1984 test.enableLegacyMode();
1985 expect (test.isLegacyModeEnabled());
1986
1987 test.setZoneLayout (testLayout);
1988 expect (! test.isLegacyModeEnabled());
1989 }
1990 {
1991 // constructor w/o default arguments
1992 MPEInstrument test;
1993 test.enableLegacyMode (0, Range<int> (1, 11));
1994 expectEquals (test.getLegacyModePitchbendRange(), 0);
1995 expect (test.getLegacyModeChannelRange() == Range<int> (1, 11));
1996 }
1997 {
1998 // getters and setters
1999 MPEInstrument test;
2000 test.enableLegacyMode();
2001
2002 expectEquals (test.getLegacyModePitchbendRange(), 2);
2003 expect (test.getLegacyModeChannelRange() == Range<int> (1, 17));
2004
2005 test.setLegacyModePitchbendRange (96);
2006 expectEquals (test.getLegacyModePitchbendRange(), 96);
2007
2008 test.setLegacyModeChannelRange (Range<int> (10, 12));
2009 expect (test.getLegacyModeChannelRange() == Range<int> (10, 12));
2010 }
2011 {
2012 // note on should trigger notes on all 16 channels
2013
2014 UnitTestInstrument test;
2015 test.enableLegacyMode();
2016
2017 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2018 test.noteOn (2, 60, MPEValue::from7BitInt (100));
2019 test.noteOn (15, 60, MPEValue::from7BitInt (100));
2020 test.noteOn (16, 60, MPEValue::from7BitInt (100));
2021 expectEquals (test.getNumPlayingNotes(), 4);
2022
2023 // polyphonic modulation should work across all 16 channels
2024
2025 test.pitchbend (1, MPEValue::from14BitInt (9999));
2026 test.pressure (2, MPEValue::from7BitInt (88));
2027 test.timbre (15, MPEValue::from7BitInt (77));
2028
2029 expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
2030 expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown);
2031 expectNote (test.getNote (15, 60), 100, 0, 8192, 77, MPENote::keyDown);
2032 expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown);
2033
2034 // note off should work in legacy mode
2035
2036 test.noteOff (15, 60, MPEValue::from7BitInt (0));
2037 test.noteOff (1, 60, MPEValue::from7BitInt (0));
2038 test.noteOff (2, 60, MPEValue::from7BitInt (0));
2039 test.noteOff (16, 60, MPEValue::from7BitInt (0));
2040 expectEquals (test.getNumPlayingNotes(), 0);
2041 }
2042 {
2043 // legacy mode w/ custom channel range: note on should trigger notes only within range
2044
2045 UnitTestInstrument test;
2046 test.enableLegacyMode (2, Range<int> (3, 8)); // channels 3-7
2047
2048 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2049 test.noteOn (2, 60, MPEValue::from7BitInt (100));
2050 test.noteOn (3, 60, MPEValue::from7BitInt (100)); // should trigger
2051 test.noteOn (4, 60, MPEValue::from7BitInt (100)); // should trigger
2052 test.noteOn (6, 60, MPEValue::from7BitInt (100)); // should trigger
2053 test.noteOn (7, 60, MPEValue::from7BitInt (100)); // should trigger
2054 test.noteOn (8, 60, MPEValue::from7BitInt (100));
2055 test.noteOn (16, 60, MPEValue::from7BitInt (100));
2056
2057 expectEquals (test.getNumPlayingNotes(), 4);
2058 expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
2059 expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
2060 expectNote (test.getNote (6, 60), 100, 0, 8192, 64, MPENote::keyDown);
2061 expectNote (test.getNote (7, 60), 100, 0, 8192, 64, MPENote::keyDown);
2062 }
2063 {
2064 // tracking mode in legacy mode
2065 {
2066 UnitTestInstrument test;
2067 test.enableLegacyMode();
2068
2069 test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
2070 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2071 test.noteOn (1, 62, MPEValue::from7BitInt (100));
2072 test.noteOn (1, 61, MPEValue::from7BitInt (100));
2073 test.pitchbend (1, MPEValue::from14BitInt (9999));
2074 expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown);
2075 expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown);
2076 expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown);
2077 }
2078 {
2079 UnitTestInstrument test;
2080 test.enableLegacyMode();
2081
2082 test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
2083 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2084 test.noteOn (1, 62, MPEValue::from7BitInt (100));
2085 test.noteOn (1, 61, MPEValue::from7BitInt (100));
2086 test.pitchbend (1, MPEValue::from14BitInt (9999));
2087 expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
2088 expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown);
2089 expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown);
2090 }
2091 {
2092 UnitTestInstrument test;
2093 test.enableLegacyMode();
2094
2095 test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
2096 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2097 test.noteOn (1, 62, MPEValue::from7BitInt (100));
2098 test.noteOn (1, 61, MPEValue::from7BitInt (100));
2099 test.pitchbend (1, MPEValue::from14BitInt (9999));
2100 expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown);
2101 expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown);
2102 expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown);
2103 }
2104 {
2105 UnitTestInstrument test;
2106 test.enableLegacyMode();
2107
2108 test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
2109 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2110 test.noteOn (1, 62, MPEValue::from7BitInt (100));
2111 test.noteOn (1, 61, MPEValue::from7BitInt (100));
2112 test.pitchbend (1, MPEValue::from14BitInt (9999));
2113 expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
2114 expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown);
2115 expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown);
2116 }
2117 }
2118 {
2119 // custom pitchbend range in legacy mode.
2120 UnitTestInstrument test;
2121 test.enableLegacyMode (11);
2122
2123 test.pitchbend (1, MPEValue::from14BitInt (4096));
2124 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2125 expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
2126 }
2127 {
2128 // sustain pedal should be per channel in legacy mode.
2129 UnitTestInstrument test;
2130 test.enableLegacyMode();
2131
2132 test.sustainPedal (1, true);
2133 test.noteOn (2, 61, MPEValue::from7BitInt (100));
2134 test.noteOff (2, 61, MPEValue::from7BitInt (100));
2135 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2136 test.noteOff (1, 60, MPEValue::from7BitInt (100));
2137
2138 expectEquals (test.getNumPlayingNotes(), 1);
2139 expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained);
2140
2141 test.sustainPedal (1, false);
2142 expectEquals (test.getNumPlayingNotes(), 0);
2143
2144 test.noteOn (2, 61, MPEValue::from7BitInt (100));
2145 test.sustainPedal (1, true);
2146 test.noteOff (2, 61, MPEValue::from7BitInt (100));
2147 expectEquals (test.getNumPlayingNotes(), 0);
2148
2149 }
2150 {
2151 // sostenuto pedal should be per channel in legacy mode.
2152 UnitTestInstrument test;
2153 test.enableLegacyMode();
2154
2155 test.noteOn (1, 60, MPEValue::from7BitInt (100));
2156 test.sostenutoPedal (1, true);
2157 test.noteOff (1, 60, MPEValue::from7BitInt (100));
2158 test.noteOn (2, 61, MPEValue::from7BitInt (100));
2159 test.noteOff (2, 61, MPEValue::from7BitInt (100));
2160
2161 expectEquals (test.getNumPlayingNotes(), 1);
2162 expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained);
2163
2164 test.sostenutoPedal (1, false);
2165 expectEquals (test.getNumPlayingNotes(), 0);
2166
2167 test.noteOn (2, 61, MPEValue::from7BitInt (100));
2168 test.sostenutoPedal (1, true);
2169 test.noteOff (2, 61, MPEValue::from7BitInt (100));
2170 expectEquals (test.getNumPlayingNotes(), 0);
2171 }
2172 {
2173 // all notes released when switching layout
2174 UnitTestInstrument test;
2175 test.setZoneLayout (testLayout);
2176 test.noteOn (3, 60, MPEValue::from7BitInt (100));
2177 expectEquals (test.getNumPlayingNotes(), 1);
2178
2179 test.enableLegacyMode();
2180 expectEquals (test.getNumPlayingNotes(), 0);
2181 test.noteOn (3, 60, MPEValue::from7BitInt (100));
2182 expectEquals (test.getNumPlayingNotes(), 1);
2183
2184 test.setZoneLayout (testLayout);
2185 expectEquals (test.getNumPlayingNotes(), 0);
2186 }
2187 }
2188 }
2190
2191private:
2192 //==============================================================================
2193 /* This mock class is used for unit testing whether the methods of
2194 MPEInstrument are called correctly.
2195 */
2196 class UnitTestInstrument : public MPEInstrument,
2197 private MPEInstrument::Listener
2198 {
2199 using Base = MPEInstrument;
2200
2201 public:
2202 UnitTestInstrument()
2203 : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0),
2204 pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0),
2205 sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0),
2206 notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0),
2207 noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0),
2208 lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1),
2209 lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false)
2210 {
2211 addListener (this);
2212 }
2213
2214 void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity) override
2215 {
2216 Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity);
2217
2218 noteOnCallCounter++;
2219 lastMidiChannelReceived = midiChannel;
2220 lastMidiNoteNumberReceived = midiNoteNumber;
2221 lastMPEValueReceived = midiNoteOnVelocity;
2222 }
2223
2224 void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) override
2225 {
2226 Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity);
2227
2228 noteOffCallCounter++;
2229 lastMidiChannelReceived = midiChannel;
2230 lastMidiNoteNumberReceived = midiNoteNumber;
2231 lastMPEValueReceived = midiNoteOffVelocity;
2232 }
2233
2234 void pitchbend (int midiChannel, MPEValue value) override
2235 {
2236 Base::pitchbend (midiChannel, value);
2237
2238 pitchbendCallCounter++;
2239 lastMidiChannelReceived = midiChannel;
2240 lastMPEValueReceived = value;
2241 }
2242
2243 void pressure (int midiChannel, MPEValue value) override
2244 {
2245 Base::pressure (midiChannel, value);
2246
2247 pressureCallCounter++;
2248 lastMidiChannelReceived = midiChannel;
2249 lastMPEValueReceived = value;
2250 }
2251
2252 void timbre (int midiChannel, MPEValue value) override
2253 {
2254 Base::timbre (midiChannel, value);
2255
2256 timbreCallCounter++;
2257 lastMidiChannelReceived = midiChannel;
2258 lastMPEValueReceived = value;
2259 }
2260
2261 void sustainPedal (int midiChannel, bool value) override
2262 {
2263 Base::sustainPedal (midiChannel, value);
2264
2265 sustainPedalCallCounter++;
2266 lastMidiChannelReceived = midiChannel;
2267 lastSustainPedalValueReceived = value;
2268 }
2269
2270 void sostenutoPedal (int midiChannel, bool value) override
2271 {
2272 Base::sostenutoPedal (midiChannel, value);
2273
2274 sostenutoPedalCallCounter++;
2275 lastMidiChannelReceived = midiChannel;
2276 lastSostenutoPedalValueReceived = value;
2277 }
2278
2279 void aftertouch (int midiChannel, int midiNoteNumber, MPEValue value)
2280 {
2281 const auto message = juce::MidiMessage::aftertouchChange (midiChannel, midiNoteNumber, value.as7BitInt());
2282 processNextMidiEvent (message);
2283 }
2284
2285 int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter,
2286 pressureCallCounter, timbreCallCounter, sustainPedalCallCounter,
2287 sostenutoPedalCallCounter, noteAddedCallCounter,
2288 notePressureChangedCallCounter, notePitchbendChangedCallCounter,
2289 noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter,
2290 noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived;
2291
2292 bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived;
2293 MPEValue lastMPEValueReceived;
2294 std::unique_ptr<MPENote> lastNoteFinished;
2295
2296 private:
2297 //==============================================================================
2298 void noteAdded (MPENote) override { noteAddedCallCounter++; }
2299
2300 void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; }
2301 void notePitchbendChanged (MPENote) override { notePitchbendChangedCallCounter++; }
2302 void noteTimbreChanged (MPENote) override { noteTimbreChangedCallCounter++; }
2303 void noteKeyStateChanged (MPENote) override { noteKeyStateChangedCallCounter++; }
2304
2305 void noteReleased (MPENote finishedNote) override
2306 {
2307 noteReleasedCallCounter++;
2308 lastNoteFinished.reset (new MPENote (finishedNote));
2309 }
2310 };
2311
2312 //==============================================================================
2313 void expectNote (MPENote noteToTest,
2314 int noteOnVelocity7Bit,
2315 int pressure7Bit,
2316 int pitchbend14Bit,
2317 int timbre7Bit,
2318 MPENote::KeyState keyState)
2319 {
2320 expect (noteToTest.isValid());
2321 expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit);
2322 expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit);
2323 expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit);
2324 expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit);
2325 expect (noteToTest.keyState == keyState);
2326 }
2327
2328 void expectHasFinishedNote (const UnitTestInstrument& test,
2329 int channel, int noteNumber, int noteOffVelocity7Bit)
2330 {
2331 expect (test.lastNoteFinished != nullptr);
2332 expectEquals (int (test.lastNoteFinished->midiChannel), channel);
2333 expectEquals (int (test.lastNoteFinished->initialNote), noteNumber);
2334 expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit);
2335 expect (test.lastNoteFinished->keyState == MPENote::off);
2336 }
2337
2338 void expectDoubleWithinRelativeError (double actual, double expected, double maxRelativeError)
2339 {
2340 const double maxAbsoluteError = jmax (1.0, std::abs (expected)) * maxRelativeError;
2341 expect (std::abs (expected - actual) < maxAbsoluteError);
2342 }
2343
2344 //==============================================================================
2345 MPEZoneLayout testLayout;
2346};
2347
2348static MPEInstrumentTests MPEInstrumentUnitTests;
2349
2350#endif
2351
2352} // namespace juce
Type jmax(const Type a, const Type b)
Definition MathsFunctions.h:48
#define noexcept
Definition DistrhoDefines.h:72
static void message(int level, const char *fmt,...)
Definition adplugdb.cpp:120
uint8_t uint8
Definition basics.h:86
static MidiMessage allControllersOff(int channel) noexcept
Definition MidiMessage.cpp:583
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
Definition MidiMessage.cpp:534
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
Definition MidiMessage.cpp:548
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
Definition MidiMessage.cpp:516
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
Definition MidiMessage.cpp:436
static MidiMessage pitchWheel(int channel, int position) noexcept
Definition MidiMessage.cpp:484
Definition juce_MPEInstrument.h:260
void setPitchbendTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:166
void setLegacyModeChannelRange(Range< int > channelRange)
Definition juce_MPEInstrument.cpp:127
void updateDimensionForNote(MPENote &, MPEDimension &, MPEValue)
Definition juce_MPEInstrument.cpp:513
bool isMemberChannelSustained[16]
Definition juce_MPEInstrument.h:370
void processMidiControllerMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:246
MPENote getMostRecentNoteOtherThan(MPENote otherThanThisNote) const noexcept
Definition juce_MPEInstrument.cpp:714
void processMidiPitchWheelMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:232
MPEZoneLayout getZoneLayout() const noexcept
Definition juce_MPEInstrument.cpp:63
virtual void sostenutoPedal(int midiChannel, bool isDown)
Definition juce_MPEInstrument.cpp:581
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
Definition juce_MPEInstrument.cpp:405
void updateNoteTotalPitchbend(MPENote &)
Definition juce_MPEInstrument.cpp:535
bool isMemberChannel(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:645
void processMidiAfterTouchMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:301
MPEZoneLayout zoneLayout
Definition juce_MPEInstrument.h:365
virtual ~MPEInstrument()
void handlePressureMSB(int midiChannel, int value) noexcept
Definition juce_MPEInstrument.cpp:311
void processMidiNoteOnMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:203
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
Definition juce_MPEInstrument.cpp:100
MPENote getNote(int index) const noexcept
Definition juce_MPEInstrument.cpp:689
TrackingMode
Definition juce_MPEInstrument.h:120
@ highestNoteOnChannel
Definition juce_MPEInstrument.h:123
@ lowestNoteOnChannel
Definition juce_MPEInstrument.h:122
@ lastNotePlayedOnChannel
Definition juce_MPEInstrument.h:121
@ allNotesOnChannel
Definition juce_MPEInstrument.h:124
void resetLastReceivedValues()
Definition juce_MPEInstrument.cpp:68
CriticalSection lock
Definition juce_MPEInstrument.h:360
ListenerList< Listener > listeners
Definition juce_MPEInstrument.h:366
MPEInstrument() noexcept
Definition juce_MPEInstrument.cpp:39
void setZoneLayout(MPEZoneLayout newLayout)
Definition juce_MPEInstrument.cpp:85
virtual void polyAftertouch(int midiChannel, int midiNoteNumber, MPEValue value)
Definition juce_MPEInstrument.cpp:423
virtual void processNextMidiEvent(const MidiMessage &message)
Definition juce_MPEInstrument.cpp:188
void setLegacyModePitchbendRange(int pitchbendRange)
Definition juce_MPEInstrument.cpp:146
MPEDimension pitchbendDimension
Definition juce_MPEInstrument.h:388
void processMidiNoteOffMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:224
bool isLegacyModeEnabled() const noexcept
Definition juce_MPEInstrument.cpp:117
void updateDimension(int midiChannel, MPEDimension &, MPEValue)
Definition juce_MPEInstrument.cpp:450
const MPENote * getLastNotePlayedPtr(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:766
void setPressureTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:161
MPEDimension timbreDimension
Definition juce_MPEInstrument.h:388
void handleSustainOrSostenuto(int midiChannel, bool isDown, bool isSostenuto)
Definition juce_MPEInstrument.cpp:588
void addListener(Listener *listenerToAdd)
Definition juce_MPEInstrument.cpp:177
void removeListener(Listener *listenerToRemove)
Definition juce_MPEInstrument.cpp:182
MPENote getNoteWithID(uint16 noteID) const noexcept
Definition juce_MPEInstrument.cpp:694
void handlePressureLSB(int midiChannel, int value) noexcept
Definition juce_MPEInstrument.cpp:319
virtual void sustainPedal(int midiChannel, bool isDown)
Definition juce_MPEInstrument.cpp:575
void setTimbreTrackingMode(TrackingMode modeToUse)
Definition juce_MPEInstrument.cpp:171
const MPENote * getNotePtr(int midiChannel, int midiNoteNumber) const noexcept
Definition juce_MPEInstrument.cpp:728
int getNumPlayingNotes() const noexcept
Definition juce_MPEInstrument.cpp:676
void releaseAllNotes()
Definition juce_MPEInstrument.cpp:841
virtual void timbre(int midiChannel, MPEValue value)
Definition juce_MPEInstrument.cpp:417
MPEDimension pressureDimension
Definition juce_MPEInstrument.h:388
void updateDimensionMaster(bool, MPEDimension &, MPEValue)
Definition juce_MPEInstrument.cpp:482
const MPENote * getLowestNotePtr(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:814
MPENote getMostRecentNote(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:706
Range< int > getLegacyModeChannelRange() const noexcept
Definition juce_MPEInstrument.cpp:122
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
Definition juce_MPEInstrument.cpp:338
void processMidiResetAllControllersMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:261
int getLegacyModePitchbendRange() const noexcept
Definition juce_MPEInstrument.cpp:141
void handleTimbreLSB(int midiChannel, int value) noexcept
Definition juce_MPEInstrument.cpp:332
MPEValue getInitialValueForNewNote(int midiChannel, MPEDimension &) const
Definition juce_MPEInstrument.cpp:441
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
Definition juce_MPEInstrument.cpp:370
uint8 lastTimbreLowerBitReceivedOnChannel[16]
Definition juce_MPEInstrument.h:369
void handleTimbreMSB(int midiChannel, int value) noexcept
Definition juce_MPEInstrument.cpp:324
void processMidiChannelPressureMessage(const MidiMessage &)
Definition juce_MPEInstrument.cpp:239
Array< MPENote > notes
Definition juce_MPEInstrument.h:364
const MPENote * getHighestNotePtr(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:788
virtual void pressure(int midiChannel, MPEValue value)
Definition juce_MPEInstrument.cpp:411
void callListenersDimensionChanged(const MPENote &, const MPEDimension &)
Definition juce_MPEInstrument.cpp:527
bool isMasterChannel(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:654
uint8 lastPressureLowerBitReceivedOnChannel[16]
Definition juce_MPEInstrument.h:368
bool isUsingChannel(int midiChannel) const noexcept
Definition juce_MPEInstrument.cpp:666
LegacyMode legacyMode
Definition juce_MPEInstrument.h:387
Definition juce_MPEValue.h:37
float asSignedFloat() const noexcept
Definition juce_MPEValue.cpp:74
static MPEValue centreValue() noexcept
Definition juce_MPEValue.cpp:60
static MPEValue from14BitInt(int value) noexcept
Definition juce_MPEValue.cpp:40
static MPEValue minValue() noexcept
Definition juce_MPEValue.cpp:59
static MPEValue from7BitInt(int value) noexcept
Definition juce_MPEValue.cpp:30
Definition juce_MPEZoneLayout.h:121
Definition juce_MidiMessage.h:35
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
Definition juce_MidiMessage.cpp:482
Definition juce_Range.h:40
Definition juce_UnitTest.h:70
int * l
Definition inflate.c:1579
register unsigned i
Definition inflate.c:1575
static PuglViewHint int value
Definition pugl.h:1708
#define JUCE_BEGIN_IGNORE_WARNINGS_MSVC(warnings)
Definition juce_CompilerWarnings.h:198
#define JUCE_END_IGNORE_WARNINGS_MSVC
Definition juce_CompilerWarnings.h:199
#define jassert(expression)
#define jassertfalse
Definition juce_UnitTestCategories.h:27
JOCTET * buffer
Definition juce_JPEGLoader.cpp:302
Definition carla_juce.cpp:31
CriticalSection::ScopedLockType ScopedLock
Definition juce_CriticalSection.h:186
unsigned short uint16
Definition juce_MathsFunctions.h:41
Base
Definition PathUtil.h:34
#define false
Definition ordinals.h:83
png_structrp int mode
Definition png.h:1139
static int test(SerdEnv *env, bool top_level, bool pretty_numbers)
Definition sratom_test.c:79
Definition juce_MPEInstrument.h:380
TrackingMode trackingMode
Definition juce_MPEInstrument.h:381
MPEValue & getValue(MPENote &note) noexcept
Definition juce_MPEInstrument.h:384
MPEValue lastValueReceivedOnChannel[16]
Definition juce_MPEInstrument.h:382
Definition juce_MPENote.h:40
@ keyDown
Definition juce_MPENote.h:46
@ off
Definition juce_MPENote.h:45
@ keyDownAndSustained
Definition juce_MPENote.h:48
@ sustained
Definition juce_MPENote.h:47
MPEValue timbre
Definition juce_MPENote.h:144
MPEValue pitchbend
Definition juce_MPENote.h:128
MPEValue pressure
Definition juce_MPENote.h:133
double totalPitchbendInSemitones
Definition juce_MPENote.h:164
uint8 midiChannel
Definition juce_MPENote.h:103
int result
Definition process.c:1455
#define void
Definition unzip.h:396
#define const
Definition zconf.h:137