1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - 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 {
initialByte(const int type,const int channel)28     inline uint8 initialByte (const int type, const int channel) noexcept
29     {
30         return (uint8) (type | jlimit (0, 15, channel - 1));
31     }
32 
validVelocity(const int v)33     inline uint8 validVelocity (const int v) noexcept
34     {
35         return (uint8) jlimit (0, 127, v);
36     }
37 }
38 
39 //==============================================================================
floatValueToMidiByte(const float v)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 
pitchbendToPitchwheelPos(const float pitchbend,const float pitchbendRange)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 //==============================================================================
readVariableLengthValue(const uint8 * data,int maxBytesToUse)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 
readVariableLengthVal(const uint8 * data,int & numBytesUsed)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 
getMessageLengthFromFirstByte(const uint8 firstByte)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 //==============================================================================
MidiMessage()125 MidiMessage::MidiMessage() noexcept
126    : size (2)
127 {
128     packedData.asBytes[0] = 0xf0;
129     packedData.asBytes[1] = 0xf7;
130 }
131 
MidiMessage(const void * const d,const int dataSize,const double t)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 
MidiMessage(const int byte1,const double t)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 
MidiMessage(const int byte1,const int byte2,const double t)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 
MidiMessage(const int byte1,const int byte2,const int byte3,const double t)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 
MidiMessage(const MidiMessage & other)172 MidiMessage::MidiMessage (const MidiMessage& other)
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 
MidiMessage(const MidiMessage & other,const double newTimeStamp)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 
MidiMessage(const void * srcData,int sz,int & numBytesUsed,const uint8 lastStatusByte,double t,bool sysexHasEmbeddedLength)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 
operator =(const MidiMessage & other)284 MidiMessage& MidiMessage::operator= (const MidiMessage& other)
285 {
286     if (this != &other)
287     {
288         if (other.isHeapAllocated())
289         {
290             if (isHeapAllocated())
291                 packedData.allocatedData = static_cast<uint8*> (std::realloc (packedData.allocatedData, (size_t) other.size));
292             else
293                 packedData.allocatedData = static_cast<uint8*> (std::malloc ((size_t) other.size));
294 
295             memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size);
296         }
297         else
298         {
299             if (isHeapAllocated())
300                 std::free (packedData.allocatedData);
301 
302             packedData.allocatedData = other.packedData.allocatedData;
303         }
304 
305         timeStamp = other.timeStamp;
306         size = other.size;
307     }
308 
309     return *this;
310 }
311 
MidiMessage(MidiMessage && other)312 MidiMessage::MidiMessage (MidiMessage&& other) noexcept
313    : timeStamp (other.timeStamp), size (other.size)
314 {
315     packedData.allocatedData = other.packedData.allocatedData;
316     other.size = 0;
317 }
318 
operator =(MidiMessage && other)319 MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept
320 {
321     packedData.allocatedData = other.packedData.allocatedData;
322     timeStamp = other.timeStamp;
323     size = other.size;
324     other.size = 0;
325     return *this;
326 }
327 
~MidiMessage()328 MidiMessage::~MidiMessage() noexcept
329 {
330     if (isHeapAllocated())
331         std::free (packedData.allocatedData);
332 }
333 
allocateSpace(int bytes)334 uint8* MidiMessage::allocateSpace (int bytes)
335 {
336     if (bytes > (int) sizeof (packedData))
337     {
338         auto d = static_cast<uint8*> (std::malloc ((size_t) bytes));
339         packedData.allocatedData = d;
340         return d;
341     }
342 
343     return packedData.asBytes;
344 }
345 
getDescription() const346 String MidiMessage::getDescription() const
347 {
348     if (isNoteOn())           return "Note on "  + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
349     if (isNoteOff())          return "Note off " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
350     if (isProgramChange())    return "Program change " + String (getProgramChangeNumber()) + " Channel " + String (getChannel());
351     if (isPitchWheel())       return "Pitch wheel " + String (getPitchWheelValue()) + " Channel " + String (getChannel());
352     if (isAftertouch())       return "Aftertouch " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) +  ": " + String (getAfterTouchValue()) + " Channel " + String (getChannel());
353     if (isChannelPressure())  return "Channel pressure " + String (getChannelPressureValue()) + " Channel " + String (getChannel());
354     if (isAllNotesOff())      return "All notes off Channel " + String (getChannel());
355     if (isAllSoundOff())      return "All sound off Channel " + String (getChannel());
356     if (isMetaEvent())        return "Meta event";
357 
358     if (isController())
359     {
360         String name (MidiMessage::getControllerName (getControllerNumber()));
361 
362         if (name.isEmpty())
363             name = String (getControllerNumber());
364 
365         return "Controller " + name + ": " + String (getControllerValue()) + " Channel " + String (getChannel());
366     }
367 
368     return String::toHexString (getRawData(), getRawDataSize());
369 }
370 
withTimeStamp(double newTimestamp) const371 MidiMessage MidiMessage::withTimeStamp (double newTimestamp) const
372 {
373     return { *this, newTimestamp };
374 }
375 
getChannel() const376 int MidiMessage::getChannel() const noexcept
377 {
378     auto data = getRawData();
379 
380     if ((data[0] & 0xf0) != 0xf0)
381         return (data[0] & 0xf) + 1;
382 
383     return 0;
384 }
385 
isForChannel(const int channel) const386 bool MidiMessage::isForChannel (const int channel) const noexcept
387 {
388     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
389 
390     auto data = getRawData();
391 
392     return ((data[0] & 0xf) == channel - 1)
393              && ((data[0] & 0xf0) != 0xf0);
394 }
395 
setChannel(const int channel)396 void MidiMessage::setChannel (const int channel) noexcept
397 {
398     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
399 
400     auto data = getData();
401 
402     if ((data[0] & 0xf0) != (uint8) 0xf0)
403         data[0] = (uint8) ((data[0] & (uint8) 0xf0)
404                             | (uint8)(channel - 1));
405 }
406 
isNoteOn(const bool returnTrueForVelocity0) const407 bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept
408 {
409     auto data = getRawData();
410 
411     return ((data[0] & 0xf0) == 0x90)
412              && (returnTrueForVelocity0 || data[2] != 0);
413 }
414 
isNoteOff(const bool returnTrueForNoteOnVelocity0) const415 bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept
416 {
417     auto data = getRawData();
418 
419     return ((data[0] & 0xf0) == 0x80)
420             || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90));
421 }
422 
isNoteOnOrOff() const423 bool MidiMessage::isNoteOnOrOff() const noexcept
424 {
425     auto d = getRawData()[0] & 0xf0;
426     return (d == 0x90) || (d == 0x80);
427 }
428 
getNoteNumber() const429 int MidiMessage::getNoteNumber() const noexcept
430 {
431     return getRawData()[1];
432 }
433 
setNoteNumber(const int newNoteNumber)434 void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept
435 {
436     if (isNoteOnOrOff() || isAftertouch())
437         getData()[1] = (uint8) (newNoteNumber & 127);
438 }
439 
getVelocity() const440 uint8 MidiMessage::getVelocity() const noexcept
441 {
442     if (isNoteOnOrOff())
443         return getRawData()[2];
444 
445     return 0;
446 }
447 
getFloatVelocity() const448 float MidiMessage::getFloatVelocity() const noexcept
449 {
450     return getVelocity() * (1.0f / 127.0f);
451 }
452 
setVelocity(const float newVelocity)453 void MidiMessage::setVelocity (const float newVelocity) noexcept
454 {
455     if (isNoteOnOrOff())
456         getData()[2] = floatValueToMidiByte (newVelocity);
457 }
458 
multiplyVelocity(const float scaleFactor)459 void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept
460 {
461     if (isNoteOnOrOff())
462     {
463         auto data = getData();
464         data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2]));
465     }
466 }
467 
isAftertouch() const468 bool MidiMessage::isAftertouch() const noexcept
469 {
470     return (getRawData()[0] & 0xf0) == 0xa0;
471 }
472 
getAfterTouchValue() const473 int MidiMessage::getAfterTouchValue() const noexcept
474 {
475     jassert (isAftertouch());
476     return getRawData()[2];
477 }
478 
aftertouchChange(const int channel,const int noteNum,const int aftertouchValue)479 MidiMessage MidiMessage::aftertouchChange (const int channel,
480                                            const int noteNum,
481                                            const int aftertouchValue) noexcept
482 {
483     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
484     jassert (isPositiveAndBelow (noteNum, 128));
485     jassert (isPositiveAndBelow (aftertouchValue, 128));
486 
487     return MidiMessage (MidiHelpers::initialByte (0xa0, channel),
488                         noteNum & 0x7f,
489                         aftertouchValue & 0x7f);
490 }
491 
isChannelPressure() const492 bool MidiMessage::isChannelPressure() const noexcept
493 {
494     return (getRawData()[0] & 0xf0) == 0xd0;
495 }
496 
getChannelPressureValue() const497 int MidiMessage::getChannelPressureValue() const noexcept
498 {
499     jassert (isChannelPressure());
500     return getRawData()[1];
501 }
502 
channelPressureChange(const int channel,const int pressure)503 MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept
504 {
505     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
506     jassert (isPositiveAndBelow (pressure, 128));
507 
508     return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f);
509 }
510 
isSustainPedalOn() const511 bool MidiMessage::isSustainPedalOn() const noexcept     { return isControllerOfType (0x40) && getRawData()[2] >= 64; }
isSustainPedalOff() const512 bool MidiMessage::isSustainPedalOff() const noexcept    { return isControllerOfType (0x40) && getRawData()[2] <  64; }
513 
isSostenutoPedalOn() const514 bool MidiMessage::isSostenutoPedalOn() const noexcept   { return isControllerOfType (0x42) && getRawData()[2] >= 64; }
isSostenutoPedalOff() const515 bool MidiMessage::isSostenutoPedalOff() const noexcept  { return isControllerOfType (0x42) && getRawData()[2] <  64; }
516 
isSoftPedalOn() const517 bool MidiMessage::isSoftPedalOn() const noexcept        { return isControllerOfType (0x43) && getRawData()[2] >= 64; }
isSoftPedalOff() const518 bool MidiMessage::isSoftPedalOff() const noexcept       { return isControllerOfType (0x43) && getRawData()[2] <  64; }
519 
520 
isProgramChange() const521 bool MidiMessage::isProgramChange() const noexcept
522 {
523     return (getRawData()[0] & 0xf0) == 0xc0;
524 }
525 
getProgramChangeNumber() const526 int MidiMessage::getProgramChangeNumber() const noexcept
527 {
528     jassert (isProgramChange());
529     return getRawData()[1];
530 }
531 
programChange(const int channel,const int programNumber)532 MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept
533 {
534     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
535 
536     return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f);
537 }
538 
isPitchWheel() const539 bool MidiMessage::isPitchWheel() const noexcept
540 {
541     return (getRawData()[0] & 0xf0) == 0xe0;
542 }
543 
getPitchWheelValue() const544 int MidiMessage::getPitchWheelValue() const noexcept
545 {
546     jassert (isPitchWheel());
547     auto data = getRawData();
548     return data[1] | (data[2] << 7);
549 }
550 
pitchWheel(const int channel,const int position)551 MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept
552 {
553     jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16
554     jassert (isPositiveAndBelow (position, 0x4000));
555 
556     return MidiMessage (MidiHelpers::initialByte (0xe0, channel),
557                         position & 127, (position >> 7) & 127);
558 }
559 
isController() const560 bool MidiMessage::isController() const noexcept
561 {
562     return (getRawData()[0] & 0xf0) == 0xb0;
563 }
564 
isControllerOfType(const int controllerType) const565 bool MidiMessage::isControllerOfType (const int controllerType) const noexcept
566 {
567     auto data = getRawData();
568     return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType;
569 }
570 
getControllerNumber() const571 int MidiMessage::getControllerNumber() const noexcept
572 {
573     jassert (isController());
574     return getRawData()[1];
575 }
576 
getControllerValue() const577 int MidiMessage::getControllerValue() const noexcept
578 {
579     jassert (isController());
580     return getRawData()[2];
581 }
582 
controllerEvent(const int channel,const int controllerType,const int value)583 MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept
584 {
585     // the channel must be between 1 and 16 inclusive
586     jassert (channel > 0 && channel <= 16);
587 
588     return MidiMessage (MidiHelpers::initialByte (0xb0, channel),
589                         controllerType & 127, value & 127);
590 }
591 
noteOn(const int channel,const int noteNumber,const uint8 velocity)592 MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept
593 {
594     jassert (channel > 0 && channel <= 16);
595     jassert (isPositiveAndBelow (noteNumber, 128));
596 
597     return MidiMessage (MidiHelpers::initialByte (0x90, channel),
598                         noteNumber & 127, MidiHelpers::validVelocity (velocity));
599 }
600 
noteOn(const int channel,const int noteNumber,const float velocity)601 MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept
602 {
603     return noteOn (channel, noteNumber, floatValueToMidiByte (velocity));
604 }
605 
noteOff(const int channel,const int noteNumber,uint8 velocity)606 MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept
607 {
608     jassert (channel > 0 && channel <= 16);
609     jassert (isPositiveAndBelow (noteNumber, 128));
610 
611     return MidiMessage (MidiHelpers::initialByte (0x80, channel),
612                         noteNumber & 127, MidiHelpers::validVelocity (velocity));
613 }
614 
noteOff(const int channel,const int noteNumber,float velocity)615 MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept
616 {
617     return noteOff (channel, noteNumber, floatValueToMidiByte (velocity));
618 }
619 
noteOff(const int channel,const int noteNumber)620 MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept
621 {
622     jassert (channel > 0 && channel <= 16);
623     jassert (isPositiveAndBelow (noteNumber, 128));
624 
625     return MidiMessage (MidiHelpers::initialByte (0x80, channel), noteNumber & 127, 0);
626 }
627 
allNotesOff(const int channel)628 MidiMessage MidiMessage::allNotesOff (const int channel) noexcept
629 {
630     return controllerEvent (channel, 123, 0);
631 }
632 
isAllNotesOff() const633 bool MidiMessage::isAllNotesOff() const noexcept
634 {
635     auto data = getRawData();
636     return (data[0] & 0xf0) == 0xb0 && data[1] == 123;
637 }
638 
allSoundOff(const int channel)639 MidiMessage MidiMessage::allSoundOff (const int channel) noexcept
640 {
641     return controllerEvent (channel, 120, 0);
642 }
643 
isAllSoundOff() const644 bool MidiMessage::isAllSoundOff() const noexcept
645 {
646     auto data = getRawData();
647     return data[1] == 120 && (data[0] & 0xf0) == 0xb0;
648 }
649 
isResetAllControllers() const650 bool MidiMessage::isResetAllControllers() const noexcept
651 {
652     auto data = getRawData();
653     return (data[0] & 0xf0) == 0xb0 && data[1] == 121;
654 }
655 
allControllersOff(const int channel)656 MidiMessage MidiMessage::allControllersOff (const int channel) noexcept
657 {
658     return controllerEvent (channel, 121, 0);
659 }
660 
masterVolume(const float volume)661 MidiMessage MidiMessage::masterVolume (const float volume)
662 {
663     auto vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000));
664 
665     return { 0xf0, 0x7f, 0x7f, 0x04, 0x01, vol & 0x7f, vol >> 7, 0xf7 };
666 }
667 
668 //==============================================================================
isSysEx() const669 bool MidiMessage::isSysEx() const noexcept
670 {
671     return *getRawData() == 0xf0;
672 }
673 
createSysExMessage(const void * sysexData,const int dataSize)674 MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize)
675 {
676     HeapBlock<uint8> m (dataSize + 2);
677 
678     m[0] = 0xf0;
679     memcpy (m + 1, sysexData, (size_t) dataSize);
680     m[dataSize + 1] = 0xf7;
681 
682     return MidiMessage (m, dataSize + 2);
683 }
684 
getSysExData() const685 const uint8* MidiMessage::getSysExData() const noexcept
686 {
687     return isSysEx() ? getRawData() + 1 : nullptr;
688 }
689 
getSysExDataSize() const690 int MidiMessage::getSysExDataSize() const noexcept
691 {
692     return isSysEx() ? size - 2 : 0;
693 }
694 
695 //==============================================================================
isMetaEvent() const696 bool MidiMessage::isMetaEvent() const noexcept      { return *getRawData() == 0xff; }
isActiveSense() const697 bool MidiMessage::isActiveSense() const noexcept    { return *getRawData() == 0xfe; }
698 
getMetaEventType() const699 int MidiMessage::getMetaEventType() const noexcept
700 {
701     auto data = getRawData();
702     return (size < 2 || *data != 0xff) ? -1 : data[1];
703 }
704 
getMetaEventLength() const705 int MidiMessage::getMetaEventLength() const noexcept
706 {
707     auto data = getRawData();
708 
709     if (*data == 0xff)
710     {
711         const auto var = readVariableLengthValue (data + 2, size - 2);
712         return jmax (0, jmin (size - 2 - var.bytesUsed, var.value));
713     }
714 
715     return 0;
716 }
717 
getMetaEventData() const718 const uint8* MidiMessage::getMetaEventData() const noexcept
719 {
720     jassert (isMetaEvent());
721 
722     auto d = getRawData() + 2;
723     const auto var = readVariableLengthValue (d, size - 2);
724     return d + var.bytesUsed;
725 }
726 
isTrackMetaEvent() const727 bool MidiMessage::isTrackMetaEvent() const noexcept         { return getMetaEventType() == 0; }
isEndOfTrackMetaEvent() const728 bool MidiMessage::isEndOfTrackMetaEvent() const noexcept    { return getMetaEventType() == 47; }
729 
isTextMetaEvent() const730 bool MidiMessage::isTextMetaEvent() const noexcept
731 {
732     auto t = getMetaEventType();
733     return t > 0 && t < 16;
734 }
735 
getTextFromTextMetaEvent() const736 String MidiMessage::getTextFromTextMetaEvent() const
737 {
738     auto textData = reinterpret_cast<const char*> (getMetaEventData());
739 
740     return String (CharPointer_UTF8 (textData),
741                    CharPointer_UTF8 (textData + getMetaEventLength()));
742 }
743 
textMetaEvent(int type,StringRef text)744 MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
745 {
746     jassert (type > 0 && type < 16);
747 
748     MidiMessage result;
749 
750     const size_t textSize = text.text.sizeInBytes() - 1;
751 
752     uint8 header[8];
753     size_t n = sizeof (header);
754 
755     header[--n] = (uint8) (textSize & 0x7f);
756 
757     for (size_t i = textSize; (i >>= 7) != 0;)
758         header[--n] = (uint8) ((i & 0x7f) | 0x80);
759 
760     header[--n] = (uint8) type;
761     header[--n] = 0xff;
762 
763     const size_t headerLen = sizeof (header) - n;
764     const int totalSize = (int) (headerLen + textSize);
765 
766     auto dest = result.allocateSpace (totalSize);
767     result.size = totalSize;
768 
769     memcpy (dest, header + n, headerLen);
770     memcpy (dest + headerLen, text.text.getAddress(), textSize);
771 
772     return result;
773 }
774 
isTrackNameEvent() const775 bool MidiMessage::isTrackNameEvent() const noexcept         { auto data = getRawData(); return (data[1] == 3)    && (*data == 0xff); }
isTempoMetaEvent() const776 bool MidiMessage::isTempoMetaEvent() const noexcept         { auto data = getRawData(); return (data[1] == 81)   && (*data == 0xff); }
isMidiChannelMetaEvent() const777 bool MidiMessage::isMidiChannelMetaEvent() const noexcept   { auto data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); }
778 
getMidiChannelMetaEventChannel() const779 int MidiMessage::getMidiChannelMetaEventChannel() const noexcept
780 {
781     jassert (isMidiChannelMetaEvent());
782     return getRawData()[3] + 1;
783 }
784 
getTempoSecondsPerQuarterNote() const785 double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept
786 {
787     if (! isTempoMetaEvent())
788         return 0.0;
789 
790     auto d = getMetaEventData();
791 
792     return (((unsigned int) d[0] << 16)
793              | ((unsigned int) d[1] << 8)
794              | d[2])
795             / 1000000.0;
796 }
797 
getTempoMetaEventTickLength(const short timeFormat) const798 double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept
799 {
800     if (timeFormat > 0)
801     {
802         if (! isTempoMetaEvent())
803             return 0.5 / timeFormat;
804 
805         return getTempoSecondsPerQuarterNote() / timeFormat;
806     }
807 
808     const int frameCode = (-timeFormat) >> 8;
809     double framesPerSecond;
810 
811     switch (frameCode)
812     {
813         case 24: framesPerSecond = 24.0;   break;
814         case 25: framesPerSecond = 25.0;   break;
815         case 29: framesPerSecond = 30.0 * 1000.0 / 1001.0;  break;
816         case 30: framesPerSecond = 30.0;   break;
817         default: framesPerSecond = 30.0;   break;
818     }
819 
820     return (1.0 / framesPerSecond) / (timeFormat & 0xff);
821 }
822 
tempoMetaEvent(int microsecondsPerQuarterNote)823 MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept
824 {
825     return { 0xff, 81, 3,
826              (uint8) (microsecondsPerQuarterNote >> 16),
827              (uint8) (microsecondsPerQuarterNote >> 8),
828              (uint8) microsecondsPerQuarterNote };
829 }
830 
isTimeSignatureMetaEvent() const831 bool MidiMessage::isTimeSignatureMetaEvent() const noexcept
832 {
833     auto data = getRawData();
834     return (data[1] == 0x58) && (*data == (uint8) 0xff);
835 }
836 
getTimeSignatureInfo(int & numerator,int & denominator) const837 void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept
838 {
839     if (isTimeSignatureMetaEvent())
840     {
841         auto d = getMetaEventData();
842         numerator = d[0];
843         denominator = 1 << d[1];
844     }
845     else
846     {
847         numerator = 4;
848         denominator = 4;
849     }
850 }
851 
timeSignatureMetaEvent(const int numerator,const int denominator)852 MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator)
853 {
854     int n = 1;
855     int powerOfTwo = 0;
856 
857     while (n < denominator)
858     {
859         n <<= 1;
860         ++powerOfTwo;
861     }
862 
863     return { 0xff, 0x58, 0x04, numerator, powerOfTwo, 1, 96 };
864 }
865 
midiChannelMetaEvent(const int channel)866 MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept
867 {
868     return { 0xff, 0x20, 0x01, jlimit (0, 0xff, channel - 1) };
869 }
870 
isKeySignatureMetaEvent() const871 bool MidiMessage::isKeySignatureMetaEvent() const noexcept
872 {
873     return getMetaEventType() == 0x59;
874 }
875 
getKeySignatureNumberOfSharpsOrFlats() const876 int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept
877 {
878     return (int) (int8) getMetaEventData()[0];
879 }
880 
isKeySignatureMajorKey() const881 bool MidiMessage::isKeySignatureMajorKey() const noexcept
882 {
883     return getMetaEventData()[1] == 0;
884 }
885 
keySignatureMetaEvent(int numberOfSharpsOrFlats,bool isMinorKey)886 MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey)
887 {
888     jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7);
889 
890     return { 0xff, 0x59, 0x02, numberOfSharpsOrFlats, isMinorKey ? 1 : 0 };
891 }
892 
endOfTrack()893 MidiMessage MidiMessage::endOfTrack() noexcept
894 {
895     return { 0xff, 0x2f, 0x00 };
896 }
897 
898 //==============================================================================
isSongPositionPointer() const899 bool MidiMessage::isSongPositionPointer() const noexcept         { return *getRawData() == 0xf2; }
getSongPositionPointerMidiBeat() const900 int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { auto data = getRawData(); return data[1] | (data[2] << 7); }
901 
songPositionPointer(const int positionInMidiBeats)902 MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept
903 {
904     return { 0xf2,
905              positionInMidiBeats & 127,
906              (positionInMidiBeats >> 7) & 127 };
907 }
908 
isMidiStart() const909 bool MidiMessage::isMidiStart() const noexcept                  { return *getRawData() == 0xfa; }
midiStart()910 MidiMessage MidiMessage::midiStart() noexcept                   { return MidiMessage (0xfa); }
911 
isMidiContinue() const912 bool MidiMessage::isMidiContinue() const noexcept               { return *getRawData() == 0xfb; }
midiContinue()913 MidiMessage MidiMessage::midiContinue() noexcept                { return MidiMessage (0xfb); }
914 
isMidiStop() const915 bool MidiMessage::isMidiStop() const noexcept                   { return *getRawData() == 0xfc; }
midiStop()916 MidiMessage MidiMessage::midiStop() noexcept                    { return MidiMessage (0xfc); }
917 
isMidiClock() const918 bool MidiMessage::isMidiClock() const noexcept                  { return *getRawData() == 0xf8; }
midiClock()919 MidiMessage MidiMessage::midiClock() noexcept                   { return MidiMessage (0xf8); }
920 
isQuarterFrame() const921 bool MidiMessage::isQuarterFrame() const noexcept               { return *getRawData() == 0xf1; }
getQuarterFrameSequenceNumber() const922 int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; }
getQuarterFrameValue() const923 int MidiMessage::getQuarterFrameValue() const noexcept          { return ((int) getRawData()[1]) & 0x0f; }
924 
quarterFrame(const int sequenceNumber,const int value)925 MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept
926 {
927     return MidiMessage (0xf1, (sequenceNumber << 4) | value);
928 }
929 
isFullFrame() const930 bool MidiMessage::isFullFrame() const noexcept
931 {
932     auto data = getRawData();
933 
934     return data[0] == 0xf0
935             && data[1] == 0x7f
936             && size >= 10
937             && data[3] == 0x01
938             && data[4] == 0x01;
939 }
940 
getFullFrameParameters(int & hours,int & minutes,int & seconds,int & frames,MidiMessage::SmpteTimecodeType & timecodeType) const941 void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames,
942                                           MidiMessage::SmpteTimecodeType& timecodeType) const noexcept
943 {
944     jassert (isFullFrame());
945 
946     auto data = getRawData();
947     timecodeType = (SmpteTimecodeType) (data[5] >> 5);
948     hours   = data[5] & 0x1f;
949     minutes = data[6];
950     seconds = data[7];
951     frames  = data[8];
952 }
953 
fullFrame(int hours,int minutes,int seconds,int frames,MidiMessage::SmpteTimecodeType timecodeType)954 MidiMessage MidiMessage::fullFrame (int hours, int minutes, int seconds, int frames,
955                                     MidiMessage::SmpteTimecodeType timecodeType)
956 {
957     return { 0xf0, 0x7f, 0x7f, 0x01, 0x01,
958              (hours & 0x01f) | (timecodeType << 5),
959              minutes, seconds, frames,
960              0xf7 };
961 }
962 
isMidiMachineControlMessage() const963 bool MidiMessage::isMidiMachineControlMessage() const noexcept
964 {
965     auto data = getRawData();
966 
967     return data[0] == 0xf0
968         && data[1] == 0x7f
969         && data[3] == 0x06
970         && size > 5;
971 }
972 
getMidiMachineControlCommand() const973 MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept
974 {
975     jassert (isMidiMachineControlMessage());
976 
977     return (MidiMachineControlCommand) getRawData()[4];
978 }
979 
midiMachineControlCommand(MidiMessage::MidiMachineControlCommand command)980 MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command)
981 {
982     return { 0xf0, 0x7f, 0, 6, command, 0xf7 };
983 }
984 
985 //==============================================================================
isMidiMachineControlGoto(int & hours,int & minutes,int & seconds,int & frames) const986 bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept
987 {
988     auto data = getRawData();
989 
990     if (size >= 12
991          && data[0] == 0xf0
992          && data[1] == 0x7f
993          && data[3] == 0x06
994          && data[4] == 0x44
995          && data[5] == 0x06
996          && data[6] == 0x01)
997     {
998         hours = data[7] % 24;   // (that some machines send out hours > 24)
999         minutes = data[8];
1000         seconds = data[9];
1001         frames  = data[10];
1002 
1003         return true;
1004     }
1005 
1006     return false;
1007 }
1008 
midiMachineControlGoto(int hours,int minutes,int seconds,int frames)1009 MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames)
1010 {
1011     return { 0xf0, 0x7f, 0, 6, 0x44, 6, 1, hours, minutes, seconds, frames, 0xf7 };
1012 }
1013 
1014 //==============================================================================
getMidiNoteName(int note,bool useSharps,bool includeOctaveNumber,int octaveNumForMiddleC)1015 String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
1016 {
1017     static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
1018     static const char* const flatNoteNames[]  = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" };
1019 
1020     if (isPositiveAndBelow (note, 128))
1021     {
1022         String s (useSharps ? sharpNoteNames[note % 12]
1023                             : flatNoteNames [note % 12]);
1024 
1025         if (includeOctaveNumber)
1026             s << (note / 12 + (octaveNumForMiddleC - 5));
1027 
1028         return s;
1029     }
1030 
1031     return {};
1032 }
1033 
getMidiNoteInHertz(const int noteNumber,const double frequencyOfA)1034 double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept
1035 {
1036     return frequencyOfA * std::pow (2.0, (noteNumber - 69) / 12.0);
1037 }
1038 
isMidiNoteBlack(int noteNumber)1039 bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept
1040 {
1041     return ((1 << (noteNumber % 12)) & 0x054a) != 0;
1042 }
1043 
getGMInstrumentName(const int n)1044 const char* MidiMessage::getGMInstrumentName (const int n)
1045 {
1046     static const char* names[] =
1047     {
1048         NEEDS_TRANS("Acoustic Grand Piano"),    NEEDS_TRANS("Bright Acoustic Piano"),   NEEDS_TRANS("Electric Grand Piano"),    NEEDS_TRANS("Honky-tonk Piano"),
1049         NEEDS_TRANS("Electric Piano 1"),        NEEDS_TRANS("Electric Piano 2"),        NEEDS_TRANS("Harpsichord"),             NEEDS_TRANS("Clavinet"),
1050         NEEDS_TRANS("Celesta"),                 NEEDS_TRANS("Glockenspiel"),            NEEDS_TRANS("Music Box"),               NEEDS_TRANS("Vibraphone"),
1051         NEEDS_TRANS("Marimba"),                 NEEDS_TRANS("Xylophone"),               NEEDS_TRANS("Tubular Bells"),           NEEDS_TRANS("Dulcimer"),
1052         NEEDS_TRANS("Drawbar Organ"),           NEEDS_TRANS("Percussive Organ"),        NEEDS_TRANS("Rock Organ"),              NEEDS_TRANS("Church Organ"),
1053         NEEDS_TRANS("Reed Organ"),              NEEDS_TRANS("Accordion"),               NEEDS_TRANS("Harmonica"),               NEEDS_TRANS("Tango Accordion"),
1054         NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"),  NEEDS_TRANS("Electric Guitar (clean)"),
1055         NEEDS_TRANS("Electric Guitar (mute)"),  NEEDS_TRANS("Overdriven Guitar"),       NEEDS_TRANS("Distortion Guitar"),       NEEDS_TRANS("Guitar Harmonics"),
1056         NEEDS_TRANS("Acoustic Bass"),           NEEDS_TRANS("Electric Bass (finger)"),  NEEDS_TRANS("Electric Bass (pick)"),    NEEDS_TRANS("Fretless Bass"),
1057         NEEDS_TRANS("Slap Bass 1"),             NEEDS_TRANS("Slap Bass 2"),             NEEDS_TRANS("Synth Bass 1"),            NEEDS_TRANS("Synth Bass 2"),
1058         NEEDS_TRANS("Violin"),                  NEEDS_TRANS("Viola"),                   NEEDS_TRANS("Cello"),                   NEEDS_TRANS("Contrabass"),
1059         NEEDS_TRANS("Tremolo Strings"),         NEEDS_TRANS("Pizzicato Strings"),       NEEDS_TRANS("Orchestral Harp"),         NEEDS_TRANS("Timpani"),
1060         NEEDS_TRANS("String Ensemble 1"),       NEEDS_TRANS("String Ensemble 2"),       NEEDS_TRANS("SynthStrings 1"),          NEEDS_TRANS("SynthStrings 2"),
1061         NEEDS_TRANS("Choir Aahs"),              NEEDS_TRANS("Voice Oohs"),              NEEDS_TRANS("Synth Voice"),             NEEDS_TRANS("Orchestra Hit"),
1062         NEEDS_TRANS("Trumpet"),                 NEEDS_TRANS("Trombone"),                NEEDS_TRANS("Tuba"),                    NEEDS_TRANS("Muted Trumpet"),
1063         NEEDS_TRANS("French Horn"),             NEEDS_TRANS("Brass Section"),           NEEDS_TRANS("SynthBrass 1"),            NEEDS_TRANS("SynthBrass 2"),
1064         NEEDS_TRANS("Soprano Sax"),             NEEDS_TRANS("Alto Sax"),                NEEDS_TRANS("Tenor Sax"),               NEEDS_TRANS("Baritone Sax"),
1065         NEEDS_TRANS("Oboe"),                    NEEDS_TRANS("English Horn"),            NEEDS_TRANS("Bassoon"),                 NEEDS_TRANS("Clarinet"),
1066         NEEDS_TRANS("Piccolo"),                 NEEDS_TRANS("Flute"),                   NEEDS_TRANS("Recorder"),                NEEDS_TRANS("Pan Flute"),
1067         NEEDS_TRANS("Blown Bottle"),            NEEDS_TRANS("Shakuhachi"),              NEEDS_TRANS("Whistle"),                 NEEDS_TRANS("Ocarina"),
1068         NEEDS_TRANS("Lead 1 (square)"),         NEEDS_TRANS("Lead 2 (sawtooth)"),       NEEDS_TRANS("Lead 3 (calliope)"),       NEEDS_TRANS("Lead 4 (chiff)"),
1069         NEEDS_TRANS("Lead 5 (charang)"),        NEEDS_TRANS("Lead 6 (voice)"),          NEEDS_TRANS("Lead 7 (fifths)"),         NEEDS_TRANS("Lead 8 (bass+lead)"),
1070         NEEDS_TRANS("Pad 1 (new age)"),         NEEDS_TRANS("Pad 2 (warm)"),            NEEDS_TRANS("Pad 3 (polysynth)"),       NEEDS_TRANS("Pad 4 (choir)"),
1071         NEEDS_TRANS("Pad 5 (bowed)"),           NEEDS_TRANS("Pad 6 (metallic)"),        NEEDS_TRANS("Pad 7 (halo)"),            NEEDS_TRANS("Pad 8 (sweep)"),
1072         NEEDS_TRANS("FX 1 (rain)"),             NEEDS_TRANS("FX 2 (soundtrack)"),       NEEDS_TRANS("FX 3 (crystal)"),          NEEDS_TRANS("FX 4 (atmosphere)"),
1073         NEEDS_TRANS("FX 5 (brightness)"),       NEEDS_TRANS("FX 6 (goblins)"),          NEEDS_TRANS("FX 7 (echoes)"),           NEEDS_TRANS("FX 8 (sci-fi)"),
1074         NEEDS_TRANS("Sitar"),                   NEEDS_TRANS("Banjo"),                   NEEDS_TRANS("Shamisen"),                NEEDS_TRANS("Koto"),
1075         NEEDS_TRANS("Kalimba"),                 NEEDS_TRANS("Bag pipe"),                NEEDS_TRANS("Fiddle"),                  NEEDS_TRANS("Shanai"),
1076         NEEDS_TRANS("Tinkle Bell"),             NEEDS_TRANS("Agogo"),                   NEEDS_TRANS("Steel Drums"),             NEEDS_TRANS("Woodblock"),
1077         NEEDS_TRANS("Taiko Drum"),              NEEDS_TRANS("Melodic Tom"),             NEEDS_TRANS("Synth Drum"),              NEEDS_TRANS("Reverse Cymbal"),
1078         NEEDS_TRANS("Guitar Fret Noise"),       NEEDS_TRANS("Breath Noise"),            NEEDS_TRANS("Seashore"),                NEEDS_TRANS("Bird Tweet"),
1079         NEEDS_TRANS("Telephone Ring"),          NEEDS_TRANS("Helicopter"),              NEEDS_TRANS("Applause"),                NEEDS_TRANS("Gunshot")
1080     };
1081 
1082     return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
1083 }
1084 
getGMInstrumentBankName(const int n)1085 const char* MidiMessage::getGMInstrumentBankName (const int n)
1086 {
1087     static const char* names[] =
1088     {
1089         NEEDS_TRANS("Piano"),           NEEDS_TRANS("Chromatic Percussion"),    NEEDS_TRANS("Organ"),       NEEDS_TRANS("Guitar"),
1090         NEEDS_TRANS("Bass"),            NEEDS_TRANS("Strings"),                 NEEDS_TRANS("Ensemble"),    NEEDS_TRANS("Brass"),
1091         NEEDS_TRANS("Reed"),            NEEDS_TRANS("Pipe"),                    NEEDS_TRANS("Synth Lead"),  NEEDS_TRANS("Synth Pad"),
1092         NEEDS_TRANS("Synth Effects"),   NEEDS_TRANS("Ethnic"),                  NEEDS_TRANS("Percussive"),  NEEDS_TRANS("Sound Effects")
1093     };
1094 
1095     return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
1096 }
1097 
getRhythmInstrumentName(const int n)1098 const char* MidiMessage::getRhythmInstrumentName (const int n)
1099 {
1100     static const char* names[] =
1101     {
1102         NEEDS_TRANS("Acoustic Bass Drum"),  NEEDS_TRANS("Bass Drum 1"),     NEEDS_TRANS("Side Stick"),      NEEDS_TRANS("Acoustic Snare"),
1103         NEEDS_TRANS("Hand Clap"),           NEEDS_TRANS("Electric Snare"),  NEEDS_TRANS("Low Floor Tom"),   NEEDS_TRANS("Closed Hi-Hat"),
1104         NEEDS_TRANS("High Floor Tom"),      NEEDS_TRANS("Pedal Hi-Hat"),    NEEDS_TRANS("Low Tom"),         NEEDS_TRANS("Open Hi-Hat"),
1105         NEEDS_TRANS("Low-Mid Tom"),         NEEDS_TRANS("Hi-Mid Tom"),      NEEDS_TRANS("Crash Cymbal 1"),  NEEDS_TRANS("High Tom"),
1106         NEEDS_TRANS("Ride Cymbal 1"),       NEEDS_TRANS("Chinese Cymbal"),  NEEDS_TRANS("Ride Bell"),       NEEDS_TRANS("Tambourine"),
1107         NEEDS_TRANS("Splash Cymbal"),       NEEDS_TRANS("Cowbell"),         NEEDS_TRANS("Crash Cymbal 2"),  NEEDS_TRANS("Vibraslap"),
1108         NEEDS_TRANS("Ride Cymbal 2"),       NEEDS_TRANS("Hi Bongo"),        NEEDS_TRANS("Low Bongo"),       NEEDS_TRANS("Mute Hi Conga"),
1109         NEEDS_TRANS("Open Hi Conga"),       NEEDS_TRANS("Low Conga"),       NEEDS_TRANS("High Timbale"),    NEEDS_TRANS("Low Timbale"),
1110         NEEDS_TRANS("High Agogo"),          NEEDS_TRANS("Low Agogo"),       NEEDS_TRANS("Cabasa"),          NEEDS_TRANS("Maracas"),
1111         NEEDS_TRANS("Short Whistle"),       NEEDS_TRANS("Long Whistle"),    NEEDS_TRANS("Short Guiro"),     NEEDS_TRANS("Long Guiro"),
1112         NEEDS_TRANS("Claves"),              NEEDS_TRANS("Hi Wood Block"),   NEEDS_TRANS("Low Wood Block"),  NEEDS_TRANS("Mute Cuica"),
1113         NEEDS_TRANS("Open Cuica"),          NEEDS_TRANS("Mute Triangle"),   NEEDS_TRANS("Open Triangle")
1114     };
1115 
1116     return (n >= 35 && n <= 81) ? names[n - 35] : nullptr;
1117 }
1118 
getControllerName(const int n)1119 const char* MidiMessage::getControllerName (const int n)
1120 {
1121     static const char* names[] =
1122     {
1123         NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"),
1124         nullptr,
1125         NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"),
1126         NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"),
1127         nullptr,
1128         NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"),
1129         NEEDS_TRANS("Effect Control 2 (coarse)"),
1130         nullptr, nullptr,
1131         NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"),
1132         NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"),
1133         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1134         NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"),
1135         nullptr,
1136         NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"),
1137         NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"),
1138         NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"),
1139         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1140         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1141         NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"),
1142         NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"),
1143         NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"),
1144         NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"),
1145         NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"),
1146         NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"),
1147         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1148         NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"),
1149         NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"),
1150         NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"),
1151         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1152         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
1153         NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"),
1154         NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation")
1155     };
1156 
1157     return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr;
1158 }
1159 
1160 //==============================================================================
1161 //==============================================================================
1162 #if JUCE_UNIT_TESTS
1163 
1164 struct MidiMessageTest  : public UnitTest
1165 {
MidiMessageTestjuce::MidiMessageTest1166     MidiMessageTest()
1167         : UnitTest ("MidiMessage", UnitTestCategories::midi)
1168     {}
1169 
runTestjuce::MidiMessageTest1170     void runTest() override
1171     {
1172         using std::begin;
1173         using std::end;
1174 
1175         beginTest ("ReadVariableLengthValue should return valid, backward-compatible results");
1176         {
1177             const std::vector<uint8> inputs[]
1178             {
1179                 { 0x00 },
1180                 { 0x40 },
1181                 { 0x7f },
1182                 { 0x81, 0x00 },
1183                 { 0xc0, 0x00 },
1184                 { 0xff, 0x7f },
1185                 { 0x81, 0x80, 0x00 },
1186                 { 0xc0, 0x80, 0x00 },
1187                 { 0xff, 0xff, 0x7f },
1188                 { 0x81, 0x80, 0x80, 0x00 },
1189                 { 0xc0, 0x80, 0x80, 0x00 },
1190                 { 0xff, 0xff, 0xff, 0x7f }
1191             };
1192 
1193             const int outputs[]
1194             {
1195                 0x00,
1196                 0x40,
1197                 0x7f,
1198                 0x80,
1199                 0x2000,
1200                 0x3fff,
1201                 0x4000,
1202                 0x100000,
1203                 0x1fffff,
1204                 0x200000,
1205                 0x8000000,
1206                 0xfffffff,
1207             };
1208 
1209             expectEquals (std::distance (begin (inputs), end (inputs)),
1210                           std::distance (begin (outputs), end (outputs)));
1211 
1212             size_t index = 0;
1213 
1214             JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
1215             JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
1216 
1217             for (const auto& input : inputs)
1218             {
1219                 auto copy = input;
1220 
1221                 while (copy.size() < 16)
1222                     copy.push_back (0);
1223 
1224                 const auto result = MidiMessage::readVariableLengthValue (copy.data(),
1225                                                                           (int) copy.size());
1226 
1227                 expect (result.isValid());
1228                 expectEquals (result.value, outputs[index]);
1229                 expectEquals (result.bytesUsed, (int) inputs[index].size());
1230 
1231                 int legacyNumUsed = 0;
1232                 const auto legacyResult = MidiMessage::readVariableLengthVal (copy.data(),
1233                                                                               legacyNumUsed);
1234 
1235                 expectEquals (result.value, legacyResult);
1236                 expectEquals (result.bytesUsed, legacyNumUsed);
1237 
1238                 ++index;
1239             }
1240 
1241             JUCE_END_IGNORE_WARNINGS_GCC_LIKE
1242             JUCE_END_IGNORE_WARNINGS_MSVC
1243         }
1244 
1245         beginTest ("ReadVariableLengthVal should return 0 if input is truncated");
1246         {
1247             for (size_t i = 0; i != 16; ++i)
1248             {
1249                 std::vector<uint8> input;
1250                 input.resize (i, 0xFF);
1251 
1252                 const auto result = MidiMessage::readVariableLengthValue (input.data(),
1253                                                                           (int) input.size());
1254 
1255                 expect (! result.isValid());
1256                 expectEquals (result.value, 0);
1257                 expectEquals (result.bytesUsed, 0);
1258             }
1259         }
1260 
1261         const std::vector<uint8> metaEvents[]
1262         {
1263             // Format is 0xff, followed by a 'kind' byte, followed by a variable-length
1264             // 'data-length' value, followed by that many data bytes
1265             { 0xff, 0x00, 0x02, 0x00, 0x00 },                   // Sequence number
1266             { 0xff, 0x01, 0x00 },                               // Text event
1267             { 0xff, 0x02, 0x00 },                               // Copyright notice
1268             { 0xff, 0x03, 0x00 },                               // Track name
1269             { 0xff, 0x04, 0x00 },                               // Instrument name
1270             { 0xff, 0x05, 0x00 },                               // Lyric
1271             { 0xff, 0x06, 0x00 },                               // Marker
1272             { 0xff, 0x07, 0x00 },                               // Cue point
1273             { 0xff, 0x20, 0x01, 0x00 },                         // Channel prefix
1274             { 0xff, 0x2f, 0x00 },                               // End of track
1275             { 0xff, 0x51, 0x03, 0x01, 0x02, 0x03 },             // Set tempo
1276             { 0xff, 0x54, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 }, // SMPTE offset
1277             { 0xff, 0x58, 0x04, 0x01, 0x02, 0x03, 0x04 },       // Time signature
1278             { 0xff, 0x59, 0x02, 0x01, 0x02 },                   // Key signature
1279             { 0xff, 0x7f, 0x00 },                               // Sequencer-specific
1280         };
1281 
1282         beginTest ("MidiMessage data constructor works for well-formed meta-events");
1283         {
1284             const auto status = (uint8) 0x90;
1285 
1286             for (const auto& input : metaEvents)
1287             {
1288                 int bytesUsed = 0;
1289                 const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
1290 
1291                 expect (msg.isMetaEvent());
1292                 expectEquals (msg.getMetaEventLength(), (int) input.size() - 3);
1293                 expectEquals (msg.getMetaEventType(), (int) input[1]);
1294             }
1295         }
1296 
1297         beginTest ("MidiMessage data constructor works for malformed meta-events");
1298         {
1299             const auto status = (uint8) 0x90;
1300 
1301             const auto runTest = [&] (const std::vector<uint8>& input)
1302             {
1303                 int bytesUsed = 0;
1304                 const MidiMessage msg (input.data(), (int) input.size(), bytesUsed, status);
1305 
1306                 expect (msg.isMetaEvent());
1307                 expectEquals (msg.getMetaEventLength(), jmax (0, (int) input.size() - 3));
1308                 expectEquals (msg.getMetaEventType(), input.size() >= 2 ? input[1] : -1);
1309             };
1310 
1311             runTest ({ 0xff });
1312 
1313             for (const auto& input : metaEvents)
1314             {
1315                 auto copy = input;
1316                 copy[2] = 0x40; // Set the size of the message to more bytes than are present
1317 
1318                 runTest (copy);
1319             }
1320         }
1321     }
1322 };
1323 
1324 static MidiMessageTest midiMessageTests;
1325 
1326 #endif
1327 
1328 } // namespace juce
1329