VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_audio_basics / midi / ump / juce_UMP_test.cpp
blob0706b13baec27aa5cb2b2e3e45cbc799d6b16f39
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
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.
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.
20 ==============================================================================
23 namespace juce
25 namespace universal_midi_packets
28 constexpr uint8_t operator""_u8 (unsigned long long int i) { return static_cast<uint8_t> (i); }
29 constexpr uint16_t operator""_u16 (unsigned long long int i) { return static_cast<uint16_t> (i); }
30 constexpr uint32_t operator""_u32 (unsigned long long int i) { return static_cast<uint32_t> (i); }
31 constexpr uint64_t operator""_u64 (unsigned long long int i) { return static_cast<uint64_t> (i); }
33 class UniversalMidiPacketTests : public UnitTest
35 public:
36 UniversalMidiPacketTests()
37 : UnitTest ("Universal MIDI Packet", UnitTestCategories::midi)
41 void runTest() override
43 auto random = getRandom();
45 beginTest ("Short bytestream midi messages can be round-tripped through the UMP converter");
47 Midi1ToBytestreamTranslator translator (0);
49 forEachNonSysExTestMessage (random, [&] (const MidiMessage& m)
51 Packets packets;
52 Conversion::toMidi1 (m, packets);
53 expect (packets.size() == 1);
55 // Make sure that the message type is correct
56 expect (Utils::getMessageType (packets.data()[0]) == ((m.getRawData()[0] >> 0x4) == 0xf ? 0x1 : 0x2));
58 translator.dispatch (View {packets.data() },
60 [&] (const MidiMessage& roundTripped)
62 expect (equal (m, roundTripped));
63 });
64 });
67 beginTest ("Bytestream SysEx converts to universal packets");
70 // Zero length message
71 Packets packets;
72 Conversion::toMidi1 (createRandomSysEx (random, 0), packets);
73 expect (packets.size() == 2);
75 expect (packets.data()[0] == 0x30000000);
76 expect (packets.data()[1] == 0x00000000);
80 const auto message = createRandomSysEx (random, 1);
81 Packets packets;
82 Conversion::toMidi1 (message, packets);
83 expect (packets.size() == 2);
85 const auto* sysEx = message.getSysExData();
86 expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x01, sysEx[0], 0));
87 expect (packets.data()[1] == 0x00000000);
91 const auto message = createRandomSysEx (random, 6);
92 Packets packets;
93 Conversion::toMidi1 (message, packets);
94 expect (packets.size() == 2);
96 const auto* sysEx = message.getSysExData();
97 expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x06, sysEx[0], sysEx[1]));
98 expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5]));
102 const auto message = createRandomSysEx (random, 12);
103 Packets packets;
104 Conversion::toMidi1 (message, packets);
105 expect (packets.size() == 4);
107 const auto* sysEx = message.getSysExData();
108 expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1]));
109 expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5]));
110 expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x36, sysEx[6], sysEx[7]));
111 expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11]));
115 const auto message = createRandomSysEx (random, 13);
116 Packets packets;
117 Conversion::toMidi1 (message, packets);
118 expect (packets.size() == 6);
120 const auto* sysEx = message.getSysExData();
121 expect (packets.data()[0] == Utils::bytesToWord (0x30, 0x16, sysEx[0], sysEx[1]));
122 expect (packets.data()[1] == Utils::bytesToWord (sysEx[2], sysEx[3], sysEx[4], sysEx[5]));
123 expect (packets.data()[2] == Utils::bytesToWord (0x30, 0x26, sysEx[6], sysEx[7]));
124 expect (packets.data()[3] == Utils::bytesToWord (sysEx[8], sysEx[9], sysEx[10], sysEx[11]));
125 expect (packets.data()[4] == Utils::bytesToWord (0x30, 0x31, sysEx[12], 0));
126 expect (packets.data()[5] == 0x00000000);
130 ToBytestreamDispatcher converter (0);
131 Packets packets;
133 const auto checkRoundTrip = [&] (const MidiBuffer& expected)
135 for (const auto meta : expected)
136 Conversion::toMidi1 (meta.getMessage(), packets);
138 MidiBuffer output;
139 converter.dispatch (packets.data(),
140 packets.data() + packets.size(),
142 [&] (const MidiMessage& roundTripped)
144 output.addEvent (roundTripped, int (roundTripped.getTimeStamp()));
146 packets.clear();
148 expect (equal (expected, output));
151 beginTest ("Long SysEx bytestream midi messages can be round-tripped through the UMP converter");
153 for (auto length : { 0, 1, 2, 3, 4, 5, 6, 7, 13, 20, 100, 1000 })
155 MidiBuffer expected;
156 expected.addEvent (createRandomSysEx (random, size_t (length)), 0);
157 checkRoundTrip (expected);
161 beginTest ("UMP SysEx7 messages interspersed with utility messages convert to bytestream");
163 const auto sysEx = createRandomSysEx (random, 100);
164 Packets originalPackets;
165 Conversion::toMidi1 (sysEx, originalPackets);
167 Packets modifiedPackets;
169 const auto addRandomUtilityUMP = [&]
171 const auto newPacket = createRandomUtilityUMP (random);
172 modifiedPackets.add (View (newPacket.data()));
175 for (const auto& packet : originalPackets)
177 addRandomUtilityUMP();
178 modifiedPackets.add (packet);
179 addRandomUtilityUMP();
182 MidiBuffer output;
183 converter.dispatch (modifiedPackets.data(),
184 modifiedPackets.data() + modifiedPackets.size(),
186 [&] (const MidiMessage& roundTripped)
188 output.addEvent (roundTripped, int (roundTripped.getTimeStamp()));
191 // All Utility messages should have been ignored
192 expect (output.getNumEvents() == 1);
194 for (const auto meta : output)
195 expect (equal (meta.getMessage(), sysEx));
198 beginTest ("UMP SysEx7 messages interspersed with System Realtime messages convert to bytestream");
200 const auto sysEx = createRandomSysEx (random, 200);
201 Packets originalPackets;
202 Conversion::toMidi1 (sysEx, originalPackets);
204 Packets modifiedPackets;
205 MidiBuffer realtimeMessages;
207 const auto addRandomRealtimeUMP = [&]
209 const auto newPacket = createRandomRealtimeUMP (random);
210 modifiedPackets.add (View (newPacket.data()));
211 realtimeMessages.addEvent (Midi1ToBytestreamTranslator::fromUmp (newPacket), 0);
214 for (const auto& packet : originalPackets)
216 addRandomRealtimeUMP();
217 modifiedPackets.add (packet);
218 addRandomRealtimeUMP();
221 MidiBuffer output;
222 converter.dispatch (modifiedPackets.data(),
223 modifiedPackets.data() + modifiedPackets.size(),
225 [&] (const MidiMessage& roundTripped)
227 output.addEvent (roundTripped, int (roundTripped.getTimeStamp()));
230 const auto numOutputs = output.getNumEvents();
231 const auto numInputs = realtimeMessages.getNumEvents();
232 expect (numOutputs == numInputs + 1);
234 if (numOutputs == numInputs + 1)
236 const auto isMetadataEquivalent = [] (const MidiMessageMetadata& a,
237 const MidiMessageMetadata& b)
239 return equal (a.getMessage(), b.getMessage());
242 auto it = output.begin();
244 for (const auto meta : realtimeMessages)
246 if (! isMetadataEquivalent (*it, meta))
248 expect (equal ((*it).getMessage(), sysEx));
249 ++it;
252 expect (isMetadataEquivalent (*it, meta));
253 ++it;
258 beginTest ("UMP SysEx7 messages interspersed with System Realtime and Utility messages convert to bytestream");
260 const auto sysEx = createRandomSysEx (random, 300);
261 Packets originalPackets;
262 Conversion::toMidi1 (sysEx, originalPackets);
264 Packets modifiedPackets;
265 MidiBuffer realtimeMessages;
267 const auto addRandomRealtimeUMP = [&]
269 const auto newPacket = createRandomRealtimeUMP (random);
270 modifiedPackets.add (View (newPacket.data()));
271 realtimeMessages.addEvent (Midi1ToBytestreamTranslator::fromUmp (newPacket), 0);
274 const auto addRandomUtilityUMP = [&]
276 const auto newPacket = createRandomUtilityUMP (random);
277 modifiedPackets.add (View (newPacket.data()));
280 for (const auto& packet : originalPackets)
282 addRandomRealtimeUMP();
283 addRandomUtilityUMP();
284 modifiedPackets.add (packet);
285 addRandomRealtimeUMP();
286 addRandomUtilityUMP();
289 MidiBuffer output;
290 converter.dispatch (modifiedPackets.data(),
291 modifiedPackets.data() + modifiedPackets.size(),
293 [&] (const MidiMessage& roundTripped)
295 output.addEvent (roundTripped, int (roundTripped.getTimeStamp()));
298 const auto numOutputs = output.getNumEvents();
299 const auto numInputs = realtimeMessages.getNumEvents();
300 expect (numOutputs == numInputs + 1);
302 if (numOutputs == numInputs + 1)
304 const auto isMetadataEquivalent = [] (const MidiMessageMetadata& a, const MidiMessageMetadata& b)
306 return equal (a.getMessage(), b.getMessage());
309 auto it = output.begin();
311 for (const auto meta : realtimeMessages)
313 if (! isMetadataEquivalent (*it, meta))
315 expect (equal ((*it).getMessage(), sysEx));
316 ++it;
319 expect (isMetadataEquivalent (*it, meta));
320 ++it;
325 beginTest ("SysEx messages are terminated by non-Utility, non-Realtime messages");
327 const auto noteOn = [&]
329 MidiBuffer b;
330 b.addEvent (MidiMessage::noteOn (1, uint8_t (64), uint8_t (64)), 0);
331 return b;
332 }();
334 const auto noteOnPackets = [&]
336 Packets p;
338 for (const auto meta : noteOn)
339 Conversion::toMidi1 (meta.getMessage(), p);
341 return p;
342 }();
344 const auto sysEx = createRandomSysEx (random, 300);
346 const auto originalPackets = [&]
348 Packets p;
349 Conversion::toMidi1 (sysEx, p);
350 return p;
351 }();
353 const auto modifiedPackets = [&]
355 Packets p;
357 const auto insertionPoint = std::next (originalPackets.begin(), 10);
358 std::for_each (originalPackets.begin(),
359 insertionPoint,
360 [&] (const View& view) { p.add (view); });
362 for (const auto& view : noteOnPackets)
363 p.add (view);
365 std::for_each (insertionPoint,
366 originalPackets.end(),
367 [&] (const View& view) { p.add (view); });
369 return p;
370 }();
372 // modifiedPackets now contains some SysEx packets interrupted by a MIDI 1 noteOn
374 MidiBuffer output;
376 const auto pushToOutput = [&] (const Packets& p)
378 converter.dispatch (p.data(),
379 p.data() + p.size(),
381 [&] (const MidiMessage& roundTripped)
383 output.addEvent (roundTripped, int (roundTripped.getTimeStamp()));
387 pushToOutput (modifiedPackets);
389 // Interrupted sysEx shouldn't be present
390 expect (equal (output, noteOn));
392 const auto newSysEx = createRandomSysEx (random, 300);
393 Packets newSysExPackets;
394 Conversion::toMidi1 (newSysEx, newSysExPackets);
396 // If we push another midi event without interrupting it,
397 // it should get through without being modified,
398 // and it shouldn't be affected by the previous (interrupted) sysex.
400 output.clear();
401 pushToOutput (newSysExPackets);
403 expect (output.getNumEvents() == 1);
405 for (const auto meta : output)
406 expect (equal (meta.getMessage(), newSysEx));
409 beginTest ("Widening conversions work");
411 // This is similar to the 'slow' example code from the MIDI 2.0 spec
412 const auto baselineScale = [] (uint32_t srcVal, uint32_t srcBits, uint32_t dstBits)
414 const auto scaleBits = (uint32_t) (dstBits - srcBits);
416 auto bitShiftedValue = (uint32_t) (srcVal << scaleBits);
418 const auto srcCenter = (uint32_t) (1 << (srcBits - 1));
420 if (srcVal <= srcCenter)
421 return bitShiftedValue;
423 const auto repeatBits = (uint32_t) (srcBits - 1);
424 const auto repeatMask = (uint32_t) ((1 << repeatBits) - 1);
426 auto repeatValue = (uint32_t) (srcVal & repeatMask);
428 if (scaleBits > repeatBits)
429 repeatValue <<= scaleBits - repeatBits;
430 else
431 repeatValue >>= repeatBits - scaleBits;
433 while (repeatValue != 0)
435 bitShiftedValue |= repeatValue;
436 repeatValue >>= repeatBits;
439 return bitShiftedValue;
442 const auto baselineScale7To8 = [&] (uint8_t in)
444 return baselineScale (in, 7, 8);
447 const auto baselineScale7To16 = [&] (uint8_t in)
449 return baselineScale (in, 7, 16);
452 const auto baselineScale14To16 = [&] (uint16_t in)
454 return baselineScale (in, 14, 16);
457 const auto baselineScale7To32 = [&] (uint8_t in)
459 return baselineScale (in, 7, 32);
462 const auto baselineScale14To32 = [&] (uint16_t in)
464 return baselineScale (in, 14, 32);
467 for (auto i = 0; i != 100; ++i)
469 const auto rand = (uint8_t) random.nextInt (0x80);
470 expectEquals ((int64_t) Conversion::scaleTo8 (rand),
471 (int64_t) baselineScale7To8 (rand));
474 expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x00), (int64_t) 0x0000);
475 expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x0a), (int64_t) 0x1400);
476 expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x40), (int64_t) 0x8000);
477 expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x57), (int64_t) 0xaeba);
478 expectEquals ((int64_t) Conversion::scaleTo16 ((uint8_t) 0x7f), (int64_t) 0xffff);
480 for (auto i = 0; i != 100; ++i)
482 const auto rand = (uint8_t) random.nextInt (0x80);
483 expectEquals ((int64_t) Conversion::scaleTo16 (rand),
484 (int64_t) baselineScale7To16 (rand));
487 for (auto i = 0; i != 100; ++i)
489 const auto rand = (uint16_t) random.nextInt (0x4000);
490 expectEquals ((int64_t) Conversion::scaleTo16 (rand),
491 (int64_t) baselineScale14To16 (rand));
494 for (auto i = 0; i != 100; ++i)
496 const auto rand = (uint8_t) random.nextInt (0x80);
497 expectEquals ((int64_t) Conversion::scaleTo32 (rand),
498 (int64_t) baselineScale7To32 (rand));
501 expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x0000), (int64_t) 0x00000000);
502 expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x2000), (int64_t) 0x80000000);
503 expectEquals ((int64_t) Conversion::scaleTo32 ((uint16_t) 0x3fff), (int64_t) 0xffffffff);
505 for (auto i = 0; i != 100; ++i)
507 const auto rand = (uint16_t) random.nextInt (0x4000);
508 expectEquals ((int64_t) Conversion::scaleTo32 (rand),
509 (int64_t) baselineScale14To32 (rand));
513 beginTest ("Round-trip widening/narrowing conversions work");
515 for (auto i = 0; i != 100; ++i)
518 const auto rand = (uint8_t) random.nextInt (0x80);
519 expectEquals (Conversion::scaleTo7 (Conversion::scaleTo8 (rand)), rand);
523 const auto rand = (uint8_t) random.nextInt (0x80);
524 expectEquals (Conversion::scaleTo7 (Conversion::scaleTo16 (rand)), rand);
528 const auto rand = (uint8_t) random.nextInt (0x80);
529 expectEquals (Conversion::scaleTo7 (Conversion::scaleTo32 (rand)), rand);
533 const auto rand = (uint16_t) random.nextInt (0x4000);
534 expectEquals ((uint64_t) Conversion::scaleTo14 (Conversion::scaleTo16 (rand)), (uint64_t) rand);
538 const auto rand = (uint16_t) random.nextInt (0x4000);
539 expectEquals ((uint64_t) Conversion::scaleTo14 (Conversion::scaleTo32 (rand)), (uint64_t) rand);
544 beginTest ("MIDI 2 -> 1 note on conversions");
547 Packets midi2;
548 midi2.add (PacketX2 { 0x41946410, 0x12345678 });
550 Packets midi1;
551 midi1.add (PacketX1 { 0x21946409 });
553 checkMidi2ToMidi1Conversion (midi2, midi1);
557 // If the velocity is close to 0, the output velocity should still be 1
558 Packets midi2;
559 midi2.add (PacketX2 { 0x4295327f, 0x00345678 });
561 Packets midi1;
562 midi1.add (PacketX1 { 0x22953201 });
564 checkMidi2ToMidi1Conversion (midi2, midi1);
568 beginTest ("MIDI 2 -> 1 note off conversion");
570 Packets midi2;
571 midi2.add (PacketX2 { 0x448b0520, 0xfedcba98 });
573 Packets midi1;
574 midi1.add (PacketX1 { 0x248b057f });
576 checkMidi2ToMidi1Conversion (midi2, midi1);
579 beginTest ("MIDI 2 -> 1 poly pressure conversion");
581 Packets midi2;
582 midi2.add (PacketX2 { 0x49af0520, 0x80dcba98 });
584 Packets midi1;
585 midi1.add (PacketX1 { 0x29af0540 });
587 checkMidi2ToMidi1Conversion (midi2, midi1);
590 beginTest ("MIDI 2 -> 1 control change conversion");
592 Packets midi2;
593 midi2.add (PacketX2 { 0x49b00520, 0x80dcba98 });
595 Packets midi1;
596 midi1.add (PacketX1 { 0x29b00540 });
598 checkMidi2ToMidi1Conversion (midi2, midi1);
601 beginTest ("MIDI 2 -> 1 channel pressure conversion");
603 Packets midi2;
604 midi2.add (PacketX2 { 0x40d20520, 0x80dcba98 });
606 Packets midi1;
607 midi1.add (PacketX1 { 0x20d24000 });
609 checkMidi2ToMidi1Conversion (midi2, midi1);
612 beginTest ("MIDI 2 -> 1 nrpn rpn conversion");
615 Packets midi2;
616 midi2.add (PacketX2 { 0x44240123, 0x456789ab });
618 Packets midi1;
619 midi1.add (PacketX1 { 0x24b46501 });
620 midi1.add (PacketX1 { 0x24b46423 });
621 midi1.add (PacketX1 { 0x24b40622 });
622 midi1.add (PacketX1 { 0x24b42659 });
624 checkMidi2ToMidi1Conversion (midi2, midi1);
628 Packets midi2;
629 midi2.add (PacketX2 { 0x48347f7f, 0xffffffff });
631 Packets midi1;
632 midi1.add (PacketX1 { 0x28b4637f });
633 midi1.add (PacketX1 { 0x28b4627f });
634 midi1.add (PacketX1 { 0x28b4067f });
635 midi1.add (PacketX1 { 0x28b4267f });
637 checkMidi2ToMidi1Conversion (midi2, midi1);
641 beginTest ("MIDI 2 -> 1 program change and bank select conversion");
644 // If the bank valid bit is 0, just emit a program change
645 Packets midi2;
646 midi2.add (PacketX2 { 0x4cc10000, 0x70004020 });
648 Packets midi1;
649 midi1.add (PacketX1 { 0x2cc17000 });
651 checkMidi2ToMidi1Conversion (midi2, midi1);
655 // If the bank valid bit is 1, emit bank select control changes and a program change
656 Packets midi2;
657 midi2.add (PacketX2 { 0x4bc20001, 0x70004020 });
659 Packets midi1;
660 midi1.add (PacketX1 { 0x2bb20040 });
661 midi1.add (PacketX1 { 0x2bb22020 });
662 midi1.add (PacketX1 { 0x2bc27000 });
664 checkMidi2ToMidi1Conversion (midi2, midi1);
668 beginTest ("MIDI 2 -> 1 pitch bend conversion");
670 Packets midi2;
671 midi2.add (PacketX2 { 0x4eee0000, 0x12340000 });
673 Packets midi1;
674 midi1.add (PacketX1 { 0x2eee0d09 });
676 checkMidi2ToMidi1Conversion (midi2, midi1);
679 beginTest ("MIDI 2 -> 1 messages which don't convert");
681 const uint8_t opcodes[] { 0x0, 0x1, 0x4, 0x5, 0x6, 0xf };
683 for (const auto opcode : opcodes)
685 Packets midi2;
686 midi2.add (PacketX2 { Utils::bytesToWord (0x40, (uint8_t) (opcode << 0x4), 0, 0), 0x0 });
687 checkMidi2ToMidi1Conversion (midi2, {});
691 beginTest ("MIDI 2 -> 1 messages which are passed through");
693 const uint8_t typecodesX1[] { 0x0, 0x1, 0x2 };
695 for (const auto typecode : typecodesX1)
697 Packets p;
698 p.add (PacketX1 { (uint32_t) ((int64_t) typecode << 0x1c | (random.nextInt64() & 0xffffff)) });
700 checkMidi2ToMidi1Conversion (p, p);
704 Packets p;
705 p.add (PacketX2 { (uint32_t) (0x3 << 0x1c | (random.nextInt64() & 0xffffff)),
706 (uint32_t) (random.nextInt64() & 0xffffffff) });
708 checkMidi2ToMidi1Conversion (p, p);
712 Packets p;
713 p.add (PacketX4 { (uint32_t) (0x5 << 0x1c | (random.nextInt64() & 0xffffff)),
714 (uint32_t) (random.nextInt64() & 0xffffffff),
715 (uint32_t) (random.nextInt64() & 0xffffffff),
716 (uint32_t) (random.nextInt64() & 0xffffffff) });
718 checkMidi2ToMidi1Conversion (p, p);
722 beginTest ("MIDI 2 -> 1 control changes which should be ignored");
724 const uint8_t CCs[] { 6, 38, 98, 99, 100, 101, 0, 32 };
726 for (const auto cc : CCs)
728 Packets midi2;
729 midi2.add (PacketX2 { (uint32_t) (0x40b00000 | (cc << 0x8)), 0x00000000 });
731 checkMidi2ToMidi1Conversion (midi2, {});
735 beginTest ("MIDI 1 -> 2 note on conversions");
738 Packets midi1;
739 midi1.add (PacketX1 { 0x20904040 });
741 Packets midi2;
742 midi2.add (PacketX2 { 0x40904000, static_cast<uint32_t> (Conversion::scaleTo16 (0x40_u8)) << 0x10 });
744 checkMidi1ToMidi2Conversion (midi1, midi2);
747 // If velocity is 0, convert to a note-off
749 Packets midi1;
750 midi1.add (PacketX1 { 0x23935100 });
752 Packets midi2;
753 midi2.add (PacketX2 { 0x43835100, 0x0 });
755 checkMidi1ToMidi2Conversion (midi1, midi2);
759 beginTest ("MIDI 1 -> 2 note off conversions");
761 Packets midi1;
762 midi1.add (PacketX1 { 0x21831020 });
764 Packets midi2;
765 midi2.add (PacketX2 { 0x41831000, static_cast<uint32_t> (Conversion::scaleTo16 (0x20_u8)) << 0x10 });
767 checkMidi1ToMidi2Conversion (midi1, midi2);
770 beginTest ("MIDI 1 -> 2 poly pressure conversions");
772 Packets midi1;
773 midi1.add (PacketX1 { 0x20af7330 });
775 Packets midi2;
776 midi2.add (PacketX2 { 0x40af7300, Conversion::scaleTo32 (0x30_u8) });
778 checkMidi1ToMidi2Conversion (midi1, midi2);
781 beginTest ("individual MIDI 1 -> 2 control changes which should be ignored");
783 const uint8_t CCs[] { 6, 38, 98, 99, 100, 101, 0, 32 };
785 for (const auto cc : CCs)
787 Packets midi1;
788 midi1.add (PacketX1 { Utils::bytesToWord (0x20, 0xb0, cc, 0x00) });
790 checkMidi1ToMidi2Conversion (midi1, {});
794 beginTest ("MIDI 1 -> 2 control change conversions");
796 // normal control change
798 Packets midi1;
799 midi1.add (PacketX1 { 0x29b1017f });
801 Packets midi2;
802 midi2.add (PacketX2 { 0x49b10100, Conversion::scaleTo32 (0x7f_u8) });
804 checkMidi1ToMidi2Conversion (midi1, midi2);
807 // nrpn
809 Packets midi1;
810 midi1.add (PacketX1 { 0x20b06301 });
811 midi1.add (PacketX1 { 0x20b06223 });
812 midi1.add (PacketX1 { 0x20b00645 });
813 midi1.add (PacketX1 { 0x20b02667 });
815 Packets midi2;
816 midi2.add (PacketX2 { 0x40300123, Conversion::scaleTo32 (static_cast<uint16_t> ((0x45 << 7) | 0x67)) });
818 checkMidi1ToMidi2Conversion (midi1, midi2);
821 // rpn
823 Packets midi1;
824 midi1.add (PacketX1 { 0x20b06543 });
825 midi1.add (PacketX1 { 0x20b06421 });
826 midi1.add (PacketX1 { 0x20b00601 });
827 midi1.add (PacketX1 { 0x20b02623 });
829 Packets midi2;
830 midi2.add (PacketX2 { 0x40204321, Conversion::scaleTo32 (static_cast<uint16_t> ((0x01 << 7) | 0x23)) });
832 checkMidi1ToMidi2Conversion (midi1, midi2);
836 beginTest ("MIDI 1 -> MIDI 2 program change and bank select");
838 Packets midi1;
839 // program change with bank
840 midi1.add (PacketX1 { 0x2bb20030 });
841 midi1.add (PacketX1 { 0x2bb22010 });
842 midi1.add (PacketX1 { 0x2bc24000 });
843 // program change without bank (different group and channel)
844 midi1.add (PacketX1 { 0x20c01000 });
846 Packets midi2;
847 midi2.add (PacketX2 { 0x4bc20001, 0x40003010 });
848 midi2.add (PacketX2 { 0x40c00000, 0x10000000 });
850 checkMidi1ToMidi2Conversion (midi1, midi2);
853 beginTest ("MIDI 1 -> MIDI 2 channel pressure conversions");
855 Packets midi1;
856 midi1.add (PacketX1 { 0x20df3000 });
858 Packets midi2;
859 midi2.add (PacketX2 { 0x40df0000, Conversion::scaleTo32 (0x30_u8) });
861 checkMidi1ToMidi2Conversion (midi1, midi2);
864 beginTest ("MIDI 1 -> MIDI 2 pitch bend conversions");
866 Packets midi1;
867 midi1.add (PacketX1 { 0x20e74567 });
869 Packets midi2;
870 midi2.add (PacketX2 { 0x40e70000, Conversion::scaleTo32 (static_cast<uint16_t> ((0x67 << 7) | 0x45)) });
872 checkMidi1ToMidi2Conversion (midi1, midi2);
876 private:
877 static Packets convertMidi2ToMidi1 (const Packets& midi2)
879 Packets r;
881 for (const auto& packet : midi2)
882 Conversion::midi2ToMidi1DefaultTranslation (packet, [&r] (const View& v) { r.add (v); });
884 return r;
887 static Packets convertMidi1ToMidi2 (const Packets& midi1)
889 Packets r;
890 Midi1ToMidi2DefaultTranslator translator;
892 for (const auto& packet : midi1)
893 translator.dispatch (packet, [&r] (const View& v) { r.add (v); });
895 return r;
898 void checkBytestreamConversion (const Packets& actual, const Packets& expected)
900 expectEquals ((int) actual.size(), (int) expected.size());
902 if (actual.size() != expected.size())
903 return;
905 auto actualPtr = actual.data();
907 std::for_each (expected.data(),
908 expected.data() + expected.size(),
909 [&] (const uint32_t word) { expectEquals ((uint64_t) *actualPtr++, (uint64_t) word); });
912 void checkMidi2ToMidi1Conversion (const Packets& midi2, const Packets& expected)
914 checkBytestreamConversion (convertMidi2ToMidi1 (midi2), expected);
917 void checkMidi1ToMidi2Conversion (const Packets& midi1, const Packets& expected)
919 checkBytestreamConversion (convertMidi1ToMidi2 (midi1), expected);
922 MidiMessage createRandomSysEx (Random& random, size_t sysExBytes)
924 std::vector<uint8_t> data;
925 data.reserve (sysExBytes);
927 for (size_t i = 0; i != sysExBytes; ++i)
928 data.push_back (uint8_t (random.nextInt (0x80)));
930 return MidiMessage::createSysExMessage (data.data(), int (data.size()));
933 PacketX1 createRandomUtilityUMP (Random& random)
935 const auto status = random.nextInt (3);
937 return PacketX1 { Utils::bytesToWord (0,
938 uint8_t (status << 0x4),
939 uint8_t (status == 0 ? 0 : random.nextInt (0x100)),
940 uint8_t (status == 0 ? 0 : random.nextInt (0x100))) };
943 PacketX1 createRandomRealtimeUMP (Random& random)
945 const auto status = [&]
947 switch (random.nextInt (6))
949 case 0: return 0xf8;
950 case 1: return 0xfa;
951 case 2: return 0xfb;
952 case 3: return 0xfc;
953 case 4: return 0xfe;
954 case 5: return 0xff;
957 jassertfalse;
958 return 0x00;
959 }();
961 return PacketX1 { Utils::bytesToWord (0x10, uint8_t (status), 0x00, 0x00) };
964 template <typename Fn>
965 void forEachNonSysExTestMessage (Random& random, Fn&& fn)
967 for (uint16_t counter = 0x80; counter != 0x100; ++counter)
969 const auto firstByte = (uint8_t) counter;
971 if (firstByte == 0xf0 || firstByte == 0xf7)
972 continue; // sysEx is tested separately
974 const auto length = MidiMessage::getMessageLengthFromFirstByte (firstByte);
975 const auto getDataByte = [&] { return uint8_t (random.nextInt (256) & 0x7f); };
977 const auto message = [&]
979 switch (length)
981 case 1: return MidiMessage (firstByte);
982 case 2: return MidiMessage (firstByte, getDataByte());
983 case 3: return MidiMessage (firstByte, getDataByte(), getDataByte());
986 return MidiMessage();
987 }();
989 fn (message);
993 #if JUCE_WINDOWS && ! JUCE_MINGW
994 #define JUCE_CHECKED_ITERATOR(msg, size) \
995 stdext::checked_array_iterator<typename std::remove_reference<decltype (msg)>::type> ((msg), (size_t) (size))
996 #else
997 #define JUCE_CHECKED_ITERATOR(msg, size) (msg)
998 #endif
1000 static bool equal (const MidiMessage& a, const MidiMessage& b) noexcept
1002 return a.getRawDataSize() == b.getRawDataSize()
1003 && std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
1004 JUCE_CHECKED_ITERATOR (b.getRawData(), b.getRawDataSize()));
1007 #undef JUCE_CHECKED_ITERATOR
1009 static bool equal (const MidiBuffer& a, const MidiBuffer& b) noexcept
1011 return a.data == b.data;
1015 static UniversalMidiPacketTests universalMidiPacketTests;