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