OpenShot Audio Library | OpenShotAudio  0.3.3
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 
23 namespace juce
24 {
25 
26 namespace
27 {
28  const uint8 noLSBValueReceived = 0xff;
29  const Range<int> allChannels { 1, 17 };
30 }
31 
32 //==============================================================================
34 {
35  std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived);
36  std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived);
37  std::fill_n (isMemberChannelSustained, 16, false);
38 
39  pitchbendDimension.value = &MPENote::pitchbend;
40  pressureDimension.value = &MPENote::pressure;
41  timbreDimension.value = &MPENote::timbre;
42 
43  // the default value for pressure is 0, for all other dimension it is centre (= default MPEValue)
44  std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue());
45 
46  legacyMode.isEnabled = false;
47  legacyMode.pitchbendRange = 2;
48  legacyMode.channelRange = allChannels;
49 }
50 
52 {
53 }
54 
55 //==============================================================================
57 {
58  return zoneLayout;
59 }
60 
62 {
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 {
74 
75  const ScopedLock sl (lock);
76  legacyMode.isEnabled = true;
77  legacyMode.pitchbendRange = pitchbendRange;
78  legacyMode.channelRange = channelRange;
79  zoneLayout.clearAllZones();
80 }
81 
83 {
84  return legacyMode.isEnabled;
85 }
86 
88 {
89  return legacyMode.channelRange;
90 }
91 
93 {
94  jassert (allChannels.contains (channelRange));
95 
97  const ScopedLock sl (lock);
98  legacyMode.channelRange = channelRange;
99 }
100 
102 {
103  return legacyMode.pitchbendRange;
104 }
105 
107 {
108  jassert (pitchbendRange >= 0 && pitchbendRange <= 96);
109 
110  releaseAllNotes();
111  const ScopedLock sl (lock);
112  legacyMode.pitchbendRange = pitchbendRange;
113 }
114 
115 //==============================================================================
117 {
118  pressureDimension.trackingMode = modeToUse;
119 }
120 
122 {
123  pitchbendDimension.trackingMode = modeToUse;
124 }
125 
127 {
128  timbreDimension.trackingMode = modeToUse;
129 }
130 
131 //==============================================================================
133 {
134  listeners.add (listenerToAdd);
135 }
136 
137 void MPEInstrument::removeListener (Listener* listenerToRemove)
138 {
139  listeners.remove (listenerToRemove);
140 }
141 
142 //==============================================================================
144 {
145  zoneLayout.processNextMidiEvent (message);
146 
147  if (message.isNoteOn (true)) processMidiNoteOnMessage (message);
148  else if (message.isNoteOff (false)) processMidiNoteOffMessage (message);
149  else if (message.isResetAllControllers()
150  || message.isAllNotesOff()) processMidiResetAllControllersMessage (message);
151  else if (message.isPitchWheel()) processMidiPitchWheelMessage (message);
152  else if (message.isChannelPressure()) processMidiChannelPressureMessage (message);
153  else if (message.isController()) processMidiControllerMessage (message);
154  else if (message.isAftertouch()) processMidiAfterTouchMessage (message);
155 }
156 
157 //==============================================================================
158 void 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 //==============================================================================
179 void MPEInstrument::processMidiNoteOffMessage (const MidiMessage& message)
180 {
181  noteOff (message.getChannel(),
182  message.getNoteNumber(),
183  MPEValue::from7BitInt (message.getVelocity()));
184 }
185 
186 //==============================================================================
187 void MPEInstrument::processMidiPitchWheelMessage (const MidiMessage& message)
188 {
189  pitchbend (message.getChannel(),
190  MPEValue::from14BitInt (message.getPitchWheelValue()));
191 }
192 
193 //==============================================================================
194 void MPEInstrument::processMidiChannelPressureMessage (const MidiMessage& message)
195 {
196  pressure (message.getChannel(),
197  MPEValue::from7BitInt (message.getChannelPressureValue()));
198 }
199 
200 //==============================================================================
201 void 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 //==============================================================================
216 void 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 
256 void 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 //==============================================================================
266 void 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 
274 void MPEInstrument::handlePressureLSB (int midiChannel, int value) noexcept
275 {
276  lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
277 }
278 
279 void 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 
287 void 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 //==============================================================================
405 void 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 //==============================================================================
437 void 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 //==============================================================================
468 void 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 //==============================================================================
482 void 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 //==============================================================================
490 void 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 //==============================================================================
541 void 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 //==============================================================================
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 
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 //==============================================================================
668 const 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 
681 MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) noexcept
682 {
683  return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getNotePtr (midiChannel, midiNoteNumber));
684 }
685 
686 //==============================================================================
687 const 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 
700 MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) noexcept
701 {
702  return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getNotePtr (midiChannel, mode));
703 }
704 
705 //==============================================================================
706 const 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 
720 MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) noexcept
721 {
722  return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getLastNotePlayedPtr (midiChannel));
723 }
724 
725 //==============================================================================
726 const 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 
747 MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) noexcept
748 {
749  return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getHighestNotePtr (midiChannel));
750 }
751 
752 const 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 
773 MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) noexcept
774 {
775  return const_cast<MPENote*> (static_cast<const MPEInstrument&> (*this).getLowestNotePtr (midiChannel));
776 }
777 
778 //==============================================================================
780 {
781  const ScopedLock sl (lock);
782 
783  for (auto i = notes.size(); --i >= 0;)
784  {
785  auto& note = notes.getReference (i);
786  note.keyState = MPENote::off;
787  note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
788  listeners.call ([&] (Listener& l) { l.noteReleased (note); });
789  }
790 
791  notes.clear();
792 }
793 
794 
795 //==============================================================================
796 //==============================================================================
797 #if JUCE_UNIT_TESTS
798 
799 class MPEInstrumentTests : public UnitTest
800 {
801 public:
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,
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 
2131 private:
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 
2288 static MPEInstrumentTests MPEInstrumentUnitTests;
2289 
2290 #endif
2291 
2292 } // namespace juce
virtual void noteAdded(MPENote newNote)
virtual void noteReleased(MPENote finishedNote)
virtual void noteKeyStateChanged(MPENote changedNote)
void setPitchbendTrackingMode(TrackingMode modeToUse)
void setLegacyModeChannelRange(Range< int > channelRange)
MPENote getMostRecentNoteOtherThan(MPENote otherThanThisNote) const noexcept
MPEZoneLayout getZoneLayout() const noexcept
virtual void sostenutoPedal(int midiChannel, bool isDown)
virtual void pitchbend(int midiChannel, MPEValue pitchbend)
bool isMemberChannel(int midiChannel) const noexcept
void enableLegacyMode(int pitchbendRange=2, Range< int > channelRange=Range< int >(1, 17))
MPENote getNote(int index) const noexcept
void setZoneLayout(MPEZoneLayout newLayout)
virtual void polyAftertouch(int midiChannel, int midiNoteNumber, MPEValue value)
virtual void processNextMidiEvent(const MidiMessage &message)
void setLegacyModePitchbendRange(int pitchbendRange)
bool isLegacyModeEnabled() const noexcept
void setPressureTrackingMode(TrackingMode modeToUse)
void addListener(Listener *listenerToAdd)
void removeListener(Listener *listenerToRemove)
virtual void sustainPedal(int midiChannel, bool isDown)
void setTimbreTrackingMode(TrackingMode modeToUse)
int getNumPlayingNotes() const noexcept
virtual void timbre(int midiChannel, MPEValue value)
MPENote getMostRecentNote(int midiChannel) const noexcept
Range< int > getLegacyModeChannelRange() const noexcept
virtual void noteOn(int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity)
int getLegacyModePitchbendRange() const noexcept
virtual void noteOff(int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity)
virtual void pressure(int midiChannel, MPEValue value)
bool isMasterChannel(int midiChannel) const noexcept
bool isUsingChannel(int midiChannel) const noexcept
static MidiBuffer setUpperZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static MidiBuffer setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2)
static MPEValue centreValue() noexcept
static MPEValue from14BitInt(int value) noexcept
static MPEValue minValue() noexcept
static MPEValue from7BitInt(int value) noexcept
const Zone getUpperZone() const noexcept
const Zone getLowerZone() const noexcept
void processNextMidiEvent(const MidiMessage &message)
bool isAftertouch() const noexcept
bool isNoteOn(bool returnTrueForVelocity0=false) const noexcept
int getChannel() const noexcept
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
bool isController() const noexcept
static MidiMessage pitchWheel(int channel, int position) noexcept
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
bool isPitchWheel() const noexcept
int getNoteNumber() const noexcept
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
bool isResetAllControllers() const noexcept
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
bool isAllNotesOff() const noexcept
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
uint8 getVelocity() const noexcept
bool isChannelPressure() const noexcept
static MidiMessage allControllersOff(int channel) noexcept
MPEValue timbre
Definition: juce_MPENote.h:144
MPEValue pitchbend
Definition: juce_MPENote.h:128
MPEValue pressure
Definition: juce_MPENote.h:133