The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2058 lines
92KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace
  18. {
  19. const uint8 noLSBValueReceived = 0xff;
  20. const Range<int> allChannels = Range<int> (1, 17);
  21. }
  22. //==============================================================================
  23. MPEInstrument::MPEInstrument() noexcept
  24. {
  25. std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived);
  26. std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived);
  27. std::fill_n (isNoteChannelSustained, 16, false);
  28. pitchbendDimension.value = &MPENote::pitchbend;
  29. pressureDimension.value = &MPENote::pressure;
  30. timbreDimension.value = &MPENote::timbre;
  31. omniMode.isEnabled = false;
  32. omniMode.pitchbendRange = 2;
  33. }
  34. MPEInstrument::~MPEInstrument()
  35. {
  36. }
  37. //==============================================================================
  38. MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept
  39. {
  40. return zoneLayout;
  41. }
  42. void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
  43. {
  44. releaseAllNotes();
  45. const ScopedLock sl (lock);
  46. omniMode.isEnabled = false;
  47. zoneLayout = newLayout;
  48. }
  49. //==============================================================================
  50. void MPEInstrument::enableOmniMode (int pitchbendRange)
  51. {
  52. releaseAllNotes();
  53. const ScopedLock sl (lock);
  54. omniMode.isEnabled = true;
  55. omniMode.pitchbendRange = pitchbendRange;
  56. zoneLayout.clearAllZones();
  57. }
  58. bool MPEInstrument::isOmniModeEnabled() const noexcept
  59. {
  60. return omniMode.isEnabled;
  61. }
  62. //==============================================================================
  63. void MPEInstrument::setPressureTrackingMode (TrackingMode modeToUse)
  64. {
  65. pressureDimension.trackingMode = modeToUse;
  66. }
  67. void MPEInstrument::setPitchbendTrackingMode (TrackingMode modeToUse)
  68. {
  69. pitchbendDimension.trackingMode = modeToUse;
  70. }
  71. void MPEInstrument::setTimbreTrackingMode (TrackingMode modeToUse)
  72. {
  73. timbreDimension.trackingMode = modeToUse;
  74. }
  75. //==============================================================================
  76. void MPEInstrument::addListener (Listener* const listenerToAdd) noexcept
  77. {
  78. listeners.add (listenerToAdd);
  79. }
  80. void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept
  81. {
  82. listeners.remove (listenerToRemove);
  83. }
  84. MPEInstrument::Listener::Listener()
  85. {
  86. }
  87. MPEInstrument::Listener::~Listener()
  88. {
  89. }
  90. //==============================================================================
  91. void MPEInstrument::processNextMidiEvent (const MidiMessage& message)
  92. {
  93. zoneLayout.processNextMidiEvent (message);
  94. if (message.isNoteOn (true)) processMidiNoteOnMessage (message);
  95. else if (message.isNoteOff (false)) processMidiNoteOffMessage (message);
  96. else if (message.isAllNotesOff()) processMidiAllNotesOffMessage (message);
  97. else if (message.isPitchWheel()) processMidiPitchWheelMessage (message);
  98. else if (message.isChannelPressure()) processMidiChannelPressureMessage (message);
  99. else if (message.isController()) processMidiControllerMessage (message);
  100. }
  101. //==============================================================================
  102. void MPEInstrument::processMidiNoteOnMessage (const MidiMessage& message)
  103. {
  104. // Note: if a note-on with velocity = 0 is used to convey a note-off,
  105. // then the actual note-off velocity is not known. In this case,
  106. // the MPE convention is to use note-off velocity = 64.
  107. if (message.getVelocity() == 0)
  108. {
  109. noteOff (message.getChannel(),
  110. message.getNoteNumber(),
  111. MPEValue::from7BitInt (64));
  112. }
  113. else
  114. {
  115. noteOn (message.getChannel(),
  116. message.getNoteNumber(),
  117. MPEValue::from7BitInt (message.getVelocity()));
  118. }
  119. }
  120. //==============================================================================
  121. void MPEInstrument::processMidiNoteOffMessage (const MidiMessage& message)
  122. {
  123. noteOff (message.getChannel(),
  124. message.getNoteNumber(),
  125. MPEValue::from7BitInt (message.getVelocity()));
  126. }
  127. //==============================================================================
  128. void MPEInstrument::processMidiPitchWheelMessage (const MidiMessage& message)
  129. {
  130. pitchbend (message.getChannel(),
  131. MPEValue::from14BitInt (message.getPitchWheelValue()));
  132. }
  133. //==============================================================================
  134. void MPEInstrument::processMidiChannelPressureMessage (const MidiMessage& message)
  135. {
  136. pressure (message.getChannel(),
  137. MPEValue::from7BitInt (message.getChannelPressureValue()));
  138. }
  139. //==============================================================================
  140. void MPEInstrument::processMidiControllerMessage (const MidiMessage& message)
  141. {
  142. switch (message.getControllerNumber())
  143. {
  144. case 64: sustainPedal (message.getChannel(), message.isSustainPedalOn()); break;
  145. case 66: sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn()); break;
  146. case 70: handlePressureMSB (message.getChannel(), message.getControllerValue()); break;
  147. case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue()); break;
  148. case 102: handlePressureLSB (message.getChannel(), message.getControllerValue()); break;
  149. case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue()); break;
  150. default: break;
  151. }
  152. }
  153. //==============================================================================
  154. void MPEInstrument::processMidiAllNotesOffMessage (const MidiMessage& message)
  155. {
  156. if (omniMode.isEnabled)
  157. {
  158. for (int i = notes.size(); --i >= 0;)
  159. {
  160. MPENote& note = notes.getReference (i);
  161. if (note.midiChannel == message.getChannel())
  162. {
  163. note.keyState = MPENote::off;
  164. note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
  165. listeners.call (&MPEInstrument::Listener::noteReleased, note);
  166. notes.remove (i);
  167. }
  168. }
  169. }
  170. else if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (message.getChannel()))
  171. {
  172. for (int i = notes.size(); --i >= 0;)
  173. {
  174. MPENote& note = notes.getReference (i);
  175. if (zone->isUsingChannelAsNoteChannel (note.midiChannel))
  176. {
  177. note.keyState = MPENote::off;
  178. note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
  179. listeners.call (&MPEInstrument::Listener::noteReleased, note);
  180. notes.remove (i);
  181. }
  182. }
  183. }
  184. }
  185. //==============================================================================
  186. void MPEInstrument::handlePressureMSB (int midiChannel, int value) noexcept
  187. {
  188. const uint8 lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1];
  189. pressure (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value)
  190. : MPEValue::from14BitInt (lsb + (value << 7)));
  191. }
  192. void MPEInstrument::handlePressureLSB (int midiChannel, int value) noexcept
  193. {
  194. lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
  195. }
  196. void MPEInstrument::handleTimbreMSB (int midiChannel, int value) noexcept
  197. {
  198. const uint8 lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1];
  199. timbre (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value)
  200. : MPEValue::from14BitInt (lsb + (value << 7)));
  201. }
  202. void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept
  203. {
  204. lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
  205. }
  206. //==============================================================================
  207. MPEValue MPEInstrument::getInitialPitchbendForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const
  208. {
  209. return pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1];
  210. }
  211. MPEValue MPEInstrument::getInitialPressureForNoteOn (int /*midiChannel*/, int /*midiNoteNumber*/, MPEValue midiNoteOnVelocity) const
  212. {
  213. return midiNoteOnVelocity;
  214. }
  215. MPEValue MPEInstrument::getInitialTimbreForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const
  216. {
  217. return timbreDimension.lastValueReceivedOnChannel[midiChannel - 1];
  218. }
  219. //==============================================================================
  220. void MPEInstrument::noteOn (int midiChannel,
  221. int midiNoteNumber,
  222. MPEValue midiNoteOnVelocity)
  223. {
  224. if (! isNoteChannel (midiChannel) && ! omniMode.isEnabled)
  225. return;
  226. MPENote newNote (midiChannel,
  227. midiNoteNumber,
  228. midiNoteOnVelocity,
  229. getInitialPitchbendForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
  230. getInitialPressureForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
  231. getInitialTimbreForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
  232. isNoteChannelSustained[midiChannel - 1] ? MPENote::keyDownAndSustained : MPENote::keyDown);
  233. const ScopedLock sl (lock);
  234. updateNoteTotalPitchbend (newNote);
  235. if (MPENote* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber))
  236. {
  237. // pathological case: second note-on received for same note -> retrigger it
  238. alreadyPlayingNote->keyState = MPENote::off;
  239. alreadyPlayingNote->noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
  240. listeners.call (&MPEInstrument::Listener::noteReleased, *alreadyPlayingNote);
  241. notes.remove (alreadyPlayingNote);
  242. }
  243. notes.add (newNote);
  244. listeners.call (&MPEInstrument::Listener::noteAdded, newNote);
  245. }
  246. //==============================================================================
  247. void MPEInstrument::noteOff (int midiChannel,
  248. int midiNoteNumber,
  249. MPEValue midiNoteOffVelocity)
  250. {
  251. if (notes.empty() || (! isNoteChannel (midiChannel) && ! omniMode.isEnabled))
  252. return;
  253. const ScopedLock sl (lock);
  254. if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber))
  255. {
  256. note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off;
  257. note->noteOffVelocity = midiNoteOffVelocity;
  258. // last pitchbend and timbre values received for this note should not be re-used for
  259. // any new notes, so reset them:
  260. pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue();
  261. timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue();
  262. if (note->keyState == MPENote::off)
  263. {
  264. listeners.call (&MPEInstrument::Listener::noteReleased, *note);
  265. notes.remove (note);
  266. }
  267. else
  268. {
  269. listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, *note);
  270. }
  271. }
  272. }
  273. //==============================================================================
  274. void MPEInstrument::pitchbend (int midiChannel, MPEValue value)
  275. {
  276. const ScopedLock sl (lock);
  277. updateDimension (midiChannel, pitchbendDimension, value);
  278. }
  279. void MPEInstrument::pressure (int midiChannel, MPEValue value)
  280. {
  281. const ScopedLock sl (lock);
  282. updateDimension (midiChannel, pressureDimension, value);
  283. }
  284. void MPEInstrument::timbre (int midiChannel, MPEValue value)
  285. {
  286. const ScopedLock sl (lock);
  287. updateDimension (midiChannel, timbreDimension, value);
  288. }
  289. //==============================================================================
  290. void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, MPEValue value)
  291. {
  292. dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
  293. if (notes.empty())
  294. return;
  295. if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (midiChannel))
  296. {
  297. updateDimensionMaster (*zone, dimension, value);
  298. }
  299. else if (isNoteChannel (midiChannel) || omniMode.isEnabled)
  300. {
  301. if (dimension.trackingMode == allNotesOnChannel)
  302. {
  303. for (int i = notes.size(); --i >= 0;)
  304. {
  305. MPENote& note = notes.getReference (i);
  306. if (note.midiChannel == midiChannel)
  307. updateDimensionForNote (note, dimension, value);
  308. }
  309. }
  310. else
  311. {
  312. if (MPENote* note = getNotePtr (midiChannel, dimension.trackingMode))
  313. updateDimensionForNote (*note, dimension, value);
  314. }
  315. }
  316. }
  317. //==============================================================================
  318. void MPEInstrument::updateDimensionMaster (MPEZone& zone, MPEDimension& dimension, MPEValue value)
  319. {
  320. const Range<int> channels (zone.getNoteChannelRange());
  321. for (int i = notes.size(); --i >= 0;)
  322. {
  323. MPENote& note = notes.getReference (i);
  324. if (! channels.contains (note.midiChannel))
  325. continue;
  326. if (&dimension == &pitchbendDimension)
  327. {
  328. // master pitchbend is a special case: we don't change the note's own pitchbend,
  329. // instead we have to update its total (master + note) pitchbend.
  330. updateNoteTotalPitchbend (note);
  331. listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note);
  332. }
  333. else if (dimension.getValue (note) != value)
  334. {
  335. dimension.getValue (note) = value;
  336. callListenersDimensionChanged (note, dimension);
  337. }
  338. }
  339. }
  340. //==============================================================================
  341. void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value)
  342. {
  343. if (dimension.getValue (note) != value)
  344. {
  345. dimension.getValue (note) = value;
  346. if (&dimension == &pitchbendDimension)
  347. updateNoteTotalPitchbend (note);
  348. callListenersDimensionChanged (note, dimension);
  349. }
  350. }
  351. //==============================================================================
  352. void MPEInstrument::callListenersDimensionChanged (MPENote& note, MPEDimension& dimension)
  353. {
  354. if (&dimension == &pressureDimension) { listeners.call (&MPEInstrument::Listener::notePressureChanged, note); return; }
  355. if (&dimension == &timbreDimension) { listeners.call (&MPEInstrument::Listener::noteTimbreChanged, note); return; }
  356. if (&dimension == &pitchbendDimension) { listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note); return; }
  357. }
  358. //==============================================================================
  359. void MPEInstrument::updateNoteTotalPitchbend (MPENote& note)
  360. {
  361. if (omniMode.isEnabled)
  362. {
  363. note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * omniMode.pitchbendRange;
  364. }
  365. else
  366. {
  367. if (MPEZone* zone = zoneLayout.getZoneByNoteChannel (note.midiChannel))
  368. {
  369. double notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone->getPerNotePitchbendRange();
  370. double masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone->getMasterChannel() - 1].asSignedFloat() * zone->getMasterPitchbendRange();
  371. note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones;
  372. }
  373. else
  374. {
  375. // oops - this note seems to not belong to any zone!
  376. jassertfalse;
  377. }
  378. }
  379. }
  380. //==============================================================================
  381. void MPEInstrument::sustainPedal (int midiChannel, bool isDown)
  382. {
  383. const ScopedLock sl (lock);
  384. handleSustainOrSostenuto (midiChannel, isDown, false);
  385. }
  386. void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown)
  387. {
  388. const ScopedLock sl (lock);
  389. handleSustainOrSostenuto (midiChannel, isDown, true);
  390. }
  391. //==============================================================================
  392. void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto)
  393. {
  394. MPEZone* affectedZone = zoneLayout.getZoneByMasterChannel (midiChannel);
  395. if (affectedZone == nullptr && ! omniMode.isEnabled)
  396. return;
  397. for (int i = notes.size(); --i >= 0;)
  398. {
  399. MPENote& note = notes.getReference (i);
  400. if ((omniMode.isEnabled
  401. || note.midiChannel == midiChannel)
  402. || affectedZone->isUsingChannel (note.midiChannel))
  403. {
  404. if (note.keyState == MPENote::keyDown && isDown)
  405. note.keyState = MPENote::keyDownAndSustained;
  406. else if (note.keyState == MPENote::sustained && ! isDown)
  407. note.keyState = MPENote::off;
  408. else if (note.keyState == MPENote::keyDownAndSustained && ! isDown)
  409. note.keyState = MPENote::keyDown;
  410. if (note.keyState == MPENote::off)
  411. {
  412. listeners.call (&MPEInstrument::Listener::noteReleased, note);
  413. notes.remove (i);
  414. }
  415. else
  416. {
  417. listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, note);
  418. }
  419. }
  420. }
  421. if (! isSostenuto)
  422. {
  423. if (omniMode.isEnabled)
  424. isNoteChannelSustained[midiChannel - 1] = isDown;
  425. else
  426. for (int i = affectedZone->getFirstNoteChannel(); i <= affectedZone->getLastNoteChannel(); ++i)
  427. isNoteChannelSustained[i - 1] = isDown;
  428. }
  429. }
  430. //==============================================================================
  431. bool MPEInstrument::isNoteChannel (int midiChannel) const noexcept
  432. {
  433. return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr;
  434. }
  435. bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept
  436. {
  437. return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr;
  438. }
  439. //==============================================================================
  440. int MPEInstrument::getNumPlayingNotes() const noexcept
  441. {
  442. return notes.size();
  443. }
  444. MPENote MPEInstrument::getNote (int midiChannel, int midiNoteNumber) const noexcept
  445. {
  446. if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber))
  447. return *note;
  448. return MPENote();
  449. }
  450. MPENote MPEInstrument::getNote (int index) const noexcept
  451. {
  452. return notes[index];
  453. }
  454. //==============================================================================
  455. MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept
  456. {
  457. if (MPENote* note = getLastNotePlayedPtr (midiChannel))
  458. return *note;
  459. return MPENote();
  460. }
  461. MPENote MPEInstrument::getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept
  462. {
  463. for (int i = notes.size(); --i >= 0;)
  464. {
  465. const MPENote& note = notes.getReference (i);
  466. if (note != otherThanThisNote)
  467. return note;
  468. }
  469. return MPENote();
  470. }
  471. //==============================================================================
  472. MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) const noexcept
  473. {
  474. for (int i = 0; i < notes.size(); ++i)
  475. {
  476. MPENote& note = notes.getReference (i);
  477. if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber)
  478. return &note;
  479. }
  480. return nullptr;
  481. }
  482. //==============================================================================
  483. MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) const noexcept
  484. {
  485. // for the "all notes" tracking mode, this method can never possibly
  486. // work because it returns 0 or 1 note but there might be more than one!
  487. jassert (mode != allNotesOnChannel);
  488. if (mode == lastNotePlayedOnChannel) return getLastNotePlayedPtr (midiChannel);
  489. if (mode == lowestNoteOnChannel) return getLowestNotePtr (midiChannel);
  490. if (mode == highestNoteOnChannel) return getHighestNotePtr (midiChannel);
  491. return nullptr;
  492. }
  493. //==============================================================================
  494. MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept
  495. {
  496. for (int i = notes.size(); --i >= 0;)
  497. {
  498. MPENote& note = notes.getReference (i);
  499. if (note.midiChannel == midiChannel
  500. && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained))
  501. return &note;
  502. }
  503. return nullptr;
  504. }
  505. //==============================================================================
  506. MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) const noexcept
  507. {
  508. int initialNoteMax = -1;
  509. MPENote* result = nullptr;
  510. for (int i = notes.size(); --i >= 0;)
  511. {
  512. MPENote& note = notes.getReference (i);
  513. if (note.midiChannel == midiChannel
  514. && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)
  515. && note.initialNote > initialNoteMax)
  516. {
  517. result = &note;
  518. initialNoteMax = note.initialNote;
  519. }
  520. }
  521. return result;
  522. }
  523. MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) const noexcept
  524. {
  525. int initialNoteMin = 128;
  526. MPENote* result = nullptr;
  527. for (int i = notes.size(); --i >= 0;)
  528. {
  529. MPENote& note = notes.getReference (i);
  530. if (note.midiChannel == midiChannel
  531. && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)
  532. && note.initialNote < initialNoteMin)
  533. {
  534. result = &note;
  535. initialNoteMin = note.initialNote;
  536. }
  537. }
  538. return result;
  539. }
  540. //==============================================================================
  541. void MPEInstrument::releaseAllNotes()
  542. {
  543. const ScopedLock sl (lock);
  544. for (int i = notes.size(); --i >= 0;)
  545. {
  546. MPENote& note = notes.getReference (i);
  547. note.keyState = MPENote::off;
  548. note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
  549. listeners.call (&MPEInstrument::Listener::noteReleased, note);
  550. }
  551. notes.clear();
  552. }
  553. //==============================================================================
  554. //==============================================================================
  555. #if JUCE_UNIT_TESTS
  556. class MPEInstrumentTests : public UnitTest
  557. {
  558. public:
  559. MPEInstrumentTests()
  560. : UnitTest ("MPEInstrument class")
  561. {
  562. // using two MPE zones with the following layout for testing
  563. //
  564. // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
  565. // * ...................| * ........................|
  566. testLayout.addZone (MPEZone (2, 5));
  567. testLayout.addZone (MPEZone (9, 6));
  568. }
  569. void runTest() override
  570. {
  571. beginTest ("initial zone layout");
  572. {
  573. MPEInstrument test;
  574. expectEquals (test.getZoneLayout().getNumZones(), 0);
  575. }
  576. beginTest ("get/setZoneLayout");
  577. {
  578. MPEInstrument test;
  579. test.setZoneLayout (testLayout);
  580. MPEZoneLayout newLayout = test.getZoneLayout();
  581. expectEquals (newLayout.getNumZones(), 2);
  582. expectEquals (newLayout.getZoneByIndex (0)->getMasterChannel(), 2);
  583. expectEquals (newLayout.getZoneByIndex (0)->getNumNoteChannels(), 5);
  584. expectEquals (newLayout.getZoneByIndex (1)->getMasterChannel(), 9);
  585. expectEquals (newLayout.getZoneByIndex (1)->getNumNoteChannels(), 6);
  586. }
  587. beginTest ("noteOn / noteOff");
  588. {
  589. {
  590. MPEInstrument test;
  591. test.setZoneLayout (testLayout);
  592. expectEquals (test.getNumPlayingNotes(), 0);
  593. }
  594. {
  595. UnitTestInstrument test;
  596. test.setZoneLayout (testLayout);
  597. // note-on on master channel - ignore
  598. test.noteOn (9, 60, MPEValue::from7BitInt (100));
  599. expectEquals (test.getNumPlayingNotes(), 0);
  600. expectEquals (test.noteAddedCallCounter, 0);
  601. // note-on on any other channel - ignore
  602. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  603. expectEquals (test.getNumPlayingNotes(), 0);
  604. expectEquals (test.noteAddedCallCounter, 0);
  605. // note-on on note channel - create new note
  606. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  607. expectEquals (test.getNumPlayingNotes(), 1);
  608. expectEquals (test.noteAddedCallCounter, 1);
  609. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  610. // note-off
  611. test.noteOff (3, 60, MPEValue::from7BitInt (33));
  612. expectEquals (test.getNumPlayingNotes(), 0);
  613. expectEquals (test.noteReleasedCallCounter, 1);
  614. expectHasFinishedNote (test, 3, 60, 33);
  615. }
  616. {
  617. UnitTestInstrument test;
  618. test.setZoneLayout (testLayout);
  619. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  620. // note off with non-matching note number shouldn't do anything
  621. test.noteOff (3, 61, MPEValue::from7BitInt (33));
  622. expectEquals (test.getNumPlayingNotes(), 1);
  623. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  624. expectEquals (test.noteReleasedCallCounter, 0);
  625. // note off with non-matching midi channel shouldn't do anything
  626. test.noteOff (2, 60, MPEValue::from7BitInt (33));
  627. expectEquals (test.getNumPlayingNotes(), 1);
  628. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  629. expectEquals (test.noteReleasedCallCounter, 0);
  630. }
  631. {
  632. // can have multiple notes on the same channel
  633. UnitTestInstrument test;
  634. test.setZoneLayout (testLayout);
  635. test.noteOn (3, 0, MPEValue::from7BitInt (100));
  636. test.noteOn (3, 1, MPEValue::from7BitInt (100));
  637. test.noteOn (3, 2, MPEValue::from7BitInt (100));
  638. expectEquals (test.getNumPlayingNotes(), 3);
  639. expectNote (test.getNote (3, 0), 100, 100, 8192, 64, MPENote::keyDown);
  640. expectNote (test.getNote (3, 1), 100, 100, 8192, 64, MPENote::keyDown);
  641. expectNote (test.getNote (3, 2), 100, 100, 8192, 64, MPENote::keyDown);
  642. }
  643. {
  644. // pathological case: second note-on for same note should retrigger it.
  645. UnitTestInstrument test;
  646. test.setZoneLayout (testLayout);
  647. test.noteOn (3, 0, MPEValue::from7BitInt (100));
  648. test.noteOn (3, 0, MPEValue::from7BitInt (60));
  649. expectEquals (test.getNumPlayingNotes(), 1);
  650. expectNote (test.getNote (3, 0), 60, 60, 8192, 64, MPENote::keyDown);
  651. }
  652. }
  653. beginTest ("noteReleased after setZoneLayout");
  654. {
  655. UnitTestInstrument test;
  656. test.setZoneLayout (testLayout);
  657. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  658. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  659. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  660. expectEquals (test.getNumPlayingNotes(), 3);
  661. expectEquals (test.noteReleasedCallCounter, 0);
  662. test.setZoneLayout (testLayout);
  663. expectEquals (test.getNumPlayingNotes(), 0);
  664. expectEquals (test.noteReleasedCallCounter, 3);
  665. }
  666. beginTest ("releaseAllNotes");
  667. {
  668. UnitTestInstrument test;
  669. test.setZoneLayout (testLayout);
  670. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  671. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  672. test.noteOn (15, 62, MPEValue::from7BitInt (100));
  673. expectEquals (test.getNumPlayingNotes(), 3);
  674. test.releaseAllNotes();
  675. expectEquals (test.getNumPlayingNotes(), 0);
  676. }
  677. beginTest ("sustainPedal");
  678. {
  679. UnitTestInstrument test;
  680. test.setZoneLayout (testLayout);
  681. test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1
  682. test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2
  683. // sustain pedal on per-note channel shouldn't do anything.
  684. test.sustainPedal (3, true);
  685. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  686. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  687. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  688. expectEquals (test.noteKeyStateChangedCallCounter, 0);
  689. // sustain pedal on non-zone channel shouldn't do anything either.
  690. test.sustainPedal (1, true);
  691. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  692. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  693. expectEquals (test.noteKeyStateChangedCallCounter, 0);
  694. // sustain pedal on master channel should sustain notes on *that* zone.
  695. test.sustainPedal (2, true);
  696. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
  697. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  698. expectEquals (test.noteKeyStateChangedCallCounter, 1);
  699. // release
  700. test.sustainPedal (2, false);
  701. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  702. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  703. expectEquals (test.noteKeyStateChangedCallCounter, 2);
  704. // should also sustain new notes added after the press
  705. test.sustainPedal (2, true);
  706. expectEquals (test.noteKeyStateChangedCallCounter, 3);
  707. test.noteOn (4, 51, MPEValue::from7BitInt (100));
  708. expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
  709. expectEquals (test.noteKeyStateChangedCallCounter, 3);
  710. // ...but only if that sustain came on the master channel of that zone!
  711. test.sustainPedal (11, true);
  712. test.noteOn (11, 52, MPEValue::from7BitInt (100));
  713. expectNote (test.getNote (11, 52), 100, 100, 8192, 64, MPENote::keyDown);
  714. test.noteOff (11, 52, MPEValue::from7BitInt (100));
  715. expectEquals (test.noteReleasedCallCounter, 1);
  716. // note-off should not turn off sustained notes inside the same zone
  717. test.noteOff (3, 60, MPEValue::from7BitInt (100));
  718. test.noteOff (4, 51, MPEValue::from7BitInt (100));
  719. test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected!
  720. expectEquals (test.getNumPlayingNotes(), 2);
  721. expectEquals (test.noteReleasedCallCounter, 2);
  722. expectEquals (test.noteKeyStateChangedCallCounter, 5);
  723. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
  724. expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::sustained);
  725. // notes should be turned off when pedal is released
  726. test.sustainPedal (2, false);
  727. expectEquals (test.getNumPlayingNotes(), 0);
  728. expectEquals (test.noteReleasedCallCounter, 4);
  729. }
  730. beginTest ("sostenutoPedal");
  731. {
  732. UnitTestInstrument test;
  733. test.setZoneLayout (testLayout);
  734. test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1
  735. test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2
  736. // sostenuto pedal on per-note channel shouldn't do anything.
  737. test.sostenutoPedal (3, true);
  738. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  739. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  740. expectEquals (test.noteKeyStateChangedCallCounter, 0);
  741. // sostenuto pedal on non-zone channel shouldn't do anything either.
  742. test.sostenutoPedal (1, true);
  743. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  744. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  745. expectEquals (test.noteKeyStateChangedCallCounter, 0);
  746. // sostenuto pedal on master channel should sustain notes on *that* zone.
  747. test.sostenutoPedal (2, true);
  748. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
  749. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  750. expectEquals (test.noteKeyStateChangedCallCounter, 1);
  751. // release
  752. test.sostenutoPedal (2, false);
  753. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  754. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  755. expectEquals (test.noteKeyStateChangedCallCounter, 2);
  756. // should only sustain notes turned on *before* the press (difference to sustain pedal)
  757. test.sostenutoPedal (2, true);
  758. expectEquals (test.noteKeyStateChangedCallCounter, 3);
  759. test.noteOn (4, 51, MPEValue::from7BitInt (100));
  760. expectEquals (test.getNumPlayingNotes(), 3);
  761. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
  762. expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDown);
  763. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  764. expectEquals (test.noteKeyStateChangedCallCounter, 3);
  765. // note-off should not turn off sustained notes inside the same zone,
  766. // but only if they were turned on *before* the sostenuto pedal (difference to sustain pedal)
  767. test.noteOff (3, 60, MPEValue::from7BitInt (100));
  768. test.noteOff (4, 51, MPEValue::from7BitInt (100));
  769. test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected!
  770. expectEquals (test.getNumPlayingNotes(), 1);
  771. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
  772. expectEquals (test.noteReleasedCallCounter, 2);
  773. expectEquals (test.noteKeyStateChangedCallCounter, 4);
  774. // notes should be turned off when pedal is released
  775. test.sustainPedal (2, false);
  776. expectEquals (test.getNumPlayingNotes(), 0);
  777. expectEquals (test.noteReleasedCallCounter, 3);
  778. }
  779. beginTest ("getMostRecentNote");
  780. {
  781. MPEInstrument test;
  782. test.setZoneLayout (testLayout);
  783. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  784. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  785. {
  786. MPENote note = test.getMostRecentNote (2);
  787. expect (! note.isValid());
  788. }
  789. {
  790. MPENote note = test.getMostRecentNote (3);
  791. expect (note.isValid());
  792. expectEquals (int (note.midiChannel), 3);
  793. expectEquals (int (note.initialNote), 61);
  794. }
  795. test.sustainPedal (2, true);
  796. test.noteOff (3, 61, MPEValue::from7BitInt (100));
  797. {
  798. MPENote note = test.getMostRecentNote (3);
  799. expect (note.isValid());
  800. expectEquals (int (note.midiChannel), 3);
  801. expectEquals (int (note.initialNote), 60);
  802. }
  803. test.sustainPedal (2, false);
  804. test.noteOff (3, 60, MPEValue::from7BitInt (100));
  805. {
  806. MPENote note = test.getMostRecentNote (3);
  807. expect (! note.isValid());
  808. }
  809. }
  810. beginTest ("getMostRecentNoteOtherThan");
  811. {
  812. MPENote testNote (3, 60,
  813. MPEValue::centreValue(), MPEValue::centreValue(),
  814. MPEValue::centreValue(), MPEValue::centreValue());
  815. {
  816. // case 1: the note to exclude is not the most recent one.
  817. MPEInstrument test;
  818. test.setZoneLayout (testLayout);
  819. expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
  820. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  821. expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
  822. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  823. expect (test.getMostRecentNoteOtherThan (testNote).isValid());
  824. expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
  825. expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
  826. }
  827. {
  828. // case 2: the note to exclude is the most recent one.
  829. MPEInstrument test;
  830. test.setZoneLayout (testLayout);
  831. expect (! test.getMostRecentNoteOtherThan (testNote).isValid());
  832. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  833. expect (test.getMostRecentNoteOtherThan (testNote).isValid());
  834. expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
  835. expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
  836. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  837. expect (test.getMostRecentNoteOtherThan (testNote).isValid());
  838. expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4);
  839. expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61);
  840. }
  841. }
  842. beginTest ("pressure");
  843. {
  844. {
  845. UnitTestInstrument test;
  846. test.setZoneLayout (testLayout);
  847. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  848. test.noteOn (4, 60, MPEValue::from7BitInt (100));
  849. test.noteOn (10, 60, MPEValue::from7BitInt (100));
  850. // applying pressure on a per-note channel should modulate one note
  851. test.pressure (3, MPEValue::from7BitInt (33));
  852. expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown);
  853. expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
  854. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  855. expectEquals (test.notePressureChangedCallCounter, 1);
  856. // applying pressure on a master channel should modulate all notes in this zone
  857. test.pressure (2, MPEValue::from7BitInt (44));
  858. expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
  859. expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
  860. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  861. expectEquals (test.notePressureChangedCallCounter, 3);
  862. // applying pressure on an unrelated channel should be ignored
  863. test.pressure (1, MPEValue::from7BitInt (55));
  864. expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
  865. expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
  866. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  867. expectEquals (test.notePressureChangedCallCounter, 3);
  868. }
  869. {
  870. UnitTestInstrument test;
  871. test.setZoneLayout (testLayout);
  872. // two notes on same channel - only last added should be modulated
  873. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  874. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  875. test.pressure (3, MPEValue::from7BitInt (66));
  876. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  877. expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown);
  878. expectEquals (test.notePressureChangedCallCounter, 1);
  879. }
  880. {
  881. UnitTestInstrument test;
  882. test.setZoneLayout (testLayout);
  883. // edge case: two notes on same channel, one gets released,
  884. // then the other should be modulated
  885. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  886. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  887. test.noteOff (3, 61, MPEValue::from7BitInt (100));
  888. test.pressure (3, MPEValue::from7BitInt (77));
  889. expectEquals (test.getNumPlayingNotes(), 1);
  890. expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown);
  891. expectEquals (test.notePressureChangedCallCounter, 1);
  892. }
  893. }
  894. beginTest ("pitchbend");
  895. {
  896. {
  897. UnitTestInstrument test;
  898. test.setZoneLayout (testLayout);
  899. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  900. test.noteOn (4, 60, MPEValue::from7BitInt (100));
  901. test.noteOn (10, 60, MPEValue::from7BitInt (100));
  902. // applying pitchbend on a per-note channel should modulate one note
  903. test.pitchbend (3, MPEValue::from14BitInt (1111));
  904. expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
  905. expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
  906. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  907. expectEquals (test.notePitchbendChangedCallCounter, 1);
  908. // applying pitchbend on a master channel should be ignored for the
  909. // value of per-note pitchbend. Tests covering master pitchbend below.
  910. // Note: noteChanged will be called anyway for notes in that zone
  911. // because the total pitchbend for those notes has changed
  912. test.pitchbend (2, MPEValue::from14BitInt (2222));
  913. expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
  914. expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
  915. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  916. expectEquals (test.notePitchbendChangedCallCounter, 3);
  917. // applying pitchbend on an unrelated channel should do nothing.
  918. test.pitchbend (1, MPEValue::from14BitInt (3333));
  919. expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
  920. expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
  921. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  922. expectEquals (test.notePitchbendChangedCallCounter, 3);
  923. }
  924. {
  925. UnitTestInstrument test;
  926. test.setZoneLayout (testLayout);
  927. // two notes on same channel - only last added should be bent
  928. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  929. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  930. test.pitchbend (3, MPEValue::from14BitInt (4444));
  931. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  932. expectNote (test.getNote (3, 61), 100, 100, 4444, 64, MPENote::keyDown);
  933. expectEquals (test.notePitchbendChangedCallCounter, 1);
  934. }
  935. {
  936. UnitTestInstrument test;
  937. test.setZoneLayout (testLayout);
  938. // edge case: two notes on same channel, one gets released,
  939. // then the other should be bent
  940. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  941. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  942. test.noteOff (3, 61, MPEValue::from7BitInt (100));
  943. test.pitchbend (3, MPEValue::from14BitInt (5555));
  944. expectEquals (test.getNumPlayingNotes(), 1);
  945. expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown);
  946. expectEquals (test.notePitchbendChangedCallCounter, 1);
  947. }
  948. {
  949. UnitTestInstrument test;
  950. test.setZoneLayout (testLayout);
  951. // Richard's edge case:
  952. // - press one note
  953. // - press sustain (careful: must be sent on master channel)
  954. // - release first note (is still sustained!)
  955. // - press another note (happens to be on the same MIDI channel!)
  956. // - pitchbend that other note
  957. // - the first note should not be bent, only the second one.
  958. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  959. test.sustainPedal (2, true);
  960. test.noteOff (3, 60, MPEValue::from7BitInt (64));
  961. expectEquals (test.getNumPlayingNotes(), 1);
  962. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
  963. expectEquals (test.noteKeyStateChangedCallCounter, 2);
  964. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  965. test.pitchbend (3, MPEValue::from14BitInt (6666));
  966. expectEquals (test.getNumPlayingNotes(), 2);
  967. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
  968. expectNote (test.getNote (3, 61), 100, 100, 6666, 64, MPENote::keyDownAndSustained);
  969. expectEquals (test.notePitchbendChangedCallCounter, 1);
  970. }
  971. {
  972. UnitTestInstrument test;
  973. test.setZoneLayout (testLayout);
  974. // Zsolt's edge case:
  975. // - press one note
  976. // - modulate pitchbend or timbre
  977. // - release the note
  978. // - press same note again without sending a pitchbend or timbre message before the note-on
  979. // - the note should be turned on with a default value for pitchbend/timbre,
  980. // and *not* the last value received on channel.
  981. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  982. test.pitchbend (3, MPEValue::from14BitInt (5555));
  983. expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown);
  984. test.noteOff (3, 60, MPEValue::from7BitInt (100));
  985. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  986. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  987. }
  988. {
  989. // applying per-note pitchbend should set the note's totalPitchbendInSemitones
  990. // correctly depending on the per-note pitchbend range of the zone.
  991. UnitTestInstrument test;
  992. MPEZoneLayout layout = testLayout;
  993. test.setZoneLayout (layout); // default should be +/- 48 semitones
  994. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  995. test.pitchbend (3, MPEValue::from14BitInt (4096));
  996. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01);
  997. layout.getZoneByIndex (0)->setPerNotePitchbendRange (96);
  998. test.setZoneLayout (layout);
  999. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1000. test.pitchbend (3, MPEValue::from14BitInt (0)); // -max
  1001. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
  1002. layout.getZoneByIndex (0)->setPerNotePitchbendRange (1);
  1003. test.setZoneLayout (layout);
  1004. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1005. test.pitchbend (3, MPEValue::from14BitInt (16383)); // +max
  1006. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
  1007. layout.getZoneByIndex (0)->setPerNotePitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all
  1008. test.setZoneLayout (layout);
  1009. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1010. test.pitchbend (3, MPEValue::from14BitInt (12345));
  1011. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
  1012. }
  1013. {
  1014. // applying master pitchbend should set the note's totalPitchbendInSemitones
  1015. // correctly depending on the master pitchbend range of the zone.
  1016. UnitTestInstrument test;
  1017. MPEZoneLayout layout = testLayout;
  1018. test.setZoneLayout (layout); // default should be +/- 2 semitones
  1019. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1020. test.pitchbend (2, MPEValue::from14BitInt (4096)); //halfway between -max and centre
  1021. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01);
  1022. layout.getZoneByIndex (0)->setMasterPitchbendRange (96);
  1023. test.setZoneLayout (layout);
  1024. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1025. test.pitchbend (2, MPEValue::from14BitInt (0)); // -max
  1026. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01);
  1027. layout.getZoneByIndex (0)->setMasterPitchbendRange (1);
  1028. test.setZoneLayout (layout);
  1029. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1030. test.pitchbend (2, MPEValue::from14BitInt (16383)); // +max
  1031. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01);
  1032. layout.getZoneByIndex (0)->setMasterPitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all
  1033. test.setZoneLayout (layout);
  1034. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1035. test.pitchbend (2, MPEValue::from14BitInt (12345));
  1036. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01);
  1037. }
  1038. {
  1039. // applying both per-note and master pitchbend simultaneously should set
  1040. // the note's totalPitchbendInSemitones to the sum of both, correctly
  1041. // weighted with the per-note and master pitchbend range, respectively.
  1042. UnitTestInstrument test;
  1043. MPEZoneLayout layout = testLayout;
  1044. layout.getZoneByIndex (0)->setPerNotePitchbendRange (12);
  1045. layout.getZoneByIndex (0)->setMasterPitchbendRange (1);
  1046. test.setZoneLayout (layout);
  1047. test.pitchbend (2, MPEValue::from14BitInt (4096)); // master pitchbend 0.5 semitones down
  1048. test.pitchbend (3, MPEValue::from14BitInt (0)); // per-note pitchbend 12 semitones down
  1049. // additionally, note should react to both pitchbend messages
  1050. // correctly even if they arrived before the note-on.
  1051. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1052. expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01);
  1053. }
  1054. }
  1055. beginTest ("timbre");
  1056. {
  1057. {
  1058. UnitTestInstrument test;
  1059. test.setZoneLayout (testLayout);
  1060. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1061. test.noteOn (4, 60, MPEValue::from7BitInt (100));
  1062. test.noteOn (10, 60, MPEValue::from7BitInt (100));
  1063. // modulating timbre on a per-note channel should modulate one note
  1064. test.timbre (3, MPEValue::from7BitInt (33));
  1065. expectNote (test.getNote (3, 60), 100, 100, 8192, 33, MPENote::keyDown);
  1066. expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1067. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1068. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1069. // modulating timbre on a master channel should modulate all notes in this zone
  1070. test.timbre (2, MPEValue::from7BitInt (44));
  1071. expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown);
  1072. expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown);
  1073. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1074. expectEquals (test.noteTimbreChangedCallCounter, 3);
  1075. // modulating timbre on an unrelated channel should be ignored
  1076. test.timbre (1, MPEValue::from7BitInt (55));
  1077. expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown);
  1078. expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown);
  1079. expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1080. expectEquals (test.noteTimbreChangedCallCounter, 3);
  1081. }
  1082. {
  1083. UnitTestInstrument test;
  1084. test.setZoneLayout (testLayout);
  1085. // two notes on same channel - only last added should be modulated
  1086. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1087. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1088. test.timbre (3, MPEValue::from7BitInt (66));
  1089. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1090. expectNote (test.getNote (3, 61), 100, 100, 8192, 66, MPENote::keyDown);
  1091. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1092. }
  1093. {
  1094. UnitTestInstrument test;
  1095. test.setZoneLayout (testLayout);
  1096. // edge case: two notes on same channel, one gets released,
  1097. // then the other should be modulated
  1098. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1099. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1100. test.noteOff (3, 61, MPEValue::from7BitInt (100));
  1101. test.timbre (3, MPEValue::from7BitInt (77));
  1102. expectEquals (test.getNumPlayingNotes(), 1);
  1103. expectNote (test.getNote (3, 60), 100, 100, 8192, 77, MPENote::keyDown);
  1104. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1105. }
  1106. {
  1107. UnitTestInstrument test;
  1108. test.setZoneLayout (testLayout);
  1109. // Zsolt's edge case for timbre
  1110. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1111. test.timbre (3, MPEValue::from7BitInt (42));
  1112. expectNote (test.getNote (3, 60), 100, 100, 8192, 42, MPENote::keyDown);
  1113. test.noteOff (3, 60, MPEValue::from7BitInt (100));
  1114. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1115. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1116. }
  1117. }
  1118. beginTest ("setPressureTrackingMode");
  1119. {
  1120. {
  1121. // last note played (= default)
  1122. UnitTestInstrument test;
  1123. test.setZoneLayout (testLayout);
  1124. test.setPressureTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
  1125. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1126. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1127. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1128. test.pressure (3, MPEValue::from7BitInt (99));
  1129. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1130. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1131. expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown);
  1132. expectEquals (test.notePressureChangedCallCounter, 1);
  1133. }
  1134. {
  1135. // lowest note
  1136. UnitTestInstrument test;
  1137. test.setZoneLayout (testLayout);
  1138. test.setPressureTrackingMode (MPEInstrument::lowestNoteOnChannel);
  1139. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1140. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1141. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1142. test.pressure (3, MPEValue::from7BitInt (99));
  1143. expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown);
  1144. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1145. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1146. expectEquals (test.notePressureChangedCallCounter, 1);
  1147. }
  1148. {
  1149. // highest note
  1150. UnitTestInstrument test;
  1151. test.setZoneLayout (testLayout);
  1152. test.setPressureTrackingMode (MPEInstrument::highestNoteOnChannel);
  1153. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1154. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1155. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1156. test.pressure (3, MPEValue::from7BitInt (99));
  1157. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1158. expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown);
  1159. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1160. expectEquals (test.notePressureChangedCallCounter, 1);
  1161. }
  1162. {
  1163. // all notes
  1164. UnitTestInstrument test;
  1165. test.setZoneLayout (testLayout);
  1166. test.setPressureTrackingMode (MPEInstrument::allNotesOnChannel);
  1167. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1168. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1169. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1170. test.pressure (3, MPEValue::from7BitInt (99));
  1171. expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown);
  1172. expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown);
  1173. expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown);
  1174. expectEquals (test.notePressureChangedCallCounter, 3);
  1175. }
  1176. }
  1177. beginTest ("setPitchbendTrackingMode");
  1178. {
  1179. {
  1180. // last note played (= default)
  1181. UnitTestInstrument test;
  1182. test.setZoneLayout (testLayout);
  1183. test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
  1184. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1185. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1186. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1187. test.pitchbend (3, MPEValue::from14BitInt (9999));
  1188. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1189. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1190. expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown);
  1191. expectEquals (test.notePitchbendChangedCallCounter, 1);
  1192. }
  1193. {
  1194. // lowest note
  1195. UnitTestInstrument test;
  1196. test.setZoneLayout (testLayout);
  1197. test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
  1198. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1199. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1200. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1201. test.pitchbend (3, MPEValue::from14BitInt (9999));
  1202. expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown);
  1203. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1204. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1205. expectEquals (test.notePitchbendChangedCallCounter, 1);
  1206. }
  1207. {
  1208. // highest note
  1209. UnitTestInstrument test;
  1210. test.setZoneLayout (testLayout);
  1211. test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
  1212. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1213. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1214. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1215. test.pitchbend (3, MPEValue::from14BitInt (9999));
  1216. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1217. expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown);
  1218. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1219. expectEquals (test.notePitchbendChangedCallCounter, 1);
  1220. }
  1221. {
  1222. // all notes
  1223. UnitTestInstrument test;
  1224. test.setZoneLayout (testLayout);
  1225. test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
  1226. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1227. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1228. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1229. test.pitchbend (3, MPEValue::from14BitInt (9999));
  1230. expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown);
  1231. expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown);
  1232. expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown);
  1233. expectEquals (test.notePitchbendChangedCallCounter, 3);
  1234. }
  1235. }
  1236. beginTest ("setTimbreTrackingMode");
  1237. {
  1238. {
  1239. // last note played (= default)
  1240. UnitTestInstrument test;
  1241. test.setZoneLayout (testLayout);
  1242. test.setTimbreTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
  1243. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1244. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1245. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1246. test.timbre (3, MPEValue::from7BitInt (99));
  1247. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1248. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1249. expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown);
  1250. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1251. }
  1252. {
  1253. // lowest note
  1254. UnitTestInstrument test;
  1255. test.setZoneLayout (testLayout);
  1256. test.setTimbreTrackingMode (MPEInstrument::lowestNoteOnChannel);
  1257. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1258. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1259. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1260. test.timbre (3, MPEValue::from7BitInt (99));
  1261. expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown);
  1262. expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1263. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1264. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1265. }
  1266. {
  1267. // highest note
  1268. UnitTestInstrument test;
  1269. test.setZoneLayout (testLayout);
  1270. test.setTimbreTrackingMode (MPEInstrument::highestNoteOnChannel);
  1271. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1272. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1273. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1274. test.timbre (3, MPEValue::from7BitInt (99));
  1275. expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1276. expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown);
  1277. expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1278. expectEquals (test.noteTimbreChangedCallCounter, 1);
  1279. }
  1280. {
  1281. // all notes
  1282. UnitTestInstrument test;
  1283. test.setZoneLayout (testLayout);
  1284. test.setTimbreTrackingMode (MPEInstrument::allNotesOnChannel);
  1285. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1286. test.noteOn (3, 62, MPEValue::from7BitInt (100));
  1287. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1288. test.timbre (3, MPEValue::from7BitInt (99));
  1289. expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown);
  1290. expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown);
  1291. expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown);
  1292. expectEquals (test.noteTimbreChangedCallCounter, 3);
  1293. }
  1294. }
  1295. beginTest ("processNextMidiEvent");
  1296. {
  1297. UnitTestInstrument test;
  1298. // note on should trigger noteOn method call
  1299. test.processNextMidiEvent (MidiMessage::noteOn (3, 42, uint8 (92)));
  1300. expectEquals (test.noteOnCallCounter, 1);
  1301. expectEquals (test.lastMidiChannelReceived, 3);
  1302. expectEquals (test.lastMidiNoteNumberReceived, 42);
  1303. expectEquals (test.lastMPEValueReceived.as7BitInt(), 92);
  1304. // note off should trigger noteOff method call
  1305. test.processNextMidiEvent (MidiMessage::noteOff (4, 12, uint8 (33)));
  1306. expectEquals (test.noteOffCallCounter, 1);
  1307. expectEquals (test.lastMidiChannelReceived, 4);
  1308. expectEquals (test.lastMidiNoteNumberReceived, 12);
  1309. expectEquals (test.lastMPEValueReceived.as7BitInt(), 33);
  1310. // note on with velocity = 0 should trigger noteOff method call
  1311. // with a note off velocity of 64 (centre value)
  1312. test.processNextMidiEvent (MidiMessage::noteOn (5, 11, uint8 (0)));
  1313. expectEquals (test.noteOffCallCounter, 2);
  1314. expectEquals (test.lastMidiChannelReceived, 5);
  1315. expectEquals (test.lastMidiNoteNumberReceived, 11);
  1316. expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
  1317. // pitchwheel message should trigger pitchbend method call
  1318. test.processNextMidiEvent (MidiMessage::pitchWheel (1, 3333));
  1319. expectEquals (test.pitchbendCallCounter, 1);
  1320. expectEquals (test.lastMidiChannelReceived, 1);
  1321. expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333);
  1322. // pressure using channel pressure message (7-bit value) should
  1323. // trigger pressure method call
  1324. test.processNextMidiEvent (MidiMessage::channelPressureChange (10, 35));
  1325. expectEquals (test.pressureCallCounter, 1);
  1326. expectEquals (test.lastMidiChannelReceived, 10);
  1327. expectEquals (test.lastMPEValueReceived.as7BitInt(), 35);
  1328. // pressure using 14-bit value over CC70 and CC102 should trigger
  1329. // pressure method call after the MSB is sent
  1330. // a) sending only the MSB
  1331. test.processNextMidiEvent (MidiMessage::controllerEvent (3, 70, 120));
  1332. expectEquals (test.pressureCallCounter, 2);
  1333. expectEquals (test.lastMidiChannelReceived, 3);
  1334. expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
  1335. // b) sending LSB and MSB (only the MSB should trigger the call) - per MIDI channel!
  1336. test.processNextMidiEvent (MidiMessage::controllerEvent (4, 102, 121));
  1337. expectEquals (test.pressureCallCounter, 2);
  1338. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 102, 122));
  1339. expectEquals (test.pressureCallCounter, 2);
  1340. test.processNextMidiEvent (MidiMessage::controllerEvent (4, 70, 123));
  1341. expectEquals (test.pressureCallCounter, 3);
  1342. expectEquals (test.lastMidiChannelReceived, 4);
  1343. expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
  1344. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 124));
  1345. expectEquals (test.pressureCallCounter, 4);
  1346. expectEquals (test.lastMidiChannelReceived, 5);
  1347. expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
  1348. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 64));
  1349. expectEquals (test.pressureCallCounter, 5);
  1350. expectEquals (test.lastMidiChannelReceived, 5);
  1351. expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
  1352. // same for timbre 14-bit value over CC74 and CC106
  1353. test.processNextMidiEvent (MidiMessage::controllerEvent (3, 74, 120));
  1354. expectEquals (test.timbreCallCounter, 1);
  1355. expectEquals (test.lastMidiChannelReceived, 3);
  1356. expectEquals (test.lastMPEValueReceived.as7BitInt(), 120);
  1357. test.processNextMidiEvent (MidiMessage::controllerEvent (4, 106, 121));
  1358. expectEquals (test.timbreCallCounter, 1);
  1359. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 106, 122));
  1360. expectEquals (test.timbreCallCounter, 1);
  1361. test.processNextMidiEvent (MidiMessage::controllerEvent (4, 74, 123));
  1362. expectEquals (test.timbreCallCounter, 2);
  1363. expectEquals (test.lastMidiChannelReceived, 4);
  1364. expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7));
  1365. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 124));
  1366. expectEquals (test.timbreCallCounter, 3);
  1367. expectEquals (test.lastMidiChannelReceived, 5);
  1368. expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7));
  1369. test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 64));
  1370. expectEquals (test.timbreCallCounter, 4);
  1371. expectEquals (test.lastMidiChannelReceived, 5);
  1372. expectEquals (test.lastMPEValueReceived.as7BitInt(), 64);
  1373. // sustain pedal message (CC64) should trigger sustainPedal method call
  1374. test.processNextMidiEvent (MidiMessage::controllerEvent (1, 64, 127));
  1375. expectEquals (test.sustainPedalCallCounter, 1);
  1376. expectEquals (test.lastMidiChannelReceived, 1);
  1377. expect (test.lastSustainPedalValueReceived);
  1378. test.processNextMidiEvent (MidiMessage::controllerEvent (16, 64, 0));
  1379. expectEquals (test.sustainPedalCallCounter, 2);
  1380. expectEquals (test.lastMidiChannelReceived, 16);
  1381. expect (! test.lastSustainPedalValueReceived);
  1382. // sostenuto pedal message (CC66) should trigger sostenutoPedal method call
  1383. test.processNextMidiEvent (MidiMessage::controllerEvent (1, 66, 127));
  1384. expectEquals (test.sostenutoPedalCallCounter, 1);
  1385. expectEquals (test.lastMidiChannelReceived, 1);
  1386. expect (test.lastSostenutoPedalValueReceived);
  1387. test.processNextMidiEvent (MidiMessage::controllerEvent (16, 66, 0));
  1388. expectEquals (test.sostenutoPedalCallCounter, 2);
  1389. expectEquals (test.lastMidiChannelReceived, 16);
  1390. expect (! test.lastSostenutoPedalValueReceived);
  1391. }
  1392. {
  1393. // MIDI messages modifying the zone layout should be correctly
  1394. // forwarded to the internal zone layout and modify it.
  1395. // (testing the actual logic of the zone layout is done in the
  1396. // MPEZoneLayout unit tests)
  1397. MPEInstrument test;
  1398. MidiBuffer buffer;
  1399. buffer.addEvents (MPEMessages::addZone (MPEZone (2, 5)), 0, -1, 0);
  1400. buffer.addEvents (MPEMessages::addZone (MPEZone (9, 6)), 0, -1, 0);
  1401. MidiBuffer::Iterator iter (buffer);
  1402. MidiMessage message;
  1403. int samplePosition; // not actually used, so no need to initialise.
  1404. while (iter.getNextEvent (message, samplePosition))
  1405. test.processNextMidiEvent (message);
  1406. expectEquals (test.getZoneLayout().getNumZones(), 2);
  1407. expectEquals (test.getZoneLayout().getZoneByIndex (0)->getMasterChannel(), 2);
  1408. expectEquals (test.getZoneLayout().getZoneByIndex (0)->getNumNoteChannels(), 5);
  1409. expectEquals (test.getZoneLayout().getZoneByIndex (1)->getMasterChannel(), 9);
  1410. expectEquals (test.getZoneLayout().getZoneByIndex (1)->getNumNoteChannels(), 6);
  1411. }
  1412. beginTest ("MIDI all notes off");
  1413. {
  1414. UnitTestInstrument test;
  1415. test.setZoneLayout (testLayout);
  1416. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1417. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  1418. test.noteOn (15, 62, MPEValue::from7BitInt (100));
  1419. test.noteOn (15, 63, MPEValue::from7BitInt (100));
  1420. expectEquals (test.getNumPlayingNotes(), 4);
  1421. // on note channel: ignore.
  1422. test.processNextMidiEvent (MidiMessage::allNotesOff (3));
  1423. expectEquals (test.getNumPlayingNotes(), 4);
  1424. // on unused channel: ignore.
  1425. test.processNextMidiEvent (MidiMessage::allNotesOff (1));
  1426. expectEquals (test.getNumPlayingNotes(), 4);
  1427. // on master channel: release notes in that zone only.
  1428. test.processNextMidiEvent (MidiMessage::allNotesOff (2));
  1429. expectEquals (test.getNumPlayingNotes(), 2);
  1430. test.processNextMidiEvent (MidiMessage::allNotesOff (9));
  1431. expectEquals (test.getNumPlayingNotes(), 0);
  1432. }
  1433. beginTest ("MIDI all notes off (omni mode)");
  1434. {
  1435. UnitTestInstrument test;
  1436. test.enableOmniMode();
  1437. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1438. test.noteOn (4, 61, MPEValue::from7BitInt (100));
  1439. test.noteOn (15, 62, MPEValue::from7BitInt (100));
  1440. test.noteOn (15, 63, MPEValue::from7BitInt (100));
  1441. expectEquals (test.getNumPlayingNotes(), 4);
  1442. test.processNextMidiEvent (MidiMessage::allNotesOff (3));
  1443. expectEquals (test.getNumPlayingNotes(), 3);
  1444. test.processNextMidiEvent (MidiMessage::allNotesOff (15));
  1445. expectEquals (test.getNumPlayingNotes(), 1);
  1446. test.processNextMidiEvent (MidiMessage::allNotesOff (4));
  1447. expectEquals (test.getNumPlayingNotes(), 0);
  1448. }
  1449. beginTest ("default getInitial...ForNoteOn");
  1450. {
  1451. MPEInstrument test;
  1452. test.setZoneLayout (testLayout);
  1453. test.pitchbend (3, MPEValue::from14BitInt (3333)); // use for next note-on on ch. 3
  1454. test.pitchbend (2, MPEValue::from14BitInt (4444)); // ignore
  1455. test.pitchbend (2, MPEValue::from14BitInt (5555)); // ignore
  1456. test.timbre (3, MPEValue::from7BitInt (66)); // use for next note-on on ch. 3
  1457. test.timbre (2, MPEValue::from7BitInt (77)); // ignore
  1458. test.timbre (2, MPEValue::from7BitInt (88)); // ignore
  1459. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1460. expectNote (test.getMostRecentNote (3), 100, 100, 3333, 66, MPENote::keyDown);
  1461. }
  1462. beginTest ("overriding getInitial...ForNoteOn");
  1463. {
  1464. CustomInitialValuesTest<33, 4444, 55> test;
  1465. test.setZoneLayout (testLayout);
  1466. test.noteOn (3, 61, MPEValue::from7BitInt (100));
  1467. expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown);
  1468. }
  1469. beginTest ("Omni mode");
  1470. {
  1471. {
  1472. // basic check
  1473. MPEInstrument test;
  1474. expect (! test.isOmniModeEnabled());
  1475. test.setZoneLayout (testLayout);
  1476. expect (! test.isOmniModeEnabled());
  1477. test.enableOmniMode();
  1478. expect (test.isOmniModeEnabled());
  1479. test.setZoneLayout (testLayout);
  1480. expect (! test.isOmniModeEnabled());
  1481. }
  1482. {
  1483. // note on should trigger notes on all 16 channels
  1484. UnitTestInstrument test;
  1485. test.enableOmniMode();
  1486. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1487. test.noteOn (2, 60, MPEValue::from7BitInt (100));
  1488. test.noteOn (15, 60, MPEValue::from7BitInt (100));
  1489. test.noteOn (16, 60, MPEValue::from7BitInt (100));
  1490. expectEquals (test.getNumPlayingNotes(), 4);
  1491. // polyphonic modulation should work across all 16 channels
  1492. test.pitchbend (1, MPEValue::from14BitInt (9999));
  1493. test.pressure (2, MPEValue::from7BitInt (88));
  1494. test.timbre (15, MPEValue::from7BitInt (77));
  1495. expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
  1496. expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown);
  1497. expectNote (test.getNote (15, 60), 100, 100, 8192, 77, MPENote::keyDown);
  1498. expectNote (test.getNote (16, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1499. // note off should work in omni mode
  1500. test.noteOff (15, 60, MPEValue::from7BitInt (0));
  1501. test.noteOff (1, 60, MPEValue::from7BitInt (0));
  1502. test.noteOff (2, 60, MPEValue::from7BitInt (0));
  1503. test.noteOff (16, 60, MPEValue::from7BitInt (0));
  1504. expectEquals (test.getNumPlayingNotes(), 0);
  1505. }
  1506. {
  1507. // tracking mode in omni mode
  1508. {
  1509. UnitTestInstrument test;
  1510. test.enableOmniMode();
  1511. test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel);
  1512. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1513. test.noteOn (1, 62, MPEValue::from7BitInt (100));
  1514. test.noteOn (1, 61, MPEValue::from7BitInt (100));
  1515. test.pitchbend (1, MPEValue::from14BitInt (9999));
  1516. expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1517. expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown);
  1518. expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1519. }
  1520. {
  1521. UnitTestInstrument test;
  1522. test.enableOmniMode();
  1523. test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel);
  1524. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1525. test.noteOn (1, 62, MPEValue::from7BitInt (100));
  1526. test.noteOn (1, 61, MPEValue::from7BitInt (100));
  1527. test.pitchbend (1, MPEValue::from14BitInt (9999));
  1528. expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
  1529. expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1530. expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown);
  1531. }
  1532. {
  1533. UnitTestInstrument test;
  1534. test.enableOmniMode();
  1535. test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel);
  1536. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1537. test.noteOn (1, 62, MPEValue::from7BitInt (100));
  1538. test.noteOn (1, 61, MPEValue::from7BitInt (100));
  1539. test.pitchbend (1, MPEValue::from14BitInt (9999));
  1540. expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown);
  1541. expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown);
  1542. expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown);
  1543. }
  1544. {
  1545. UnitTestInstrument test;
  1546. test.enableOmniMode();
  1547. test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel);
  1548. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1549. test.noteOn (1, 62, MPEValue::from7BitInt (100));
  1550. test.noteOn (1, 61, MPEValue::from7BitInt (100));
  1551. test.pitchbend (1, MPEValue::from14BitInt (9999));
  1552. expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
  1553. expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown);
  1554. expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown);
  1555. }
  1556. }
  1557. {
  1558. // custom pitchbend range in omni mode.
  1559. UnitTestInstrument test;
  1560. test.enableOmniMode (11);
  1561. test.pitchbend (1, MPEValue::from14BitInt (4096));
  1562. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1563. expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01);
  1564. }
  1565. {
  1566. // sustain pedal should be per channel in omni mode.
  1567. UnitTestInstrument test;
  1568. test.enableOmniMode();
  1569. test.sustainPedal (1, true);
  1570. test.noteOn (2, 61, MPEValue::from7BitInt (100));
  1571. test.noteOff (2, 61, MPEValue::from7BitInt (100));
  1572. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1573. test.noteOff (1, 60, MPEValue::from7BitInt (100));
  1574. expectEquals (test.getNumPlayingNotes(), 1);
  1575. expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained);
  1576. test.sustainPedal (1, false);
  1577. expectEquals (test.getNumPlayingNotes(), 0);
  1578. }
  1579. {
  1580. // sostenuto pedal should be per channel in omni mode.
  1581. UnitTestInstrument test;
  1582. test.enableOmniMode();
  1583. test.noteOn (1, 60, MPEValue::from7BitInt (100));
  1584. test.sostenutoPedal (1, true);
  1585. test.noteOff (1, 60, MPEValue::from7BitInt (100));
  1586. test.noteOn (2, 61, MPEValue::from7BitInt (100));
  1587. test.noteOff (2, 61, MPEValue::from7BitInt (100));
  1588. expectEquals (test.getNumPlayingNotes(), 1);
  1589. expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained);
  1590. test.sostenutoPedal (1, false);
  1591. expectEquals (test.getNumPlayingNotes(), 0);
  1592. }
  1593. {
  1594. // all notes released when switching layout
  1595. UnitTestInstrument test;
  1596. test.setZoneLayout (testLayout);
  1597. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1598. expectEquals (test.getNumPlayingNotes(), 1);
  1599. test.enableOmniMode();
  1600. expectEquals (test.getNumPlayingNotes(), 0);
  1601. test.noteOn (3, 60, MPEValue::from7BitInt (100));
  1602. expectEquals (test.getNumPlayingNotes(), 1);
  1603. test.setZoneLayout (testLayout);
  1604. expectEquals (test.getNumPlayingNotes(), 0);
  1605. }
  1606. }
  1607. }
  1608. private:
  1609. //==========================================================================
  1610. /* This mock class is used for unit testing whether the methods of
  1611. MPEInstrument are called correctly.
  1612. */
  1613. class UnitTestInstrument : public MPEInstrument,
  1614. private MPEInstrument::Listener
  1615. {
  1616. typedef MPEInstrument Base;
  1617. public:
  1618. UnitTestInstrument()
  1619. : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0),
  1620. pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0),
  1621. sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0),
  1622. notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0),
  1623. noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0),
  1624. lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1),
  1625. lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false)
  1626. {
  1627. addListener (this);
  1628. }
  1629. void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity) override
  1630. {
  1631. Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity);
  1632. noteOnCallCounter++;
  1633. lastMidiChannelReceived = midiChannel;
  1634. lastMidiNoteNumberReceived = midiNoteNumber;
  1635. lastMPEValueReceived = midiNoteOnVelocity;
  1636. }
  1637. void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) override
  1638. {
  1639. Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity);
  1640. noteOffCallCounter++;
  1641. lastMidiChannelReceived = midiChannel;
  1642. lastMidiNoteNumberReceived = midiNoteNumber;
  1643. lastMPEValueReceived = midiNoteOffVelocity;
  1644. }
  1645. void pitchbend (int midiChannel, MPEValue value) override
  1646. {
  1647. Base::pitchbend (midiChannel, value);
  1648. pitchbendCallCounter++;
  1649. lastMidiChannelReceived = midiChannel;
  1650. lastMPEValueReceived = value;
  1651. }
  1652. void pressure (int midiChannel, MPEValue value) override
  1653. {
  1654. Base::pressure (midiChannel, value);
  1655. pressureCallCounter++;
  1656. lastMidiChannelReceived = midiChannel;
  1657. lastMPEValueReceived = value;
  1658. }
  1659. void timbre (int midiChannel, MPEValue value) override
  1660. {
  1661. Base::timbre (midiChannel, value);
  1662. timbreCallCounter++;
  1663. lastMidiChannelReceived = midiChannel;
  1664. lastMPEValueReceived = value;
  1665. }
  1666. void sustainPedal (int midiChannel, bool value) override
  1667. {
  1668. Base::sustainPedal (midiChannel, value);
  1669. sustainPedalCallCounter++;
  1670. lastMidiChannelReceived = midiChannel;
  1671. lastSustainPedalValueReceived = value;
  1672. }
  1673. void sostenutoPedal (int midiChannel, bool value) override
  1674. {
  1675. Base::sostenutoPedal (midiChannel, value);
  1676. sostenutoPedalCallCounter++;
  1677. lastMidiChannelReceived = midiChannel;
  1678. lastSostenutoPedalValueReceived = value;
  1679. }
  1680. int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter,
  1681. pressureCallCounter, timbreCallCounter, sustainPedalCallCounter,
  1682. sostenutoPedalCallCounter, noteAddedCallCounter,
  1683. notePressureChangedCallCounter, notePitchbendChangedCallCounter,
  1684. noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter,
  1685. noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived;
  1686. bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived;
  1687. MPEValue lastMPEValueReceived;
  1688. ScopedPointer<MPENote> lastNoteFinished;
  1689. private:
  1690. //======================================================================
  1691. void noteAdded (MPENote) override { noteAddedCallCounter++; }
  1692. void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; }
  1693. void notePitchbendChanged (MPENote) override { notePitchbendChangedCallCounter++; }
  1694. void noteTimbreChanged (MPENote) override { noteTimbreChangedCallCounter++; }
  1695. void noteKeyStateChanged (MPENote) override { noteKeyStateChangedCallCounter++; }
  1696. void noteReleased (MPENote finishedNote) override
  1697. {
  1698. noteReleasedCallCounter++;
  1699. lastNoteFinished = new MPENote (finishedNote);
  1700. }
  1701. };
  1702. //==========================================================================
  1703. template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre>
  1704. class CustomInitialValuesTest : public MPEInstrument
  1705. {
  1706. MPEValue getInitialPitchbendForNoteOn (int, int, MPEValue) const override
  1707. {
  1708. return MPEValue::from14BitInt (initial14BitPitchbend);
  1709. }
  1710. MPEValue getInitialPressureForNoteOn (int, int, MPEValue) const override
  1711. {
  1712. return MPEValue::from7BitInt (initial7BitPressure);
  1713. }
  1714. MPEValue getInitialTimbreForNoteOn (int, int, MPEValue) const override
  1715. {
  1716. return MPEValue::from7BitInt (initial7BitTimbre);
  1717. }
  1718. };
  1719. //==========================================================================
  1720. void expectNote (MPENote noteToTest,
  1721. int noteOnVelocity7Bit,
  1722. int pressure7Bit,
  1723. int pitchbend14Bit,
  1724. int timbre7Bit,
  1725. MPENote::KeyState keyState)
  1726. {
  1727. expect (noteToTest.isValid());
  1728. expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit);
  1729. expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit);
  1730. expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit);
  1731. expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit);
  1732. expect (noteToTest.keyState == keyState);
  1733. }
  1734. void expectHasFinishedNote (const UnitTestInstrument& test,
  1735. int channel, int noteNumber, int noteOffVelocity7Bit)
  1736. {
  1737. expect (test.lastNoteFinished != nullptr);
  1738. expectEquals (int (test.lastNoteFinished->midiChannel), channel);
  1739. expectEquals (int (test.lastNoteFinished->initialNote), noteNumber);
  1740. expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit);
  1741. expect (test.lastNoteFinished->keyState == MPENote::off);
  1742. }
  1743. void expectDoubleWithinRelativeError (double actual, double expected, double maxRelativeError)
  1744. {
  1745. const double maxAbsoluteError = jmax (1.0, std::fabs (expected)) * maxRelativeError;
  1746. expect (std::fabs (expected - actual) < maxAbsoluteError);
  1747. }
  1748. //==========================================================================
  1749. MPEZoneLayout testLayout;
  1750. };
  1751. static MPEInstrumentTests MPEInstrumentUnitTests;
  1752. #endif // JUCE_UNIT_TESTS