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