Audio plugin host https://kx.studio/carla
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.

juce_MPEInstrument.cpp 103KB

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