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_MidiMessage.cpp 50KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331
  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 MidiHelpers
  20. {
  21. inline uint8 initialByte (const int type, const int channel) noexcept
  22. {
  23. return (uint8) (type | jlimit (0, 15, channel - 1));
  24. }
  25. inline uint8 validVelocity (const int v) noexcept
  26. {
  27. return (uint8) jlimit (0, 127, v);
  28. }
  29. }
  30. //==============================================================================
  31. uint8 MidiMessage::floatValueToMidiByte (const float v) noexcept
  32. {
  33. jassert (v >= 0 && v <= 1.0f); // if your value is > 1, maybe you're passing an
  34. // integer value to a float method by mistake?
  35. return MidiHelpers::validVelocity (roundToInt (v * 127.0f));
  36. }
  37. uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend,
  38. const float pitchbendRange) noexcept
  39. {
  40. // can't translate a pitchbend value that is outside of the given range!
  41. jassert (std::abs (pitchbend) <= pitchbendRange);
  42. return static_cast<uint16> (pitchbend > 0.0f
  43. ? jmap (pitchbend, 0.0f, pitchbendRange, 8192.0f, 16383.0f)
  44. : jmap (pitchbend, -pitchbendRange, 0.0f, 0.0f, 8192.0f));
  45. }
  46. //==============================================================================
  47. MidiMessage::VariableLengthValue MidiMessage::readVariableLengthValue (const uint8* data, int maxBytesToUse) noexcept
  48. {
  49. uint32 v = 0;
  50. // The largest allowable variable-length value is 0x0f'ff'ff'ff which is
  51. // represented by the 4-byte stream 0xff 0xff 0xff 0x7f.
  52. // Longer bytestreams risk overflowing a 32-bit signed int.
  53. const auto limit = jmin (maxBytesToUse, 4);
  54. for (int numBytesUsed = 0; numBytesUsed < limit; ++numBytesUsed)
  55. {
  56. const auto i = data[numBytesUsed];
  57. v = (v << 7) + (i & 0x7f);
  58. if (! (i & 0x80))
  59. return { (int) v, numBytesUsed + 1 };
  60. }
  61. // If this is hit, the input was malformed. Either there were not enough
  62. // bytes of input to construct a full value, or no terminating byte was
  63. // found. This implementation only supports variable-length values of up
  64. // to four bytes.
  65. return {};
  66. }
  67. int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept
  68. {
  69. numBytesUsed = 0;
  70. int v = 0, i;
  71. do
  72. {
  73. i = (int) *data++;
  74. if (++numBytesUsed > 6)
  75. break;
  76. v = (v << 7) + (i & 0x7f);
  77. } while (i & 0x80);
  78. return v;
  79. }
  80. int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept
  81. {
  82. // this method only works for valid starting bytes of a short midi message
  83. jassert (firstByte >= 0x80 && firstByte != 0xf0 && firstByte != 0xf7);
  84. static const char messageLengths[] =
  85. {
  86. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  87. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  88. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  89. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  90. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  91. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  92. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  93. 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  94. };
  95. return messageLengths[firstByte & 0x7f];
  96. }
  97. //==============================================================================
  98. MidiMessage::MidiMessage() noexcept
  99. : size (2)
  100. {
  101. packedData.asBytes[0] = 0xf0;
  102. packedData.asBytes[1] = 0xf7;
  103. }
  104. MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t)
  105. : timeStamp (t), size (dataSize)
  106. {
  107. jassert (dataSize > 0);
  108. // this checks that the length matches the data..
  109. jassert (dataSize > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size);
  110. memcpy (allocateSpace (dataSize), d, (size_t) dataSize);
  111. }
  112. MidiMessage::MidiMessage (const int byte1, const double t) noexcept
  113. : timeStamp (t), size (1)
  114. {
  115. packedData.asBytes[0] = (uint8) byte1;
  116. // check that the length matches the data..
  117. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1);
  118. }
  119. MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept
  120. : timeStamp (t), size (2)
  121. {
  122. packedData.asBytes[0] = (uint8) byte1;
  123. packedData.asBytes[1] = (uint8) byte2;
  124. // check that the length matches the data..
  125. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2);
  126. }
  127. MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept
  128. : timeStamp (t), size (3)
  129. {
  130. packedData.asBytes[0] = (uint8) byte1;
  131. packedData.asBytes[1] = (uint8) byte2;
  132. packedData.asBytes[2] = (uint8) byte3;
  133. // check that the length matches the data..
  134. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3);
  135. }
  136. MidiMessage::MidiMessage (const MidiMessage& other)
  137. : timeStamp (other.timeStamp), size (other.size)
  138. {
  139. if (isHeapAllocated())
  140. memcpy (allocateSpace (size), other.getData(), (size_t) size);
  141. else
  142. packedData.allocatedData = other.packedData.allocatedData;
  143. }
  144. MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
  145. : timeStamp (newTimeStamp), size (other.size)
  146. {
  147. if (isHeapAllocated())
  148. memcpy (allocateSpace (size), other.getData(), (size_t) size);
  149. else
  150. packedData.allocatedData = other.packedData.allocatedData;
  151. }
  152. MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte,
  153. double t, bool sysexHasEmbeddedLength)
  154. : timeStamp (t)
  155. {
  156. auto src = static_cast<const uint8*> (srcData);
  157. auto byte = (unsigned int) *src;
  158. if (byte < 0x80)
  159. {
  160. byte = (unsigned int) lastStatusByte;
  161. numBytesUsed = -1;
  162. }
  163. else
  164. {
  165. numBytesUsed = 0;
  166. --sz;
  167. ++src;
  168. }
  169. if (byte >= 0x80)
  170. {
  171. if (byte == 0xf0)
  172. {
  173. auto d = src;
  174. bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength;
  175. int numVariableLengthSysexBytes = 0;
  176. while (d < src + sz)
  177. {
  178. if (*d >= 0x80)
  179. {
  180. if (*d == 0xf7)
  181. {
  182. ++d; // include the trailing 0xf7 when we hit it
  183. break;
  184. }
  185. if (haveReadAllLengthBytes) // if we see a 0x80 bit set after the initial data length
  186. break; // bytes, assume it's the end of the sysex
  187. ++numVariableLengthSysexBytes;
  188. }
  189. else if (! haveReadAllLengthBytes)
  190. {
  191. haveReadAllLengthBytes = true;
  192. ++numVariableLengthSysexBytes;
  193. }
  194. ++d;
  195. }
  196. src += numVariableLengthSysexBytes;
  197. size = 1 + (int) (d - src);
  198. auto dest = allocateSpace (size);
  199. *dest = (uint8) byte;
  200. memcpy (dest + 1, src, (size_t) (size - 1));
  201. numBytesUsed += (numVariableLengthSysexBytes + size); // (these aren't counted in the size)
  202. }
  203. else if (byte == 0xff)
  204. {
  205. const auto bytesLeft = readVariableLengthValue (src + 1, sz - 1);
  206. size = jmin (sz + 1, bytesLeft.bytesUsed + 2 + bytesLeft.value);
  207. auto dest = allocateSpace (size);
  208. *dest = (uint8) byte;
  209. memcpy (dest + 1, src, (size_t) size - 1);
  210. numBytesUsed += size;
  211. }
  212. else
  213. {
  214. size = getMessageLengthFromFirstByte ((uint8) byte);
  215. packedData.asBytes[0] = (uint8) byte;
  216. if (size > 1)
  217. {
  218. packedData.asBytes[1] = (sz > 0 ? src[0] : 0);
  219. if (size > 2)
  220. packedData.asBytes[2] = (sz > 1 ? src[1] : 0);
  221. }
  222. numBytesUsed += jmin (size, sz + 1);
  223. }
  224. }
  225. else
  226. {
  227. packedData.allocatedData = nullptr;
  228. size = 0;
  229. }
  230. }
  231. MidiMessage& MidiMessage::operator= (const MidiMessage& other)
  232. {
  233. if (this != &other)
  234. {
  235. if (other.isHeapAllocated())
  236. {
  237. auto* newStorage = static_cast<uint8*> (isHeapAllocated()
  238. ? std::realloc (packedData.allocatedData, (size_t) other.size)
  239. : std::malloc ((size_t) other.size));
  240. if (newStorage == nullptr)
  241. throw std::bad_alloc{}; // The midi message has not been adjusted at this point
  242. packedData.allocatedData = newStorage;
  243. memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size);
  244. }
  245. else
  246. {
  247. if (isHeapAllocated())
  248. std::free (packedData.allocatedData);
  249. packedData.allocatedData = other.packedData.allocatedData;
  250. }
  251. timeStamp = other.timeStamp;
  252. size = other.size;
  253. }
  254. return *this;
  255. }
  256. MidiMessage::MidiMessage (MidiMessage&& other) noexcept
  257. : timeStamp (other.timeStamp), size (other.size)
  258. {
  259. packedData.allocatedData = other.packedData.allocatedData;
  260. other.size = 0;
  261. }
  262. MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept
  263. {
  264. packedData.allocatedData = other.packedData.allocatedData;
  265. timeStamp = other.timeStamp;
  266. size = other.size;
  267. other.size = 0;
  268. return *this;
  269. }
  270. MidiMessage::~MidiMessage() noexcept
  271. {
  272. if (isHeapAllocated())
  273. std::free (packedData.allocatedData);
  274. }
  275. uint8* MidiMessage::allocateSpace (int bytes)
  276. {
  277. if (bytes > (int) sizeof (packedData))
  278. {
  279. auto d = static_cast<uint8*> (std::malloc ((size_t) bytes));
  280. packedData.allocatedData = d;
  281. return d;
  282. }
  283. return packedData.asBytes;
  284. }
  285. String MidiMessage::getDescription() const
  286. {
  287. if (isNoteOn()) return "Note on " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
  288. if (isNoteOff()) return "Note off " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
  289. if (isProgramChange()) return "Program change " + String (getProgramChangeNumber()) + " Channel " + String (getChannel());
  290. if (isPitchWheel()) return "Pitch wheel " + String (getPitchWheelValue()) + " Channel " + String (getChannel());
  291. if (isAftertouch()) return "Aftertouch " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + ": " + String (getAfterTouchValue()) + " Channel " + String (getChannel());
  292. if (isChannelPressure()) return "Channel pressure " + String (getChannelPressureValue()) + " Channel " + String (getChannel());
  293. if (isAllNotesOff()) return "All notes off Channel " + String (getChannel());
  294. if (isAllSoundOff()) return "All sound off Channel " + String (getChannel());
  295. if (isMetaEvent()) return "Meta event";
  296. if (isController())
  297. {
  298. String name (MidiMessage::getControllerName (getControllerNumber()));
  299. if (name.isEmpty())
  300. name = String (getControllerNumber());
  301. return "Controller " + name + ": " + String (getControllerValue()) + " Channel " + String (getChannel());
  302. }
  303. return String::toHexString (getRawData(), getRawDataSize());
  304. }
  305. MidiMessage MidiMessage::withTimeStamp (double newTimestamp) const
  306. {
  307. return { *this, newTimestamp };
  308. }
  309. int MidiMessage::getChannel() const noexcept
  310. {
  311. auto data = getRawData();
  312. if ((data[0] & 0xf0) != 0xf0)
  313. return (data[0] & 0xf) + 1;
  314. return 0;
  315. }
  316. bool MidiMessage::isForChannel (const int channel) const noexcept
  317. {
  318. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  319. auto data = getRawData();
  320. return ((data[0] & 0xf) == channel - 1)
  321. && ((data[0] & 0xf0) != 0xf0);
  322. }
  323. void MidiMessage::setChannel (const int channel) noexcept
  324. {
  325. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  326. auto data = getData();
  327. if ((data[0] & 0xf0) != (uint8) 0xf0)
  328. data[0] = (uint8) ((data[0] & (uint8) 0xf0)
  329. | (uint8)(channel - 1));
  330. }
  331. bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept
  332. {
  333. auto data = getRawData();
  334. return ((data[0] & 0xf0) == 0x90)
  335. && (returnTrueForVelocity0 || data[2] != 0);
  336. }
  337. bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept
  338. {
  339. auto data = getRawData();
  340. return ((data[0] & 0xf0) == 0x80)
  341. || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90));
  342. }
  343. bool MidiMessage::isNoteOnOrOff() const noexcept
  344. {
  345. auto d = getRawData()[0] & 0xf0;
  346. return (d == 0x90) || (d == 0x80);
  347. }
  348. int MidiMessage::getNoteNumber() const noexcept
  349. {
  350. return getRawData()[1];
  351. }
  352. void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept
  353. {
  354. if (isNoteOnOrOff() || isAftertouch())
  355. getData()[1] = (uint8) (newNoteNumber & 127);
  356. }
  357. uint8 MidiMessage::getVelocity() const noexcept
  358. {
  359. if (isNoteOnOrOff())
  360. return getRawData()[2];
  361. return 0;
  362. }
  363. float MidiMessage::getFloatVelocity() const noexcept
  364. {
  365. return getVelocity() * (1.0f / 127.0f);
  366. }
  367. void MidiMessage::setVelocity (const float newVelocity) noexcept
  368. {
  369. if (isNoteOnOrOff())
  370. getData()[2] = floatValueToMidiByte (newVelocity);
  371. }
  372. void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept
  373. {
  374. if (isNoteOnOrOff())
  375. {
  376. auto data = getData();
  377. data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2]));
  378. }
  379. }
  380. bool MidiMessage::isAftertouch() const noexcept
  381. {
  382. return (getRawData()[0] & 0xf0) == 0xa0;
  383. }
  384. int MidiMessage::getAfterTouchValue() const noexcept
  385. {
  386. jassert (isAftertouch());
  387. return getRawData()[2];
  388. }
  389. MidiMessage MidiMessage::aftertouchChange (const int channel,
  390. const int noteNum,
  391. const int aftertouchValue) noexcept
  392. {
  393. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  394. jassert (isPositiveAndBelow (noteNum, 128));
  395. jassert (isPositiveAndBelow (aftertouchValue, 128));
  396. return MidiMessage (MidiHelpers::initialByte (0xa0, channel),
  397. noteNum & 0x7f,
  398. aftertouchValue & 0x7f);
  399. }
  400. bool MidiMessage::isChannelPressure() const noexcept
  401. {
  402. return (getRawData()[0] & 0xf0) == 0xd0;
  403. }
  404. int MidiMessage::getChannelPressureValue() const noexcept
  405. {
  406. jassert (isChannelPressure());
  407. return getRawData()[1];
  408. }
  409. MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept
  410. {
  411. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  412. jassert (isPositiveAndBelow (pressure, 128));
  413. return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
  414. }
  415. bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; }
  416. bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; }
  417. bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; }
  418. bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; }
  419. bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; }
  420. bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; }
  421. bool MidiMessage::isProgramChange() const noexcept
  422. {
  423. return (getRawData()[0] & 0xf0) == 0xc0;
  424. }
  425. int MidiMessage::getProgramChangeNumber() const noexcept
  426. {
  427. jassert (isProgramChange());
  428. return getRawData()[1];
  429. }
  430. MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept
  431. {
  432. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  433. return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f);
  434. }
  435. bool MidiMessage::isPitchWheel() const noexcept
  436. {
  437. return (getRawData()[0] & 0xf0) == 0xe0;
  438. }
  439. int MidiMessage::getPitchWheelValue() const noexcept
  440. {
  441. jassert (isPitchWheel());
  442. auto data = getRawData();
  443. return data[1] | (data[2] << 7);
  444. }
  445. MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept
  446. {
  447. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  448. jassert (isPositiveAndBelow (position, 0x4000));
  449. return MidiMessage (MidiHelpers::initialByte (0xe0, channel),
  450. position & 127, (position >> 7) & 127);
  451. }
  452. bool MidiMessage::isController() const noexcept
  453. {
  454. return (getRawData()[0] & 0xf0) == 0xb0;
  455. }
  456. bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
  457. {
  458. auto data = getRawData();
  459. return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
  460. }
  461. int MidiMessage::getControllerNumber() const noexcept
  462. {
  463. jassert (isController());
  464. return getRawData()[1];
  465. }
  466. int MidiMessage::getControllerValue() const noexcept
  467. {
  468. jassert (isController());
  469. return getRawData()[2];
  470. }
  471. MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept
  472. {
  473. // the channel must be between 1 and 16 inclusive
  474. jassert (channel > 0 && channel <= 16);
  475. return MidiMessage (MidiHelpers::initialByte (0xb0, channel),
  476. controllerType & 127, value & 127);
  477. }
  478. MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept
  479. {
  480. jassert (channel > 0 && channel <= 16);
  481. jassert (isPositiveAndBelow (noteNumber, 128));
  482. return MidiMessage (MidiHelpers::initialByte (0x90, channel),
  483. noteNumber & 127, MidiHelpers::validVelocity (velocity));
  484. }
  485. MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept
  486. {
  487. return noteOn (channel, noteNumber, floatValueToMidiByte (velocity));
  488. }
  489. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept
  490. {
  491. jassert (channel > 0 && channel <= 16);
  492. jassert (isPositiveAndBelow (noteNumber, 128));
  493. return MidiMessage (MidiHelpers::initialByte (0x80, channel),
  494. noteNumber & 127, MidiHelpers::validVelocity (velocity));
  495. }
  496. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept
  497. {
  498. return noteOff (channel, noteNumber, floatValueToMidiByte (velocity));
  499. }
  500. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept
  501. {
  502. jassert (channel > 0 && channel <= 16);
  503. jassert (isPositiveAndBelow (noteNumber, 128));
  504. return MidiMessage (MidiHelpers::initialByte (0x80, channel), noteNumber & 127, 0);
  505. }
  506. MidiMessage MidiMessage::allNotesOff (const int channel) noexcept
  507. {
  508. return controllerEvent (channel, 123, 0);
  509. }
  510. bool MidiMessage::isAllNotesOff() const noexcept
  511. {
  512. auto data = getRawData();
  513. return (data[0] & 0xf0) == 0xb0 && data[1] == 123;
  514. }
  515. MidiMessage MidiMessage::allSoundOff (const int channel) noexcept
  516. {
  517. return controllerEvent (channel, 120, 0);
  518. }
  519. bool MidiMessage::isAllSoundOff() const noexcept
  520. {
  521. auto data = getRawData();
  522. return data[1] == 120 && (data[0] & 0xf0) == 0xb0;
  523. }
  524. bool MidiMessage::isResetAllControllers() const noexcept
  525. {
  526. auto data = getRawData();
  527. return (data[0] & 0xf0) == 0xb0 && data[1] == 121;
  528. }
  529. MidiMessage MidiMessage::allControllersOff (const int channel) noexcept
  530. {
  531. return controllerEvent (channel, 121, 0);
  532. }
  533. MidiMessage MidiMessage::masterVolume (const float volume)
  534. {
  535. auto vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000));
  536. return { 0xf0, 0x7f, 0x7f, 0x04, 0x01, vol & 0x7f, vol >> 7, 0xf7 };
  537. }
  538. //==============================================================================
  539. bool MidiMessage::isSysEx() const noexcept
  540. {
  541. return *getRawData() == 0xf0;
  542. }
  543. MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize)
  544. {
  545. HeapBlock<uint8> m (dataSize + 2);
  546. m[0] = 0xf0;
  547. memcpy (m + 1, sysexData, (size_t) dataSize);
  548. m[dataSize + 1] = 0xf7;
  549. return MidiMessage (m, dataSize + 2);
  550. }
  551. const uint8* MidiMessage::getSysExData() const noexcept
  552. {
  553. return isSysEx() ? getRawData() + 1 : nullptr;
  554. }
  555. int MidiMessage::getSysExDataSize() const noexcept
  556. {
  557. return isSysEx() ? size - 2 : 0;
  558. }
  559. //==============================================================================
  560. bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; }
  561. bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; }
  562. int MidiMessage::getMetaEventType() const noexcept
  563. {
  564. auto data = getRawData();
  565. return (size < 2 || *data != 0xff) ? -1 : data[1];
  566. }
  567. int MidiMessage::getMetaEventLength() const noexcept
  568. {
  569. auto data = getRawData();
  570. if (*data == 0xff)
  571. {
  572. const auto var = readVariableLengthValue (data + 2, size - 2);
  573. return jmax (0, jmin (size - 2 - var.bytesUsed, var.value));
  574. }
  575. return 0;
  576. }
  577. const uint8* MidiMessage::getMetaEventData() const noexcept
  578. {
  579. jassert (isMetaEvent());
  580. auto d = getRawData() + 2;
  581. const auto var = readVariableLengthValue (d, size - 2);
  582. return d + var.bytesUsed;
  583. }
  584. bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; }
  585. bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; }
  586. bool MidiMessage::isTextMetaEvent() const noexcept
  587. {
  588. auto t = getMetaEventType();
  589. return t > 0 && t < 16;
  590. }
  591. String MidiMessage::getTextFromTextMetaEvent() const
  592. {
  593. auto textData = reinterpret_cast<const char*> (getMetaEventData());
  594. return String (CharPointer_UTF8 (textData),
  595. CharPointer_UTF8 (textData + getMetaEventLength()));
  596. }
  597. MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
  598. {
  599. jassert (type > 0 && type < 16);
  600. MidiMessage result;
  601. const size_t textSize = text.text.sizeInBytes() - 1;
  602. uint8 header[8];
  603. size_t n = sizeof (header);
  604. header[--n] = (uint8) (textSize & 0x7f);
  605. for (size_t i = textSize; (i >>= 7) != 0;)
  606. header[--n] = (uint8) ((i & 0x7f) | 0x80);
  607. header[--n] = (uint8) type;
  608. header[--n] = 0xff;
  609. const size_t headerLen = sizeof (header) - n;
  610. const int totalSize = (int) (headerLen + textSize);
  611. auto dest = result.allocateSpace (totalSize);
  612. result.size = totalSize;
  613. memcpy (dest, header + n, headerLen);
  614. memcpy (dest + headerLen, text.text.getAddress(), textSize);
  615. return result;
  616. }
  617. bool MidiMessage::isTrackNameEvent() const noexcept { auto data = getRawData(); return (data[1] == 3) && (*data == 0xff); }
  618. bool MidiMessage::isTempoMetaEvent() const noexcept { auto data = getRawData(); return (data[1] == 81) && (*data == 0xff); }
  619. bool MidiMessage::isMidiChannelMetaEvent() const noexcept { auto data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); }
  620. int MidiMessage::getMidiChannelMetaEventChannel() const noexcept
  621. {
  622. jassert (isMidiChannelMetaEvent());
  623. return getRawData()[3] + 1;
  624. }
  625. double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept
  626. {
  627. if (! isTempoMetaEvent())
  628. return 0.0;
  629. auto d = getMetaEventData();
  630. return (((unsigned int) d[0] << 16)
  631. | ((unsigned int) d[1] << 8)
  632. | d[2])
  633. / 1000000.0;
  634. }
  635. double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept
  636. {
  637. if (timeFormat > 0)
  638. {
  639. if (! isTempoMetaEvent())
  640. return 0.5 / timeFormat;
  641. return getTempoSecondsPerQuarterNote() / timeFormat;
  642. }
  643. const int frameCode = (-timeFormat) >> 8;
  644. double framesPerSecond;
  645. switch (frameCode)
  646. {
  647. case 24: framesPerSecond = 24.0; break;
  648. case 25: framesPerSecond = 25.0; break;
  649. case 29: framesPerSecond = 30.0 * 1000.0 / 1001.0; break;
  650. case 30: framesPerSecond = 30.0; break;
  651. default: framesPerSecond = 30.0; break;
  652. }
  653. return (1.0 / framesPerSecond) / (timeFormat & 0xff);
  654. }
  655. MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept
  656. {
  657. return { 0xff, 81, 3,
  658. (uint8) (microsecondsPerQuarterNote >> 16),
  659. (uint8) (microsecondsPerQuarterNote >> 8),
  660. (uint8) microsecondsPerQuarterNote };
  661. }
  662. bool MidiMessage::isTimeSignatureMetaEvent() const noexcept
  663. {
  664. auto data = getRawData();
  665. return (data[1] == 0x58) && (*data == (uint8) 0xff);
  666. }
  667. void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept
  668. {
  669. if (isTimeSignatureMetaEvent())
  670. {
  671. auto d = getMetaEventData();
  672. numerator = d[0];
  673. denominator = 1 << d[1];
  674. }
  675. else
  676. {
  677. numerator = 4;
  678. denominator = 4;
  679. }
  680. }
  681. MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator)
  682. {
  683. int n = 1;
  684. int powerOfTwo = 0;
  685. while (n < denominator)
  686. {
  687. n <<= 1;
  688. ++powerOfTwo;
  689. }
  690. return { 0xff, 0x58, 0x04, numerator, powerOfTwo, 1, 96 };
  691. }
  692. MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept
  693. {
  694. return { 0xff, 0x20, 0x01, jlimit (0, 0xff, channel - 1) };
  695. }
  696. bool MidiMessage::isKeySignatureMetaEvent() const noexcept
  697. {
  698. return getMetaEventType() == 0x59;
  699. }
  700. int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept
  701. {
  702. return (int) (int8) getMetaEventData()[0];
  703. }
  704. bool MidiMessage::isKeySignatureMajorKey() const noexcept
  705. {
  706. return getMetaEventData()[1] == 0;
  707. }
  708. MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey)
  709. {
  710. jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7);
  711. return { 0xff, 0x59, 0x02, numberOfSharpsOrFlats, isMinorKey ? 1 : 0 };
  712. }
  713. MidiMessage MidiMessage::endOfTrack() noexcept
  714. {
  715. return { 0xff, 0x2f, 0x00 };
  716. }
  717. //==============================================================================
  718. bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; }
  719. int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { auto data = getRawData(); return data[1] | (data[2] << 7); }
  720. MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept
  721. {
  722. return { 0xf2,
  723. positionInMidiBeats & 127,
  724. (positionInMidiBeats >> 7) & 127 };
  725. }
  726. bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; }
  727. MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); }
  728. bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; }
  729. MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); }
  730. bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; }
  731. MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); }
  732. bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; }
  733. MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); }
  734. bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; }
  735. int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; }
  736. int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; }
  737. MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept
  738. {
  739. return MidiMessage (0xf1, (sequenceNumber << 4) | value);
  740. }
  741. bool MidiMessage::isFullFrame() const noexcept
  742. {
  743. auto data = getRawData();
  744. return data[0] == 0xf0
  745. && data[1] == 0x7f
  746. && size >= 10
  747. && data[3] == 0x01
  748. && data[4] == 0x01;
  749. }
  750. void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames,
  751. MidiMessage::SmpteTimecodeType& timecodeType) const noexcept
  752. {
  753. jassert (isFullFrame());
  754. auto data = getRawData();
  755. timecodeType = (SmpteTimecodeType) (data[5] >> 5);
  756. hours = data[5] & 0x1f;
  757. minutes = data[6];
  758. seconds = data[7];
  759. frames = data[8];
  760. }
  761. MidiMessage MidiMessage::fullFrame (int hours, int minutes, int seconds, int frames,
  762. MidiMessage::SmpteTimecodeType timecodeType)
  763. {
  764. return { 0xf0, 0x7f, 0x7f, 0x01, 0x01,
  765. (hours & 0x01f) | (timecodeType << 5),
  766. minutes, seconds, frames,
  767. 0xf7 };
  768. }
  769. bool MidiMessage::isMidiMachineControlMessage() const noexcept
  770. {
  771. auto data = getRawData();
  772. return data[0] == 0xf0
  773. && data[1] == 0x7f
  774. && data[3] == 0x06
  775. && size > 5;
  776. }
  777. MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept
  778. {
  779. jassert (isMidiMachineControlMessage());
  780. return (MidiMachineControlCommand) getRawData()[4];
  781. }
  782. MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command)
  783. {
  784. return { 0xf0, 0x7f, 0, 6, command, 0xf7 };
  785. }
  786. //==============================================================================
  787. bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept
  788. {
  789. auto data = getRawData();
  790. if (size >= 12
  791. && data[0] == 0xf0
  792. && data[1] == 0x7f
  793. && data[3] == 0x06
  794. && data[4] == 0x44
  795. && data[5] == 0x06
  796. && data[6] == 0x01)
  797. {
  798. hours = data[7] % 24; // (that some machines send out hours > 24)
  799. minutes = data[8];
  800. seconds = data[9];
  801. frames = data[10];
  802. return true;
  803. }
  804. return false;
  805. }
  806. MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames)
  807. {
  808. return { 0xf0, 0x7f, 0, 6, 0x44, 6, 1, hours, minutes, seconds, frames, 0xf7 };
  809. }
  810. //==============================================================================
  811. String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
  812. {
  813. static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
  814. static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" };
  815. if (isPositiveAndBelow (note, 128))
  816. {
  817. String s (useSharps ? sharpNoteNames[note % 12]
  818. : flatNoteNames [note % 12]);
  819. if (includeOctaveNumber)
  820. s << (note / 12 + (octaveNumForMiddleC - 5));
  821. return s;
  822. }
  823. return {};
  824. }
  825. double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept
  826. {
  827. return frequencyOfA * std::pow (2.0, (noteNumber - 69) / 12.0);
  828. }
  829. bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept
  830. {
  831. return ((1 << (noteNumber % 12)) & 0x054a) != 0;
  832. }
  833. const char* MidiMessage::getGMInstrumentName (const int n)
  834. {
  835. static const char* names[] =
  836. {
  837. NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"),
  838. NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"),
  839. NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"),
  840. NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"),
  841. NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"),
  842. NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"),
  843. NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"),
  844. NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"),
  845. NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"),
  846. NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"),
  847. NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"),
  848. NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"),
  849. NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"),
  850. NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"),
  851. NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"),
  852. NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"),
  853. NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"),
  854. NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"),
  855. NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"),
  856. NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"),
  857. NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"),
  858. NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"),
  859. NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"),
  860. NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"),
  861. NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"),
  862. NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"),
  863. NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"),
  864. NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"),
  865. NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"),
  866. NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"),
  867. NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"),
  868. NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot")
  869. };
  870. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  871. }
  872. const char* MidiMessage::getGMInstrumentBankName (const int n)
  873. {
  874. static const char* names[] =
  875. {
  876. NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"),
  877. NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"),
  878. NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"),
  879. NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects")
  880. };
  881. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  882. }
  883. const char* MidiMessage::getRhythmInstrumentName (const int n)
  884. {
  885. static const char* names[] =
  886. {
  887. NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"),
  888. NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"),
  889. NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"),
  890. NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"),
  891. NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"),
  892. NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"),
  893. NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"),
  894. NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"),
  895. NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"),
  896. NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"),
  897. NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"),
  898. NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle")
  899. };
  900. return (n >= 35 && n <= 81) ? names[n - 35] : nullptr;
  901. }
  902. const char* MidiMessage::getControllerName (const int n)
  903. {
  904. static const char* names[] =
  905. {
  906. NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"),
  907. nullptr,
  908. NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"),
  909. NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"),
  910. nullptr,
  911. NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"),
  912. NEEDS_TRANS("Effect Control 2 (coarse)"),
  913. nullptr, nullptr,
  914. NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"),
  915. NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"),
  916. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  917. NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"),
  918. nullptr,
  919. NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"),
  920. NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"),
  921. NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"),
  922. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  923. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  924. NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"),
  925. NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"),
  926. NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"),
  927. NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"),
  928. NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"),
  929. NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"),
  930. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  931. NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"),
  932. NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"),
  933. NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"),
  934. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  935. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  936. NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"),
  937. NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation")
  938. };
  939. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  940. }
  941. //==============================================================================
  942. //==============================================================================
  943. #if JUCE_UNIT_TESTS
  944. struct MidiMessageTest : public UnitTest
  945. {
  946. MidiMessageTest()
  947. : UnitTest ("MidiMessage", UnitTestCategories::midi)
  948. {}
  949. void runTest() override
  950. {
  951. using std::begin;
  952. using std::end;
  953. beginTest ("ReadVariableLengthValue should return valid, backward-compatible results");
  954. {
  955. const std::vector<uint8> inputs[]
  956. {
  957. { 0x00 },
  958. { 0x40 },
  959. { 0x7f },
  960. { 0x81, 0x00 },
  961. { 0xc0, 0x00 },
  962. { 0xff, 0x7f },
  963. { 0x81, 0x80, 0x00 },
  964. { 0xc0, 0x80, 0x00 },
  965. { 0xff, 0xff, 0x7f },
  966. { 0x81, 0x80, 0x80, 0x00 },
  967. { 0xc0, 0x80, 0x80, 0x00 },
  968. { 0xff, 0xff, 0xff, 0x7f }
  969. };
  970. const int outputs[]
  971. {
  972. 0x00,
  973. 0x40,
  974. 0x7f,
  975. 0x80,
  976. 0x2000,
  977. 0x3fff,
  978. 0x4000,
  979. 0x100000,
  980. 0x1fffff,
  981. 0x200000,
  982. 0x8000000,
  983. 0xfffffff,
  984. };
  985. expectEquals (std::distance (begin (inputs), end (inputs)),
  986. std::distance (begin (outputs), end (outputs)));
  987. size_t index = 0;
  988. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  989. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
  990. for (const auto& input : inputs)
  991. {
  992. auto copy = input;
  993. while (copy.size() < 16)
  994. copy.push_back (0);
  995. const auto result = MidiMessage::readVariableLengthValue (copy.data(),
  996. (int) copy.size());
  997. expect (result.isValid());
  998. expectEquals (result.value, outputs[index]);
  999. expectEquals (result.bytesUsed, (int) inputs[index].size());
  1000. int legacyNumUsed = 0;
  1001. const auto legacyResult = MidiMessage::readVariableLengthVal (copy.data(),
  1002. legacyNumUsed);
  1003. expectEquals (result.value, legacyResult);
  1004. expectEquals (result.bytesUsed, legacyNumUsed);
  1005. ++index;
  1006. }
  1007. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1008. JUCE_END_IGNORE_WARNINGS_MSVC
  1009. }
  1010. beginTest ("ReadVariableLengthVal should return 0 if input is truncated");
  1011. {
  1012. for (size_t i = 0; i != 16; ++i)
  1013. {
  1014. std::vector<uint8> input;
  1015. input.resize (i, 0xFF);
  1016. const auto result = MidiMessage::readVariableLengthValue (input.data(),
  1017. (int) input.size());
  1018. expect (! result.isValid());
  1019. expectEquals (result.value, 0);
  1020. expectEquals (result.bytesUsed, 0);
  1021. }
  1022. }
  1023. const std::vector<uint8> metaEvents[]
  1024. {
  1025. // Format is 0xff, followed by a 'kind' byte, followed by a variable-length
  1026. // 'data-length' value, followed by that many data bytes
  1027. { 0xff, 0x00, 0x02, 0x00, 0x00 }, // Sequence number
  1028. { 0xff, 0x01, 0x00 }, // Text event
  1029. { 0xff, 0x02, 0x00 }, // Copyright notice
  1030. { 0xff, 0x03, 0x00 }, // Track name
  1031. { 0xff, 0x04, 0x00 }, // Instrument name
  1032. { 0xff, 0x05, 0x00 }, // Lyric
  1033. { 0xff, 0x06, 0x00 }, // Marker
  1034. { 0xff, 0x07, 0x00 }, // Cue point
  1035. { 0xff, 0x20, 0x01, 0x00 }, // Channel prefix
  1036. { 0xff, 0x2f, 0x00 }, // End of track
  1037. { 0xff, 0x51, 0x03, 0x01, 0x02, 0x03 }, // Set tempo
  1038. { 0xff, 0x54, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 }, // SMPTE offset
  1039. { 0xff, 0x58, 0x04, 0x01, 0x02, 0x03, 0x04 }, // Time signature
  1040. { 0xff, 0x59, 0x02, 0x01, 0x02 }, // Key signature
  1041. { 0xff, 0x7f, 0x00 }, // Sequencer-specific
  1042. };
  1043. beginTest ("MidiMessage data constructor works for well-formed meta-events");
  1044. {
  1045. const auto status = (uint8) 0x90;
  1046. for (const auto& input : metaEvents)
  1047. {
  1048. int bytesUsed = 0;
  1049. const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
  1050. expect (msg.isMetaEvent());
  1051. expectEquals (msg.getMetaEventLength(), (int) input.size() - 3);
  1052. expectEquals (msg.getMetaEventType(), (int) input[1]);
  1053. }
  1054. }
  1055. beginTest ("MidiMessage data constructor works for malformed meta-events");
  1056. {
  1057. const auto status = (uint8) 0x90;
  1058. const auto runTest = [&] (const std::vector<uint8>& input)
  1059. {
  1060. int bytesUsed = 0;
  1061. const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
  1062. expect (msg.isMetaEvent());
  1063. expectEquals (msg.getMetaEventLength(), jmax (0, (int) input.size() - 3));
  1064. expectEquals (msg.getMetaEventType(), input.size() >= 2 ? input[1] : -1);
  1065. };
  1066. runTest ({ 0xff });
  1067. for (const auto& input : metaEvents)
  1068. {
  1069. auto copy = input;
  1070. copy[2] = 0x40; // Set the size of the message to more bytes than are present
  1071. runTest (copy);
  1072. }
  1073. }
  1074. }
  1075. };
  1076. static MidiMessageTest midiMessageTests;
  1077. #endif
  1078. } // namespace juce