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