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

1138 lines
43KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace MidiHelpers
  18. {
  19. inline uint8 initialByte (const int type, const int channel) noexcept
  20. {
  21. return (uint8) (type | jlimit (0, 15, channel - 1));
  22. }
  23. inline uint8 validVelocity (const int v) noexcept
  24. {
  25. return (uint8) jlimit (0, 127, v);
  26. }
  27. inline uint8 floatVelocityToByte (const float v) noexcept
  28. {
  29. return validVelocity (roundToInt (v * 127.0f));
  30. }
  31. }
  32. //==============================================================================
  33. int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept
  34. {
  35. numBytesUsed = 0;
  36. int v = 0, i;
  37. do
  38. {
  39. i = (int) *data++;
  40. if (++numBytesUsed > 6)
  41. break;
  42. v = (v << 7) + (i & 0x7f);
  43. } while (i & 0x80);
  44. return v;
  45. }
  46. int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept
  47. {
  48. // this method only works for valid starting bytes of a short midi message
  49. jassert (firstByte >= 0x80 && firstByte != 0xf0 && firstByte != 0xf7);
  50. static const char messageLengths[] =
  51. {
  52. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  53. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  54. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  55. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  56. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  57. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
  58. 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  59. 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  60. };
  61. return messageLengths [firstByte & 0x7f];
  62. }
  63. //==============================================================================
  64. MidiMessage::MidiMessage() noexcept
  65. : timeStamp (0), size (2)
  66. {
  67. preallocatedData.asBytes[0] = 0xf0;
  68. preallocatedData.asBytes[1] = 0xf7;
  69. }
  70. MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t)
  71. : timeStamp (t),
  72. size (dataSize)
  73. {
  74. jassert (dataSize > 0);
  75. memcpy (allocateSpace (dataSize), d, (size_t) dataSize);
  76. // check that the length matches the data..
  77. jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size);
  78. }
  79. MidiMessage::MidiMessage (const int byte1, const double t) noexcept
  80. : timeStamp (t), size (1)
  81. {
  82. preallocatedData.asBytes[0] = (uint8) byte1;
  83. // check that the length matches the data..
  84. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1);
  85. }
  86. MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept
  87. : timeStamp (t), size (2)
  88. {
  89. preallocatedData.asBytes[0] = (uint8) byte1;
  90. preallocatedData.asBytes[1] = (uint8) byte2;
  91. // check that the length matches the data..
  92. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2);
  93. }
  94. MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept
  95. : timeStamp (t), size (3)
  96. {
  97. preallocatedData.asBytes[0] = (uint8) byte1;
  98. preallocatedData.asBytes[1] = (uint8) byte2;
  99. preallocatedData.asBytes[2] = (uint8) byte3;
  100. // check that the length matches the data..
  101. jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3);
  102. }
  103. MidiMessage::MidiMessage (const MidiMessage& other)
  104. : timeStamp (other.timeStamp), size (other.size)
  105. {
  106. if (other.allocatedData != nullptr)
  107. {
  108. allocatedData.malloc ((size_t) size);
  109. memcpy (allocatedData, other.allocatedData, (size_t) size);
  110. }
  111. else
  112. {
  113. preallocatedData.asInt32 = other.preallocatedData.asInt32;
  114. }
  115. }
  116. MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
  117. : timeStamp (newTimeStamp), size (other.size)
  118. {
  119. if (other.allocatedData != nullptr)
  120. {
  121. allocatedData.malloc ((size_t) size);
  122. memcpy (allocatedData, other.allocatedData, (size_t) size);
  123. }
  124. else
  125. {
  126. preallocatedData.asInt32 = other.preallocatedData.asInt32;
  127. }
  128. }
  129. MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte,
  130. double t, bool sysexHasEmbeddedLength)
  131. : timeStamp (t)
  132. {
  133. const uint8* src = static_cast<const uint8*> (srcData);
  134. unsigned int byte = (unsigned int) *src;
  135. if (byte < 0x80)
  136. {
  137. byte = (unsigned int) (uint8) lastStatusByte;
  138. numBytesUsed = -1;
  139. }
  140. else
  141. {
  142. numBytesUsed = 0;
  143. --sz;
  144. ++src;
  145. }
  146. if (byte >= 0x80)
  147. {
  148. if (byte == 0xf0)
  149. {
  150. const uint8* d = src;
  151. bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength;
  152. int numVariableLengthSysexBytes = 0;
  153. while (d < src + sz)
  154. {
  155. if (*d >= 0x80)
  156. {
  157. if (*d == 0xf7)
  158. {
  159. ++d; // include the trailing 0xf7 when we hit it
  160. break;
  161. }
  162. if (haveReadAllLengthBytes) // if we see a 0x80 bit set after the initial data length
  163. break; // bytes, assume it's the end of the sysex
  164. ++numVariableLengthSysexBytes;
  165. }
  166. else if (! haveReadAllLengthBytes)
  167. {
  168. haveReadAllLengthBytes = true;
  169. ++numVariableLengthSysexBytes;
  170. }
  171. ++d;
  172. }
  173. src += numVariableLengthSysexBytes;
  174. size = 1 + (int) (d - src);
  175. uint8* dest = allocateSpace (size);
  176. *dest = (uint8) byte;
  177. memcpy (dest + 1, src, (size_t) (size - 1));
  178. numBytesUsed += numVariableLengthSysexBytes; // (these aren't counted in the size)
  179. }
  180. else if (byte == 0xff)
  181. {
  182. int n;
  183. const int bytesLeft = readVariableLengthVal (src + 1, n);
  184. size = jmin (sz + 1, n + 2 + bytesLeft);
  185. uint8* dest = allocateSpace (size);
  186. *dest = (uint8) byte;
  187. memcpy (dest + 1, src, (size_t) size - 1);
  188. }
  189. else
  190. {
  191. preallocatedData.asInt32 = 0;
  192. size = getMessageLengthFromFirstByte ((uint8) byte);
  193. preallocatedData.asBytes[0] = (uint8) byte;
  194. if (size > 1)
  195. {
  196. preallocatedData.asBytes[1] = src[0];
  197. if (size > 2)
  198. preallocatedData.asBytes[2] = src[1];
  199. }
  200. }
  201. numBytesUsed += size;
  202. }
  203. else
  204. {
  205. preallocatedData.asInt32 = 0;
  206. size = 0;
  207. }
  208. }
  209. MidiMessage& MidiMessage::operator= (const MidiMessage& other)
  210. {
  211. if (this != &other)
  212. {
  213. timeStamp = other.timeStamp;
  214. size = other.size;
  215. if (other.allocatedData != nullptr)
  216. {
  217. allocatedData.malloc ((size_t) size);
  218. memcpy (allocatedData, other.allocatedData, (size_t) size);
  219. }
  220. else
  221. {
  222. allocatedData.free();
  223. preallocatedData.asInt32 = other.preallocatedData.asInt32;
  224. }
  225. }
  226. return *this;
  227. }
  228. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  229. MidiMessage::MidiMessage (MidiMessage&& other) noexcept
  230. : timeStamp (other.timeStamp), size (other.size)
  231. {
  232. if (other.allocatedData != nullptr)
  233. allocatedData.swapWith (other.allocatedData);
  234. else
  235. preallocatedData.asInt32 = other.preallocatedData.asInt32;
  236. }
  237. MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept
  238. {
  239. jassert (this != &other); // shouldn't be possible
  240. timeStamp = other.timeStamp;
  241. size = other.size;
  242. allocatedData.swapWith (other.allocatedData);
  243. preallocatedData.asInt32 = other.preallocatedData.asInt32;
  244. return *this;
  245. }
  246. #endif
  247. MidiMessage::~MidiMessage() {}
  248. uint8* MidiMessage::allocateSpace (int bytes)
  249. {
  250. if (bytes > 4)
  251. {
  252. allocatedData.malloc ((size_t) bytes);
  253. return allocatedData;
  254. }
  255. return preallocatedData.asBytes;
  256. }
  257. String MidiMessage::getDescription() const
  258. {
  259. if (isNoteOn()) return "Note on " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
  260. if (isNoteOff()) return "Note off " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
  261. if (isProgramChange()) return "Program change " + String (getProgramChangeNumber()) + " Channel " + String (getChannel());
  262. if (isPitchWheel()) return "Pitch wheel " + String (getPitchWheelValue()) + " Channel " + String (getChannel());
  263. if (isAftertouch()) return "Aftertouch " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + ": " + String (getAfterTouchValue()) + " Channel " + String (getChannel());
  264. if (isChannelPressure()) return "Channel pressure " + String (getChannelPressureValue()) + " Channel " + String (getChannel());
  265. if (isAllNotesOff()) return "All notes off Channel " + String (getChannel());
  266. if (isAllSoundOff()) return "All sound off Channel " + String (getChannel());
  267. if (isMetaEvent()) return "Meta event";
  268. if (isController())
  269. {
  270. String name (MidiMessage::getControllerName (getControllerNumber()));
  271. if (name.isEmpty())
  272. name = String (getControllerNumber());
  273. return "Controller " + name + ": " + String (getControllerValue()) + " Channel " + String (getChannel());
  274. }
  275. return String::toHexString (getRawData(), getRawDataSize());
  276. }
  277. int MidiMessage::getChannel() const noexcept
  278. {
  279. const uint8* const data = getRawData();
  280. if ((data[0] & 0xf0) != 0xf0)
  281. return (data[0] & 0xf) + 1;
  282. return 0;
  283. }
  284. bool MidiMessage::isForChannel (const int channel) const noexcept
  285. {
  286. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  287. const uint8* const data = getRawData();
  288. return ((data[0] & 0xf) == channel - 1)
  289. && ((data[0] & 0xf0) != 0xf0);
  290. }
  291. void MidiMessage::setChannel (const int channel) noexcept
  292. {
  293. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  294. uint8* const data = getData();
  295. if ((data[0] & 0xf0) != (uint8) 0xf0)
  296. data[0] = (uint8) ((data[0] & (uint8) 0xf0)
  297. | (uint8)(channel - 1));
  298. }
  299. bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept
  300. {
  301. const uint8* const data = getRawData();
  302. return ((data[0] & 0xf0) == 0x90)
  303. && (returnTrueForVelocity0 || data[2] != 0);
  304. }
  305. bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept
  306. {
  307. const uint8* const data = getRawData();
  308. return ((data[0] & 0xf0) == 0x80)
  309. || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90));
  310. }
  311. bool MidiMessage::isNoteOnOrOff() const noexcept
  312. {
  313. const uint8* const data = getRawData();
  314. const int d = data[0] & 0xf0;
  315. return (d == 0x90) || (d == 0x80);
  316. }
  317. int MidiMessage::getNoteNumber() const noexcept
  318. {
  319. return getRawData()[1];
  320. }
  321. void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept
  322. {
  323. if (isNoteOnOrOff() || isAftertouch())
  324. getData()[1] = (uint8) (newNoteNumber & 127);
  325. }
  326. uint8 MidiMessage::getVelocity() const noexcept
  327. {
  328. if (isNoteOnOrOff())
  329. return getRawData()[2];
  330. return 0;
  331. }
  332. float MidiMessage::getFloatVelocity() const noexcept
  333. {
  334. return getVelocity() * (1.0f / 127.0f);
  335. }
  336. void MidiMessage::setVelocity (const float newVelocity) noexcept
  337. {
  338. if (isNoteOnOrOff())
  339. getData()[2] = MidiHelpers::floatVelocityToByte (newVelocity);
  340. }
  341. void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept
  342. {
  343. if (isNoteOnOrOff())
  344. {
  345. uint8* const data = getData();
  346. data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2]));
  347. }
  348. }
  349. bool MidiMessage::isAftertouch() const noexcept
  350. {
  351. return (getRawData()[0] & 0xf0) == 0xa0;
  352. }
  353. int MidiMessage::getAfterTouchValue() const noexcept
  354. {
  355. jassert (isAftertouch());
  356. return getRawData()[2];
  357. }
  358. MidiMessage MidiMessage::aftertouchChange (const int channel,
  359. const int noteNum,
  360. const int aftertouchValue) noexcept
  361. {
  362. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  363. jassert (isPositiveAndBelow (noteNum, (int) 128));
  364. jassert (isPositiveAndBelow (aftertouchValue, (int) 128));
  365. return MidiMessage (MidiHelpers::initialByte (0xa0, channel),
  366. noteNum & 0x7f,
  367. aftertouchValue & 0x7f);
  368. }
  369. bool MidiMessage::isChannelPressure() const noexcept
  370. {
  371. return (getRawData()[0] & 0xf0) == 0xd0;
  372. }
  373. int MidiMessage::getChannelPressureValue() const noexcept
  374. {
  375. jassert (isChannelPressure());
  376. return getRawData()[1];
  377. }
  378. MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept
  379. {
  380. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  381. jassert (isPositiveAndBelow (pressure, (int) 128));
  382. return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
  383. }
  384. bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; }
  385. bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; }
  386. bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; }
  387. bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; }
  388. bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; }
  389. bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; }
  390. bool MidiMessage::isProgramChange() const noexcept
  391. {
  392. return (getRawData()[0] & 0xf0) == 0xc0;
  393. }
  394. int MidiMessage::getProgramChangeNumber() const noexcept
  395. {
  396. jassert (isProgramChange());
  397. return getRawData()[1];
  398. }
  399. MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept
  400. {
  401. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  402. return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f);
  403. }
  404. bool MidiMessage::isPitchWheel() const noexcept
  405. {
  406. return (getRawData()[0] & 0xf0) == 0xe0;
  407. }
  408. int MidiMessage::getPitchWheelValue() const noexcept
  409. {
  410. jassert (isPitchWheel());
  411. const uint8* const data = getRawData();
  412. return data[1] | (data[2] << 7);
  413. }
  414. MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept
  415. {
  416. jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
  417. jassert (isPositiveAndBelow (position, (int) 0x4000));
  418. return MidiMessage (MidiHelpers::initialByte (0xe0, channel),
  419. position & 127, (position >> 7) & 127);
  420. }
  421. bool MidiMessage::isController() const noexcept
  422. {
  423. return (getRawData()[0] & 0xf0) == 0xb0;
  424. }
  425. bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
  426. {
  427. const uint8* const data = getRawData();
  428. return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
  429. }
  430. int MidiMessage::getControllerNumber() const noexcept
  431. {
  432. jassert (isController());
  433. return getRawData()[1];
  434. }
  435. int MidiMessage::getControllerValue() const noexcept
  436. {
  437. jassert (isController());
  438. return getRawData()[2];
  439. }
  440. MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept
  441. {
  442. // the channel must be between 1 and 16 inclusive
  443. jassert (channel > 0 && channel <= 16);
  444. return MidiMessage (MidiHelpers::initialByte (0xb0, channel),
  445. controllerType & 127, value & 127);
  446. }
  447. MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept
  448. {
  449. jassert (channel > 0 && channel <= 16);
  450. jassert (isPositiveAndBelow (noteNumber, (int) 128));
  451. return MidiMessage (MidiHelpers::initialByte (0x90, channel),
  452. noteNumber & 127, MidiHelpers::validVelocity (velocity));
  453. }
  454. MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept
  455. {
  456. return noteOn (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity));
  457. }
  458. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept
  459. {
  460. jassert (channel > 0 && channel <= 16);
  461. jassert (isPositiveAndBelow (noteNumber, (int) 128));
  462. return MidiMessage (MidiHelpers::initialByte (0x80, channel),
  463. noteNumber & 127, MidiHelpers::validVelocity (velocity));
  464. }
  465. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept
  466. {
  467. return noteOff (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity));
  468. }
  469. MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept
  470. {
  471. jassert (channel > 0 && channel <= 16);
  472. jassert (isPositiveAndBelow (noteNumber, (int) 128));
  473. return MidiMessage (MidiHelpers::initialByte (0x80, channel), noteNumber & 127, 0);
  474. }
  475. MidiMessage MidiMessage::allNotesOff (const int channel) noexcept
  476. {
  477. return controllerEvent (channel, 123, 0);
  478. }
  479. bool MidiMessage::isAllNotesOff() const noexcept
  480. {
  481. const uint8* const data = getRawData();
  482. return (data[0] & 0xf0) == 0xb0 && data[1] == 123;
  483. }
  484. MidiMessage MidiMessage::allSoundOff (const int channel) noexcept
  485. {
  486. return controllerEvent (channel, 120, 0);
  487. }
  488. bool MidiMessage::isAllSoundOff() const noexcept
  489. {
  490. const uint8* const data = getRawData();
  491. return (data[0] & 0xf0) == 0xb0 && data[1] == 120;
  492. }
  493. MidiMessage MidiMessage::allControllersOff (const int channel) noexcept
  494. {
  495. return controllerEvent (channel, 121, 0);
  496. }
  497. MidiMessage MidiMessage::masterVolume (const float volume)
  498. {
  499. const int vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000));
  500. const uint8 buf[] = { 0xf0, 0x7f, 0x7f, 0x04, 0x01,
  501. (uint8) (vol & 0x7f),
  502. (uint8) (vol >> 7),
  503. 0xf7 };
  504. return MidiMessage (buf, 8);
  505. }
  506. //==============================================================================
  507. bool MidiMessage::isSysEx() const noexcept
  508. {
  509. return *getRawData() == 0xf0;
  510. }
  511. MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize)
  512. {
  513. HeapBlock<uint8> m ((size_t) dataSize + 2);
  514. m[0] = 0xf0;
  515. memcpy (m + 1, sysexData, (size_t) dataSize);
  516. m[dataSize + 1] = 0xf7;
  517. return MidiMessage (m, dataSize + 2);
  518. }
  519. const uint8* MidiMessage::getSysExData() const noexcept
  520. {
  521. return isSysEx() ? getRawData() + 1 : nullptr;
  522. }
  523. int MidiMessage::getSysExDataSize() const noexcept
  524. {
  525. return isSysEx() ? size - 2 : 0;
  526. }
  527. //==============================================================================
  528. bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; }
  529. bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; }
  530. int MidiMessage::getMetaEventType() const noexcept
  531. {
  532. const uint8* const data = getRawData();
  533. return *data != 0xff ? -1 : data[1];
  534. }
  535. int MidiMessage::getMetaEventLength() const noexcept
  536. {
  537. const uint8* const data = getRawData();
  538. if (*data == 0xff)
  539. {
  540. int n;
  541. return jmin (size - 2, readVariableLengthVal (data + 2, n));
  542. }
  543. return 0;
  544. }
  545. const uint8* MidiMessage::getMetaEventData() const noexcept
  546. {
  547. jassert (isMetaEvent());
  548. int n;
  549. const uint8* d = getRawData() + 2;
  550. readVariableLengthVal (d, n);
  551. return d + n;
  552. }
  553. bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; }
  554. bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; }
  555. bool MidiMessage::isTextMetaEvent() const noexcept
  556. {
  557. const int t = getMetaEventType();
  558. return t > 0 && t < 16;
  559. }
  560. String MidiMessage::getTextFromTextMetaEvent() const
  561. {
  562. const char* const textData = reinterpret_cast<const char*> (getMetaEventData());
  563. return String (CharPointer_UTF8 (textData),
  564. CharPointer_UTF8 (textData + getMetaEventLength()));
  565. }
  566. MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
  567. {
  568. jassert (type > 0 && type < 16);
  569. MidiMessage result;
  570. const size_t textSize = text.text.sizeInBytes() - 1;
  571. uint8 header[8];
  572. size_t n = sizeof (header);
  573. header[--n] = (uint8) (textSize & 0x7f);
  574. for (size_t i = textSize; (i >>= 7) != 0;)
  575. header[--n] = (uint8) ((i & 0x7f) | 0x80);
  576. header[--n] = (uint8) type;
  577. header[--n] = 0xff;
  578. const size_t headerLen = sizeof (header) - n;
  579. uint8* const dest = result.allocateSpace ((int) (headerLen + textSize));
  580. result.size = (int) (headerLen + textSize);
  581. memcpy (dest, header + n, headerLen);
  582. memcpy (dest + headerLen, text.text.getAddress(), textSize);
  583. return result;
  584. }
  585. bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); }
  586. bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); }
  587. bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); }
  588. int MidiMessage::getMidiChannelMetaEventChannel() const noexcept
  589. {
  590. jassert (isMidiChannelMetaEvent());
  591. return getRawData()[3] + 1;
  592. }
  593. double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept
  594. {
  595. if (! isTempoMetaEvent())
  596. return 0.0;
  597. const uint8* const d = getMetaEventData();
  598. return (((unsigned int) d[0] << 16)
  599. | ((unsigned int) d[1] << 8)
  600. | d[2])
  601. / 1000000.0;
  602. }
  603. double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept
  604. {
  605. if (timeFormat > 0)
  606. {
  607. if (! isTempoMetaEvent())
  608. return 0.5 / timeFormat;
  609. return getTempoSecondsPerQuarterNote() / timeFormat;
  610. }
  611. else
  612. {
  613. const int frameCode = (-timeFormat) >> 8;
  614. double framesPerSecond;
  615. switch (frameCode)
  616. {
  617. case 24: framesPerSecond = 24.0; break;
  618. case 25: framesPerSecond = 25.0; break;
  619. case 29: framesPerSecond = 29.97; break;
  620. case 30: framesPerSecond = 30.0; break;
  621. default: framesPerSecond = 30.0; break;
  622. }
  623. return (1.0 / framesPerSecond) / (timeFormat & 0xff);
  624. }
  625. }
  626. MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept
  627. {
  628. const uint8 d[] = { 0xff, 81, 3,
  629. (uint8) (microsecondsPerQuarterNote >> 16),
  630. (uint8) (microsecondsPerQuarterNote >> 8),
  631. (uint8) microsecondsPerQuarterNote };
  632. return MidiMessage (d, 6, 0.0);
  633. }
  634. bool MidiMessage::isTimeSignatureMetaEvent() const noexcept
  635. {
  636. const uint8* const data = getRawData();
  637. return (data[1] == 0x58) && (*data == (uint8) 0xff);
  638. }
  639. void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept
  640. {
  641. if (isTimeSignatureMetaEvent())
  642. {
  643. const uint8* const d = getMetaEventData();
  644. numerator = d[0];
  645. denominator = 1 << d[1];
  646. }
  647. else
  648. {
  649. numerator = 4;
  650. denominator = 4;
  651. }
  652. }
  653. MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator)
  654. {
  655. int n = 1;
  656. int powerOfTwo = 0;
  657. while (n < denominator)
  658. {
  659. n <<= 1;
  660. ++powerOfTwo;
  661. }
  662. const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, (uint8) powerOfTwo, 1, 96 };
  663. return MidiMessage (d, 7, 0.0);
  664. }
  665. MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept
  666. {
  667. const uint8 d[] = { 0xff, 0x20, 0x01, (uint8) jlimit (0, 0xff, channel - 1) };
  668. return MidiMessage (d, 4, 0.0);
  669. }
  670. bool MidiMessage::isKeySignatureMetaEvent() const noexcept
  671. {
  672. return getMetaEventType() == 0x59;
  673. }
  674. int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept
  675. {
  676. return (int) getMetaEventData()[0];
  677. }
  678. bool MidiMessage::isKeySignatureMajorKey() const noexcept
  679. {
  680. return getMetaEventData()[1] == 0;
  681. }
  682. MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey)
  683. {
  684. jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7);
  685. const uint8 d[] = { 0xff, 0x59, 0x02, (uint8) numberOfSharpsOrFlats, isMinorKey ? (uint8) 1 : (uint8) 0 };
  686. return MidiMessage (d, 5, 0.0);
  687. }
  688. MidiMessage MidiMessage::endOfTrack() noexcept
  689. {
  690. return MidiMessage (0xff, 0x2f, 0, 0.0);
  691. }
  692. //==============================================================================
  693. bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; }
  694. int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getRawData(); return data[1] | (data[2] << 7); }
  695. MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept
  696. {
  697. return MidiMessage (0xf2,
  698. positionInMidiBeats & 127,
  699. (positionInMidiBeats >> 7) & 127);
  700. }
  701. bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; }
  702. MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); }
  703. bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; }
  704. MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); }
  705. bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; }
  706. MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); }
  707. bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; }
  708. MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); }
  709. bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; }
  710. int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; }
  711. int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; }
  712. MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept
  713. {
  714. return MidiMessage (0xf1, (sequenceNumber << 4) | value);
  715. }
  716. bool MidiMessage::isFullFrame() const noexcept
  717. {
  718. const uint8* const data = getRawData();
  719. return data[0] == 0xf0
  720. && data[1] == 0x7f
  721. && size >= 10
  722. && data[3] == 0x01
  723. && data[4] == 0x01;
  724. }
  725. void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames,
  726. MidiMessage::SmpteTimecodeType& timecodeType) const noexcept
  727. {
  728. jassert (isFullFrame());
  729. const uint8* const data = getRawData();
  730. timecodeType = (SmpteTimecodeType) (data[5] >> 5);
  731. hours = data[5] & 0x1f;
  732. minutes = data[6];
  733. seconds = data[7];
  734. frames = data[8];
  735. }
  736. MidiMessage MidiMessage::fullFrame (const int hours, const int minutes,
  737. const int seconds, const int frames,
  738. MidiMessage::SmpteTimecodeType timecodeType)
  739. {
  740. const uint8 d[] = { 0xf0, 0x7f, 0x7f, 0x01, 0x01,
  741. (uint8) ((hours & 0x01f) | (timecodeType << 5)),
  742. (uint8) minutes,
  743. (uint8) seconds,
  744. (uint8) frames,
  745. 0xf7 };
  746. return MidiMessage (d, 10, 0.0);
  747. }
  748. bool MidiMessage::isMidiMachineControlMessage() const noexcept
  749. {
  750. const uint8* const data = getRawData();
  751. return data[0] == 0xf0
  752. && data[1] == 0x7f
  753. && data[3] == 0x06
  754. && size > 5;
  755. }
  756. MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept
  757. {
  758. jassert (isMidiMachineControlMessage());
  759. return (MidiMachineControlCommand) getRawData()[4];
  760. }
  761. MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command)
  762. {
  763. const uint8 d[] = { 0xf0, 0x7f, 0, 6, (uint8) command, 0xf7 };
  764. return MidiMessage (d, 6, 0.0);
  765. }
  766. //==============================================================================
  767. bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept
  768. {
  769. const uint8* const data = getRawData();
  770. if (size >= 12
  771. && data[0] == 0xf0
  772. && data[1] == 0x7f
  773. && data[3] == 0x06
  774. && data[4] == 0x44
  775. && data[5] == 0x06
  776. && data[6] == 0x01)
  777. {
  778. hours = data[7] % 24; // (that some machines send out hours > 24)
  779. minutes = data[8];
  780. seconds = data[9];
  781. frames = data[10];
  782. return true;
  783. }
  784. return false;
  785. }
  786. MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames)
  787. {
  788. const uint8 d[] = { 0xf0, 0x7f, 0, 6, 0x44, 6, 1,
  789. (uint8) hours,
  790. (uint8) minutes,
  791. (uint8) seconds,
  792. (uint8) frames,
  793. 0xf7 };
  794. return MidiMessage (d, 12, 0.0);
  795. }
  796. //==============================================================================
  797. String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
  798. {
  799. static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
  800. static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" };
  801. if (isPositiveAndBelow (note, (int) 128))
  802. {
  803. String s (useSharps ? sharpNoteNames [note % 12]
  804. : flatNoteNames [note % 12]);
  805. if (includeOctaveNumber)
  806. s << (note / 12 + (octaveNumForMiddleC - 5));
  807. return s;
  808. }
  809. return String();
  810. }
  811. double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept
  812. {
  813. return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0);
  814. }
  815. bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept
  816. {
  817. return ((1 << (noteNumber % 12)) & 0x054a) != 0;
  818. }
  819. const char* MidiMessage::getGMInstrumentName (const int n)
  820. {
  821. static const char* names[] =
  822. {
  823. NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"),
  824. NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"),
  825. NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"),
  826. NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"),
  827. NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"),
  828. NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"),
  829. NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"),
  830. NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"),
  831. NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"),
  832. NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"),
  833. NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"),
  834. NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"),
  835. NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"),
  836. NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"),
  837. NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"),
  838. NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"),
  839. NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"),
  840. NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"),
  841. NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"),
  842. NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"),
  843. NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"),
  844. NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"),
  845. NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"),
  846. NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"),
  847. NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"),
  848. NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"),
  849. NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"),
  850. NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"),
  851. NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"),
  852. NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"),
  853. NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"),
  854. NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot")
  855. };
  856. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  857. }
  858. const char* MidiMessage::getGMInstrumentBankName (const int n)
  859. {
  860. static const char* names[] =
  861. {
  862. NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"),
  863. NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"),
  864. NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"),
  865. NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects")
  866. };
  867. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  868. }
  869. const char* MidiMessage::getRhythmInstrumentName (const int n)
  870. {
  871. static const char* names[] =
  872. {
  873. NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"),
  874. NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"),
  875. NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"),
  876. NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"),
  877. NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"),
  878. NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"),
  879. NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"),
  880. NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"),
  881. NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"),
  882. NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"),
  883. NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"),
  884. NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle")
  885. };
  886. return (n >= 35 && n <= 81) ? names [n - 35] : nullptr;
  887. }
  888. const char* MidiMessage::getControllerName (const int n)
  889. {
  890. static const char* names[] =
  891. {
  892. NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"),
  893. nullptr,
  894. NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"),
  895. NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"),
  896. nullptr,
  897. NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"),
  898. NEEDS_TRANS("Effect Control 2 (coarse)"),
  899. nullptr, nullptr,
  900. NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"),
  901. NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"),
  902. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  903. NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"),
  904. nullptr,
  905. NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"),
  906. NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"),
  907. NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"),
  908. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  909. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  910. NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"),
  911. NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"),
  912. NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"),
  913. NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"),
  914. NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"),
  915. NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"),
  916. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  917. NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"),
  918. NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"),
  919. NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"),
  920. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  921. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  922. NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"),
  923. NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation")
  924. };
  925. return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
  926. }