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