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