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