OpenShot Audio Library | OpenShotAudio: juce_MPEInstrument.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.3.1

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//==============================================================================
33MPEInstrument::MPEInstrument() noexcept
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
51MPEInstrument::~MPEInstrument()
52{
53}
54
55//==============================================================================
56 MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept
57{
58 return zoneLayout;
59}
60
61 void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
62{
63 releaseAllNotes();
64
65 const ScopedLock sl (lock);
66 legacyMode.isEnabled = false;
67 zoneLayout = newLayout;
68}
69
70//==============================================================================
71 void MPEInstrument::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
72{
73 releaseAllNotes();
74
75 const ScopedLock sl (lock);
76 legacyMode.isEnabled = true;
77 legacyMode.pitchbendRange = pitchbendRange;
78 legacyMode.channelRange = channelRange;
79 zoneLayout.clearAllZones();
80}
81
82 bool MPEInstrument::isLegacyModeEnabled() const noexcept
83{
84 return legacyMode.isEnabled;
85}
86
87 Range<int> MPEInstrument::getLegacyModeChannelRange() const noexcept
88{
89 return legacyMode.channelRange;
90}
91
92 void MPEInstrument::setLegacyModeChannelRange (Range<int> channelRange)
93{
94 jassert (allChannels.contains (channelRange));
95
96 releaseAllNotes();
97 const ScopedLock sl (lock);
98 legacyMode.channelRange = channelRange;
99}
100
101 int MPEInstrument::getLegacyModePitchbendRange() const noexcept
102{
103 return legacyMode.pitchbendRange;
104}
105
106 void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange)
107{
108 jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
109
110 releaseAllNotes();
111 const ScopedLock sl (lock);
112 legacyMode.pitchbendRange = pitchbendRange;
113}
114
115//==============================================================================
116 void MPEInstrument::setPressureTrackingMode (TrackingMode modeToUse)
117{
118 pressureDimension.trackingMode = modeToUse;
119}
120
121 void MPEInstrument::setPitchbendTrackingMode (TrackingMode modeToUse)
122{
123 pitchbendDimension.trackingMode = modeToUse;
124}
125
126 void MPEInstrument::setTimbreTrackingMode (TrackingMode modeToUse)
127{
128 timbreDimension.trackingMode = modeToUse;
129}
130
131//==============================================================================
132 void MPEInstrument::addListener (Listener* listenerToAdd)
133{
134 listeners.add (listenerToAdd);
135}
136
137 void MPEInstrument::removeListener (Listener* listenerToRemove)
138{
139 listeners.remove (listenerToRemove);
140}
141
142//==============================================================================
143 void MPEInstrument::processNextMidiEvent (const MidiMessage& message)
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(),
168 MPEValue::from7BitInt (64));
169 }
170 else
171 {
172 noteOn (message.getChannel(),
173 message.getNoteNumber(),
174 MPEValue::from7BitInt (message.getVelocity()));
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//==============================================================================
293 void MPEInstrument::noteOn (int midiChannel,
294 int midiNoteNumber,
295 MPEValue midiNoteOnVelocity)
296{
297 if (! isUsingChannel (midiChannel))
298 return;
299
300 MPENote newNote (midiChannel,
301 midiNoteNumber,
302 midiNoteOnVelocity,
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
314 alreadyPlayingNote->keyState = MPENote::off;
315 alreadyPlayingNote->noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
316 listeners.call ([=] (Listener& l) { l.noteReleased (*alreadyPlayingNote); });
317 notes.remove (alreadyPlayingNote);
318 }
319
320 notes.add (newNote);
321 listeners.call ([&] (Listener& l) { l.noteAdded (newNote); });
322}
323
324//==============================================================================
325 void MPEInstrument::noteOff (int midiChannel,
326 int midiNoteNumber,
327 MPEValue midiNoteOffVelocity)
328{
329 if (notes.isEmpty() || ! isUsingChannel (midiChannel))
330 return;
331
332 const ScopedLock sl (lock);
333
334 if (auto* note = getNotePtr (midiChannel, midiNoteNumber))
335 {
336 note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off;
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//==============================================================================
360 void MPEInstrument::pitchbend (int midiChannel, MPEValue value)
361{
362 const ScopedLock sl (lock);
363 updateDimension (midiChannel, pitchbendDimension, value);
364}
365
366 void MPEInstrument::pressure (int midiChannel, MPEValue value)
367{
368 const ScopedLock sl (lock);
369 updateDimension (midiChannel, pressureDimension, value);
370}
371
372 void MPEInstrument::timbre (int midiChannel, MPEValue value)
373{
374 const ScopedLock sl (lock);
375 updateDimension (midiChannel, timbreDimension, value);
376}
377
378 void 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
396 MPEValue 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//==============================================================================
528 void MPEInstrument::sustainPedal (int midiChannel, bool isDown)
529{
530 const ScopedLock sl (lock);
531 handleSustainOrSostenuto (midiChannel, isDown, false);
532}
533
534 void 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)
559 note.keyState = MPENote::keyDownAndSustained;
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//==============================================================================
596 bool 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
605 bool 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
617 bool 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//==============================================================================
627 int MPEInstrument::getNumPlayingNotes() const noexcept
628{
629 return notes.size();
630}
631
632 MPENote MPEInstrument::getNote (int midiChannel, int midiNoteNumber) const noexcept
633{
634 if (auto* note = getNotePtr (midiChannel, midiNoteNumber))
635 return *note;
636
637 return {};
638}
639
640 MPENote MPEInstrument::getNote (int index) const noexcept
641{
642 return notes[index];
643}
644
645//==============================================================================
646 MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept
647{
648 if (auto* note = getLastNotePlayedPtr (midiChannel))
649 return *note;
650
651 return {};
652}
653
654 MPENote MPEInstrument::getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept
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//==============================================================================
779 void MPEInstrument::releaseAllNotes()
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:
802 MPEInstrumentTests()
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 {
846 UnitTestInstrument test;
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 {
881 UnitTestInstrument test;
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
900 UnitTestInstrument test;
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.
912 UnitTestInstrument test;
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 {
923 UnitTestInstrument test;
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 {
939 UnitTestInstrument test;
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 {
952 UnitTestInstrument test;
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 {
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 // 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,
1111 MPEValue::centreValue(), MPEValue::centreValue(),
1112 MPEValue::centreValue(), MPEValue::centreValue());
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 {
1151 UnitTestInstrument test;
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 {
1180 UnitTestInstrument test;
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 {
1192 UnitTestInstrument test;
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 {
1206 UnitTestInstrument test;
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 {
1214 UnitTestInstrument test;
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 {
1223 UnitTestInstrument test;
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 {
1235 UnitTestInstrument test;
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 {
1250 UnitTestInstrument test;
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 {
1269 UnitTestInstrument test;
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 {
1301 UnitTestInstrument test;
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 {
1313 UnitTestInstrument test;
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 {
1327 UnitTestInstrument test;
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 {
1353 UnitTestInstrument test;
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.
1375 UnitTestInstrument test;
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.
1404 UnitTestInstrument test;
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.
1434 UnitTestInstrument test;
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 {
1452 UnitTestInstrument test;
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 {
1481 UnitTestInstrument test;
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 {
1493 UnitTestInstrument test;
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 {
1507 UnitTestInstrument test;
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)
1525 UnitTestInstrument test;
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
1540 UnitTestInstrument test;
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
1555 UnitTestInstrument test;
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
1570 UnitTestInstrument test;
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)
1589 UnitTestInstrument test;
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
1604 UnitTestInstrument test;
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
1619 UnitTestInstrument test;
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
1634 UnitTestInstrument test;
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)
1653 UnitTestInstrument test;
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
1668 UnitTestInstrument test;
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
1683 UnitTestInstrument test;
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
1698 UnitTestInstrument test;
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 {
1715 UnitTestInstrument test;
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 {
1854 UnitTestInstrument test;
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 {
1879 UnitTestInstrument test;
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
1955 UnitTestInstrument test;
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
1986 UnitTestInstrument test;
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 {
2007 UnitTestInstrument test;
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 {
2020 UnitTestInstrument test;
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 {
2033 UnitTestInstrument test;
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 {
2046 UnitTestInstrument test;
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.
2061 UnitTestInstrument test;
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.
2070 UnitTestInstrument test;
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.
2093 UnitTestInstrument test;
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
2115 UnitTestInstrument test;
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,
2254 int noteOnVelocity7Bit,
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
2268 void expectHasFinishedNote (const UnitTestInstrument& test,
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
2278 void expectDoubleWithinRelativeError (double actual, double expected, double maxRelativeError)
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
juce::GenericScopedLock
Definition: juce_ScopedLock.h:55
juce::MPEInstrument::Listener
Definition: juce_MPEInstrument.h:256
juce::MPEInstrument::Listener::noteAdded
virtual void noteAdded(MPENote newNote)
Definition: juce_MPEInstrument.h:264
juce::MPEInstrument::Listener::noteReleased
virtual void noteReleased(MPENote finishedNote)
Definition: juce_MPEInstrument.h:299
juce::MPEInstrument::Listener::noteKeyStateChanged
virtual void noteKeyStateChanged(MPENote changedNote)
Definition: juce_MPEInstrument.h:292
juce::MPEInstrument::setPitchbendTrackingMode
void setPitchbendTrackingMode(TrackingMode modeToUse)
Definition: juce_MPEInstrument.cpp:121
juce::MPEInstrument::setLegacyModeChannelRange
void setLegacyModeChannelRange(Range< int > channelRange)
Definition: juce_MPEInstrument.cpp:92
juce::MPEInstrument::getMostRecentNoteOtherThan
MPENote getMostRecentNoteOtherThan(MPENote otherThanThisNote) const noexcept
Definition: juce_MPEInstrument.cpp:654
juce::MPEInstrument::getZoneLayout
MPEZoneLayout getZoneLayout() const noexcept
Definition: juce_MPEInstrument.cpp:56
juce::MPEInstrument::sostenutoPedal
virtual void sostenutoPedal(int midiChannel, bool isDown)
Definition: juce_MPEInstrument.cpp:534
juce::MPEInstrument::pitchbend
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
Definition: juce_MPEInstrument.cpp:360
juce::MPEInstrument::isMemberChannel
bool isMemberChannel(int midiChannel) const noexcept
Definition: juce_MPEInstrument.cpp:596
juce::MPEInstrument::~MPEInstrument
virtual ~MPEInstrument()
Definition: juce_MPEInstrument.cpp:51
juce::MPEInstrument::enableLegacyMode
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
Definition: juce_MPEInstrument.cpp:71
juce::MPEInstrument::getNote
MPENote getNote(int index) const noexcept
Definition: juce_MPEInstrument.cpp:640
juce::MPEInstrument::TrackingMode
TrackingMode
Definition: juce_MPEInstrument.h:119
juce::MPEInstrument::allNotesOnChannel
@ allNotesOnChannel
Definition: juce_MPEInstrument.h:123
juce::MPEInstrument::MPEInstrument
MPEInstrument() noexcept
Definition: juce_MPEInstrument.cpp:33
juce::MPEInstrument::setZoneLayout
void setZoneLayout(MPEZoneLayout newLayout)
Definition: juce_MPEInstrument.cpp:61
juce::MPEInstrument::polyAftertouch
virtual void polyAftertouch(int midiChannel, int midiNoteNumber, MPEValue value)
Definition: juce_MPEInstrument.cpp:378
juce::MPEInstrument::processNextMidiEvent
virtual void processNextMidiEvent(const MidiMessage &message)
Definition: juce_MPEInstrument.cpp:143
juce::MPEInstrument::setLegacyModePitchbendRange
void setLegacyModePitchbendRange(int pitchbendRange)
Definition: juce_MPEInstrument.cpp:106
juce::MPEInstrument::isLegacyModeEnabled
bool isLegacyModeEnabled() const noexcept
Definition: juce_MPEInstrument.cpp:82
juce::MPEInstrument::setPressureTrackingMode
void setPressureTrackingMode(TrackingMode modeToUse)
Definition: juce_MPEInstrument.cpp:116
juce::MPEInstrument::addListener
void addListener(Listener *listenerToAdd)
Definition: juce_MPEInstrument.cpp:132
juce::MPEInstrument::removeListener
void removeListener(Listener *listenerToRemove)
Definition: juce_MPEInstrument.cpp:137
juce::MPEInstrument::sustainPedal
virtual void sustainPedal(int midiChannel, bool isDown)
Definition: juce_MPEInstrument.cpp:528
juce::MPEInstrument::setTimbreTrackingMode
void setTimbreTrackingMode(TrackingMode modeToUse)
Definition: juce_MPEInstrument.cpp:126
juce::MPEInstrument::getNumPlayingNotes
int getNumPlayingNotes() const noexcept
Definition: juce_MPEInstrument.cpp:627
juce::MPEInstrument::releaseAllNotes
void releaseAllNotes()
Definition: juce_MPEInstrument.cpp:779
juce::MPEInstrument::timbre
virtual void timbre(int midiChannel, MPEValue value)
Definition: juce_MPEInstrument.cpp:372
juce::MPEInstrument::getMostRecentNote
MPENote getMostRecentNote(int midiChannel) const noexcept
Definition: juce_MPEInstrument.cpp:646
juce::MPEInstrument::getLegacyModeChannelRange
Range< int > getLegacyModeChannelRange() const noexcept
Definition: juce_MPEInstrument.cpp:87
juce::MPEInstrument::noteOn
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
Definition: juce_MPEInstrument.cpp:293
juce::MPEInstrument::getLegacyModePitchbendRange
int getLegacyModePitchbendRange() const noexcept
Definition: juce_MPEInstrument.cpp:101
juce::MPEInstrument::noteOff
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
Definition: juce_MPEInstrument.cpp:325
juce::MPEInstrument::pressure
virtual void pressure(int midiChannel, MPEValue value)
Definition: juce_MPEInstrument.cpp:366
juce::MPEInstrument::isMasterChannel
bool isMasterChannel(int midiChannel) const noexcept
Definition: juce_MPEInstrument.cpp:605
juce::MPEInstrument::isUsingChannel
bool isUsingChannel(int midiChannel) const noexcept
Definition: juce_MPEInstrument.cpp:617
juce::MPEValue
Definition: juce_MPEValue.h:37
juce::MPEValue::centreValue
static MPEValue centreValue() noexcept
Definition: juce_MPEValue.cpp:48
juce::MPEValue::from14BitInt
static MPEValue from14BitInt(int value) noexcept
Definition: juce_MPEValue.cpp:40
juce::MPEValue::minValue
static MPEValue minValue() noexcept
Definition: juce_MPEValue.cpp:47
juce::MPEValue::from7BitInt
static MPEValue from7BitInt(int value) noexcept
Definition: juce_MPEValue.cpp:30
juce::MPEZoneLayout
Definition: juce_MPEZoneLayout.h:45
juce::MPEZoneLayout::clearAllZones
void clearAllZones()
Definition: juce_MPEZoneLayout.cpp:87
juce::MPEZoneLayout::getUpperZone
const Zone getUpperZone() const noexcept
Definition: juce_MPEZoneLayout.h:145
juce::MPEZoneLayout::getLowerZone
const Zone getLowerZone() const noexcept
Definition: juce_MPEZoneLayout.h:142
juce::MPEZoneLayout::processNextMidiEvent
void processNextMidiEvent(const MidiMessage &message)
Definition: juce_MPEZoneLayout.cpp:96
juce::MidiMessage
Definition: juce_MidiMessage.h:35
juce::MidiMessage::isAftertouch
bool isAftertouch() const noexcept
Definition: juce_MidiMessage.cpp:451
juce::MidiMessage::isNoteOn
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
Definition: juce_MidiMessage.cpp:390
juce::MidiMessage::getChannel
int getChannel() const noexcept
Definition: juce_MidiMessage.cpp:359
juce::MidiMessage::aftertouchChange
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
Definition: juce_MidiMessage.cpp:462
juce::MidiMessage::isController
bool isController() const noexcept
Definition: juce_MidiMessage.cpp:543
juce::MidiMessage::isNoteOff
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
Definition: juce_MidiMessage.cpp:398
juce::MidiMessage::isPitchWheel
bool isPitchWheel() const noexcept
Definition: juce_MidiMessage.cpp:522
juce::MidiMessage::getNoteNumber
int getNoteNumber() const noexcept
Definition: juce_MidiMessage.cpp:412
juce::MidiMessage::isResetAllControllers
bool isResetAllControllers() const noexcept
Definition: juce_MidiMessage.cpp:633
juce::MidiMessage::isAllNotesOff
bool isAllNotesOff() const noexcept
Definition: juce_MidiMessage.cpp:616
juce::MidiMessage::getVelocity
uint8 getVelocity() const noexcept
Definition: juce_MidiMessage.cpp:423
juce::MidiMessage::isChannelPressure
bool isChannelPressure() const noexcept
Definition: juce_MidiMessage.cpp:475
juce::Range
Definition: juce_Range.h:40
juce::UnitTest
Definition: juce_UnitTest.h:70
juce::MPENote
Definition: juce_MPENote.h:40
juce::MPENote::keyDown
@ keyDown
Definition: juce_MPENote.h:46
juce::MPENote::off
@ off
Definition: juce_MPENote.h:45
juce::MPENote::keyDownAndSustained
@ keyDownAndSustained
Definition: juce_MPENote.h:48
juce::MPENote::sustained
@ sustained
Definition: juce_MPENote.h:47
juce::MPENote::timbre
MPEValue timbre
Definition: juce_MPENote.h:144
juce::MPENote::pitchbend
MPEValue pitchbend
Definition: juce_MPENote.h:128
juce::MPENote::pressure
MPEValue pressure
Definition: juce_MPENote.h:133