OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
juce_MidiMessageSequence.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {}
27MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
28
29//==============================================================================
30MidiMessageSequence::MidiMessageSequence()
31{
32}
33
34MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
35{
36 list.addCopiesOf (other.list);
37
38 for (int i = 0; i < list.size(); ++i)
39 {
40 auto noteOffIndex = other.getIndexOfMatchingKeyUp (i);
41
42 if (noteOffIndex >= 0)
43 list.getUnchecked (i)->noteOffObject = list.getUnchecked (noteOffIndex);
44 }
45}
46
47MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
48{
49 MidiMessageSequence otherCopy (other);
50 swapWith (otherCopy);
51 return *this;
52}
53
54MidiMessageSequence::MidiMessageSequence (MidiMessageSequence&& other) noexcept
55 : list (std::move (other.list))
56{
57}
58
59MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other) noexcept
60{
61 list = std::move (other.list);
62 return *this;
63}
64
65void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
66{
67 list.swapWith (other.list);
68}
69
70void MidiMessageSequence::clear()
71{
72 list.clear();
73}
74
75int MidiMessageSequence::getNumEvents() const noexcept
76{
77 return list.size();
78}
79
80MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (int index) const noexcept
81{
82 return list[index];
83}
84
85MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() noexcept { return list.begin(); }
86MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::begin() const noexcept { return list.begin(); }
87MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() noexcept { return list.end(); }
88MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::end() const noexcept { return list.end(); }
89
90double MidiMessageSequence::getTimeOfMatchingKeyUp (int index) const noexcept
91{
92 if (auto* meh = list[index])
93 if (auto* noteOff = meh->noteOffObject)
94 return noteOff->message.getTimeStamp();
95
96 return 0;
97}
98
99int MidiMessageSequence::getIndexOfMatchingKeyUp (int index) const noexcept
100{
101 if (auto* meh = list[index])
102 {
103 if (auto* noteOff = meh->noteOffObject)
104 {
105 for (int i = index; i < list.size(); ++i)
106 if (list.getUnchecked (i) == noteOff)
107 return i;
108
109 jassertfalse; // we've somehow got a pointer to a note-off object that isn't in the sequence
110 }
111 }
112
113 return -1;
114}
115
116int MidiMessageSequence::getIndexOf (const MidiEventHolder* event) const noexcept
117{
118 return list.indexOf (event);
119}
120
121int MidiMessageSequence::getNextIndexAtTime (double timeStamp) const noexcept
122{
123 auto numEvents = list.size();
124 int i;
125
126 for (i = 0; i < numEvents; ++i)
127 if (list.getUnchecked (i)->message.getTimeStamp() >= timeStamp)
128 break;
129
130 return i;
131}
132
133//==============================================================================
134double MidiMessageSequence::getStartTime() const noexcept
135{
136 return getEventTime (0);
137}
138
139double MidiMessageSequence::getEndTime() const noexcept
140{
141 return getEventTime (list.size() - 1);
142}
143
144double MidiMessageSequence::getEventTime (const int index) const noexcept
145{
146 if (auto* meh = list[index])
147 return meh->message.getTimeStamp();
148
149 return 0;
150}
151
152//==============================================================================
153MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiEventHolder* newEvent, double timeAdjustment)
154{
155 newEvent->message.addToTimeStamp (timeAdjustment);
156 auto time = newEvent->message.getTimeStamp();
157 int i;
158
159 for (i = list.size(); --i >= 0;)
160 if (list.getUnchecked (i)->message.getTimeStamp() <= time)
161 break;
162
163 list.insert (i + 1, newEvent);
164 return newEvent;
165}
166
167MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, double timeAdjustment)
168{
169 return addEvent (new MidiEventHolder (newMessage), timeAdjustment);
170}
171
172MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiMessage&& newMessage, double timeAdjustment)
173{
174 return addEvent (new MidiEventHolder (std::move (newMessage)), timeAdjustment);
175}
176
177void MidiMessageSequence::deleteEvent (int index, bool deleteMatchingNoteUp)
178{
179 if (isPositiveAndBelow (index, list.size()))
180 {
181 if (deleteMatchingNoteUp)
182 deleteEvent (getIndexOfMatchingKeyUp (index), false);
183
184 list.remove (index);
185 }
186}
187
188void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment)
189{
190 for (auto* m : other)
191 {
192 auto newOne = new MidiEventHolder (m->message);
193 newOne->message.addToTimeStamp (timeAdjustment);
194 list.add (newOne);
195 }
196
197 sort();
198}
199
200void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
201 double timeAdjustment,
202 double firstAllowableTime,
203 double endOfAllowableDestTimes)
204{
205 for (auto* m : other)
206 {
207 auto t = m->message.getTimeStamp() + timeAdjustment;
208
209 if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
210 {
211 auto newOne = new MidiEventHolder (m->message);
212 newOne->message.setTimeStamp (t);
213 list.add (newOne);
214 }
215 }
216
217 sort();
218}
219
220void MidiMessageSequence::sort() noexcept
221{
222 std::stable_sort (list.begin(), list.end(),
223 [] (const MidiEventHolder* a, const MidiEventHolder* b) { return a->message.getTimeStamp() < b->message.getTimeStamp(); });
224}
225
226void MidiMessageSequence::updateMatchedPairs() noexcept
227{
228 for (int i = 0; i < list.size(); ++i)
229 {
230 auto* meh = list.getUnchecked (i);
231 auto& m1 = meh->message;
232
233 if (m1.isNoteOn())
234 {
235 meh->noteOffObject = nullptr;
236 auto note = m1.getNoteNumber();
237 auto chan = m1.getChannel();
238 auto len = list.size();
239
240 for (int j = i + 1; j < len; ++j)
241 {
242 auto* meh2 = list.getUnchecked (j);
243 auto& m = meh2->message;
244
245 if (m.getNoteNumber() == note && m.getChannel() == chan)
246 {
247 if (m.isNoteOff())
248 {
249 meh->noteOffObject = meh2;
250 break;
251 }
252
253 if (m.isNoteOn())
254 {
255 auto newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note));
256 list.insert (j, newEvent);
257 newEvent->message.setTimeStamp (m.getTimeStamp());
258 meh->noteOffObject = newEvent;
259 break;
260 }
261 }
262 }
263 }
264 }
265}
266
267void MidiMessageSequence::addTimeToMessages (double delta) noexcept
268{
269 if (! approximatelyEqual (delta, 0.0))
270 for (auto* m : list)
271 m->message.addToTimeStamp (delta);
272}
273
274//==============================================================================
275void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
276 MidiMessageSequence& destSequence,
277 const bool alsoIncludeMetaEvents) const
278{
279 for (auto* meh : list)
280 if (meh->message.isForChannel (channelNumberToExtract)
281 || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
282 destSequence.addEvent (meh->message);
283}
284
285void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
286{
287 for (auto* meh : list)
288 if (meh->message.isSysEx())
289 destSequence.addEvent (meh->message);
290}
291
292void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
293{
294 for (int i = list.size(); --i >= 0;)
295 if (list.getUnchecked (i)->message.isForChannel (channelNumberToRemove))
296 list.remove (i);
297}
298
299void MidiMessageSequence::deleteSysExMessages()
300{
301 for (int i = list.size(); --i >= 0;)
302 if (list.getUnchecked (i)->message.isSysEx())
303 list.remove (i);
304}
305
306//==============================================================================
307class OptionalPitchWheel
308{
309 Optional<int> value;
310
311public:
312 void emit (int channel, Array<MidiMessage>& out) const
313 {
314 if (value.hasValue())
315 out.add (MidiMessage::pitchWheel (channel, *value));
316 }
317
318 void set (int v)
319 {
320 value = v;
321 }
322};
323
324class OptionalControllerValues
325{
326 Optional<char> values[128];
327
328public:
329 void emit (int channel, Array<MidiMessage>& out) const
330 {
331 for (auto it = std::begin (values); it != std::end (values); ++it)
332 if (it->hasValue())
333 out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it));
334 }
335
336 void set (int controller, int value)
337 {
338 values[controller] = (char) value;
339 }
340};
341
342class OptionalProgramChange
343{
344 Optional<char> value, bankLSB, bankMSB;
345
346public:
347 void emit (int channel, double time, Array<MidiMessage>& out) const
348 {
349 if (! value.hasValue())
350 return;
351
352 if (bankLSB.hasValue() && bankMSB.hasValue())
353 {
354 out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time));
355 out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time));
356 }
357
358 out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time));
359 }
360
361 // Returns true if this is a bank number change, and false otherwise.
362 bool trySetBank (int controller, int v)
363 {
364 switch (controller)
365 {
366 case 0x00: bankMSB = (char) v; return true;
367 case 0x20: bankLSB = (char) v; return true;
368 }
369
370 return false;
371 }
372
373 void setProgram (int v) { value = (char) v; }
374};
375
376class ParameterNumberState
377{
378 enum class Kind { rpn, nrpn };
379
380 Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb;
381 Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn;
382
383public:
384 // If the effective parameter number has changed since the last time this function was called,
385 // this will emit the current parameter in full (MSB and LSB).
386 // This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61)
387 // to ensure that the data message operates on the correct parameter number.
388 void sendIfNecessary (int channel, double time, Array<MidiMessage>& out)
389 {
390 const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb;
391 const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb;
392
393 auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb);
394 const auto newest = std::tie (newestKind, newestMsb, newestLsb);
395
396 if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue())
397 return;
398
399 out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time));
400 out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time));
401
402 lastSent = newest;
403 }
404
405 // Returns true if this is a parameter number change, and false otherwise.
406 bool trySetProgramNumber (int controller, int value)
407 {
408 switch (controller)
409 {
410 case 0x65: newestRpnMsb = (char) value; newestKind = Kind::rpn; return true;
411 case 0x64: newestRpnLsb = (char) value; newestKind = Kind::rpn; return true;
412 case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true;
413 case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true;
414 }
415
416 return false;
417 }
418};
419
420void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array<MidiMessage>& dest)
421{
422 OptionalProgramChange programChange;
423 OptionalControllerValues controllers;
424 OptionalPitchWheel pitchWheel;
425 ParameterNumberState parameterNumberState;
426
427 for (const auto& item : list)
428 {
429 const auto& mm = item->message;
430
431 if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time))
432 continue;
433
434 if (mm.isController())
435 {
436 const auto num = mm.getControllerNumber();
437
438 if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue()))
439 continue;
440
441 if (programChange.trySetBank (num, mm.getControllerValue()))
442 continue;
443
444 constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 };
445
446 if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs))
447 {
448 parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest);
449 dest.add (mm);
450 }
451 else
452 {
453 controllers.set (num, mm.getControllerValue());
454 }
455 }
456 else if (mm.isProgramChange())
457 {
458 programChange.setProgram (mm.getProgramChangeNumber());
459 }
460 else if (mm.isPitchWheel())
461 {
462 pitchWheel.set (mm.getPitchWheelValue());
463 }
464 }
465
466 pitchWheel.emit (channel, dest);
467 controllers.emit (channel, dest);
468
469 // Also emits bank change messages if necessary.
470 programChange.emit (channel, time, dest);
471
472 // Set the parameter number to its final state.
473 parameterNumberState.sendIfNecessary (channel, time, dest);
474}
475
476//==============================================================================
477//==============================================================================
478#if JUCE_UNIT_TESTS
479
480struct MidiMessageSequenceTest final : public UnitTest
481{
482 MidiMessageSequenceTest()
483 : UnitTest ("MidiMessageSequence", UnitTestCategories::midi)
484 {}
485
486 void runTest() override
487 {
488 MidiMessageSequence s;
489
490 s.addEvent (MidiMessage::noteOn (1, 60, 0.5f).withTimeStamp (0.0));
491 s.addEvent (MidiMessage::noteOff (1, 60, 0.5f).withTimeStamp (4.0));
492 s.addEvent (MidiMessage::noteOn (1, 30, 0.5f).withTimeStamp (2.0));
493 s.addEvent (MidiMessage::noteOff (1, 30, 0.5f).withTimeStamp (8.0));
494
495 beginTest ("Start & end time");
496 expectEquals (s.getStartTime(), 0.0);
497 expectEquals (s.getEndTime(), 8.0);
498 expectEquals (s.getEventTime (1), 2.0);
499
500 beginTest ("Matching note off & ons");
501 s.updateMatchedPairs();
502 expectEquals (s.getTimeOfMatchingKeyUp (0), 4.0);
503 expectEquals (s.getTimeOfMatchingKeyUp (1), 8.0);
504 expectEquals (s.getIndexOfMatchingKeyUp (0), 2);
505 expectEquals (s.getIndexOfMatchingKeyUp (1), 3);
506
507 beginTest ("Time & indices");
508 expectEquals (s.getNextIndexAtTime (0.5), 1);
509 expectEquals (s.getNextIndexAtTime (2.5), 2);
510 expectEquals (s.getNextIndexAtTime (9.0), 4);
511
512 beginTest ("Deleting events");
513 s.deleteEvent (0, true);
514 expectEquals (s.getNumEvents(), 2);
515
516 beginTest ("Merging sequences");
517 MidiMessageSequence s2;
518 s2.addEvent (MidiMessage::noteOn (2, 25, 0.5f).withTimeStamp (0.0));
519 s2.addEvent (MidiMessage::noteOn (2, 40, 0.5f).withTimeStamp (1.0));
520 s2.addEvent (MidiMessage::noteOff (2, 40, 0.5f).withTimeStamp (5.0));
521 s2.addEvent (MidiMessage::noteOn (2, 80, 0.5f).withTimeStamp (3.0));
522 s2.addEvent (MidiMessage::noteOff (2, 80, 0.5f).withTimeStamp (7.0));
523 s2.addEvent (MidiMessage::noteOff (2, 25, 0.5f).withTimeStamp (9.0));
524
525 s.addSequence (s2, 0.0, 0.0, 8.0); // Intentionally cut off the last note off
526 s.updateMatchedPairs();
527
528 expectEquals (s.getNumEvents(), 7);
529 expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off
530 expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0);
531
532 struct ControlValue { int control, value; };
533
534 struct DataEntry
535 {
536 int controllerBase, channel, parameter, value;
537 double time;
538
539 std::array<ControlValue, 4> getControlValues() const
540 {
541 return { { { controllerBase + 1, (parameter >> 7) & 0x7f },
542 { controllerBase + 0, (parameter >> 0) & 0x7f },
543 { 0x06, (value >> 7) & 0x7f },
544 { 0x26, (value >> 0) & 0x7f } } };
545 }
546
547 void addToSequence (MidiMessageSequence& s) const
548 {
549 for (const auto& pair : getControlValues())
550 s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time);
551 }
552
553 bool matches (const MidiMessage* begin, const MidiMessage* end) const
554 {
555 const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg)
556 {
557 return exactlyEqual (msg.getTimeStamp(), time)
558 && msg.isController()
559 && msg.getChannel() == channel
560 && msg.getControllerNumber() == cv.control
561 && msg.getControllerValue() == cv.value;
562 };
563
564 const auto pairs = getControlValues();
565 return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual);
566 }
567 };
568
569 const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
570 {
571 DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq);
572 };
573
574 const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
575 {
576 DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq);
577 };
578
579 const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
580 {
581 expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end));
582 };
583
584 const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
585 {
586 expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end));
587 };
588
589 beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order");
590 {
591 const auto channel = 1;
592 const auto number = 200;
593 const auto value = 300;
594
595 MidiMessageSequence sequence;
596 addNrpn (sequence, channel, number, value);
597
598 Array<MidiMessage> m;
599 sequence.createControllerUpdatesForTime (channel, 1.0, m);
600
601 checkNrpn (m.begin(), m.end(), channel, number, value);
602 }
603
604 beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time");
605 {
606 const auto channel = 2;
607 const auto number = 123;
608 const auto value = 456;
609
610 MidiMessageSequence sequence;
611 addRpn (sequence, channel, number, value, 0.5);
612 addRpn (sequence, channel, 111, 222, 1.5);
613 addRpn (sequence, channel, 333, 444, 2.5);
614
615 Array<MidiMessage> m;
616 sequence.createControllerUpdatesForTime (channel, 1.0, m);
617
618 checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5);
619 }
620
621 beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate");
622 {
623 const auto channel = 2;
624 const auto numberA = 1111;
625 const auto valueA = 9999;
626
627 const auto numberB = 8888;
628 const auto valueB = 2222;
629
630 const auto numberC = 7777;
631 const auto valueC = 3333;
632
633 const auto numberD = 6666;
634 const auto valueD = 4444;
635
636 const auto time = 0.5;
637
638 MidiMessageSequence sequence;
639 addRpn (sequence, channel, numberA, valueA, time);
640 addRpn (sequence, channel, numberB, valueB, time);
641 addNrpn (sequence, channel, numberC, valueC, time);
642 addNrpn (sequence, channel, numberD, valueD, time);
643
644 Array<MidiMessage> m;
645 sequence.createControllerUpdatesForTime (channel, time * 2, m);
646
647 checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), channel, numberA, valueA, time);
648 checkRpn (std::next (m.begin(), 4), std::next (m.begin(), 8), channel, numberB, valueB, time);
649 checkNrpn (std::next (m.begin(), 8), std::next (m.begin(), 12), channel, numberC, valueC, time);
650 checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time);
651 }
652
653 beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels");
654 {
655 struct Info { int channel, number, value; };
656
657 const Info infos[] { { 2, 1111, 9999 },
658 { 8, 8888, 2222 },
659 { 5, 7777, 3333 },
660 { 1, 6666, 4444 } };
661
662 const auto time = 0.5;
663
664 MidiMessageSequence sequence;
665
666 for (const auto& info : infos)
667 addRpn (sequence, info.channel, info.number, info.value, time);
668
669 for (const auto& info : infos)
670 {
671 Array<MidiMessage> m;
672 sequence.createControllerUpdatesForTime (info.channel, time * 2, m);
673 checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), info.channel, info.number, info.value, time);
674 }
675 }
676
677 const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b)
678 {
679 return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
680 b.getRawData(), b.getRawData() + b.getRawDataSize());
681 };
682
683 beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank");
684 {
685 MidiMessageSequence sequence;
686
687 const auto time = 0.0;
688 const auto channel = 1;
689
690 sequence.addEvent (MidiMessage::programChange (channel, 5), time);
691
692 sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time);
693 sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time);
694 sequence.addEvent (MidiMessage::programChange (channel, 63), time);
695
696 const Array<MidiMessage> finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50),
697 MidiMessage::controllerEvent (channel, 0x20, 40),
698 MidiMessage::programChange (channel, 30) };
699
700 for (const auto& e : finalEvents)
701 sequence.addEvent (e);
702
703 Array<MidiMessage> m;
704 sequence.createControllerUpdatesForTime (channel, 1.0, m);
705
706 expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual));
707 }
708
709 beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages");
710 {
711 MidiMessageSequence sequence;
712
713 const auto time = 0.0;
714 const auto channel = 1;
715
716 const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x60, 0),
717 MidiMessage::controllerEvent (channel, 0x06, 100),
718 MidiMessage::controllerEvent (channel, 0x26, 50),
719 MidiMessage::controllerEvent (channel, 0x60, 10),
720 MidiMessage::controllerEvent (channel, 0x61, 10),
721 MidiMessage::controllerEvent (channel, 0x06, 20),
722 MidiMessage::controllerEvent (channel, 0x26, 30),
723 MidiMessage::controllerEvent (channel, 0x61, 10),
724 MidiMessage::controllerEvent (channel, 0x61, 20) };
725
726 for (const auto& m : messages)
727 sequence.addEvent (m, time);
728
729 Array<MidiMessage> m;
730 sequence.createControllerUpdatesForTime (channel, 1.0, m);
731
732 expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual));
733 }
734
735 beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes");
736 {
737 MidiMessageSequence sequence;
738
739 const auto time = 0.0;
740 const auto channel = 1;
741
742 const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
743 MidiMessage::controllerEvent (channel, 0x64, 100),
744 MidiMessage::controllerEvent (channel, 0x63, 50),
745 MidiMessage::controllerEvent (channel, 0x62, 10),
746 MidiMessage::controllerEvent (channel, 0x06, 10) };
747
748 for (const auto& m : messages)
749 sequence.addEvent (m, time);
750
751 Array<MidiMessage> m;
752 sequence.createControllerUpdatesForTime (channel, 1.0, m);
753
754 const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
755 MidiMessage::controllerEvent (channel, 0x62, 10),
756 MidiMessage::controllerEvent (channel, 0x06, 10) };
757
758 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
759 }
760
761 beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence");
762 {
763 MidiMessageSequence sequence;
764
765 const auto time = 0.0;
766 const auto channel = 1;
767
768 const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
769 MidiMessage::controllerEvent (channel, 0x64, 100),
770 MidiMessage::controllerEvent (channel, 0x63, 50),
771 MidiMessage::controllerEvent (channel, 0x62, 10),
772 MidiMessage::controllerEvent (channel, 0x06, 10),
773 MidiMessage::controllerEvent (channel, 0x64, 5) };
774
775 for (const auto& m : messages)
776 sequence.addEvent (m, time);
777
778 const auto finalTime = 1.0;
779
780 Array<MidiMessage> m;
781 sequence.createControllerUpdatesForTime (channel, finalTime, m);
782
783 const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
784 MidiMessage::controllerEvent (channel, 0x62, 10),
785 MidiMessage::controllerEvent (channel, 0x06, 10),
786 // Note: we should send both the MSB and LSB!
787 MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
788 MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) };
789
790 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
791 }
792
793 beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages");
794 {
795 MidiMessageSequence sequence;
796
797 const auto time = 0.0;
798 const auto channel = 1;
799
800 const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 1),
801 MidiMessage::controllerEvent (channel, 0x64, 2),
802 MidiMessage::controllerEvent (channel, 0x63, 3),
803 MidiMessage::controllerEvent (channel, 0x62, 4),
804 MidiMessage::controllerEvent (channel, 0x06, 10),
805 MidiMessage::controllerEvent (channel, 0x63, 30),
806 MidiMessage::controllerEvent (channel, 0x62, 40),
807 MidiMessage::controllerEvent (channel, 0x63, 3),
808 MidiMessage::controllerEvent (channel, 0x62, 4),
809 MidiMessage::controllerEvent (channel, 0x60, 5),
810 MidiMessage::controllerEvent (channel, 0x65, 10) };
811
812 for (const auto& m : messages)
813 sequence.addEvent (m, time);
814
815 const auto finalTime = 1.0;
816
817 Array<MidiMessage> m;
818 sequence.createControllerUpdatesForTime (channel, finalTime, m);
819
820 const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 3),
821 MidiMessage::controllerEvent (channel, 0x62, 4),
822 MidiMessage::controllerEvent (channel, 0x06, 10),
823 // Parameter number is set to (30, 40) then back to (3, 4),
824 // so there is no need to resend it
825 MidiMessage::controllerEvent (channel, 0x60, 5),
826 // Set parameter number to final value
827 MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime),
828 MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) };
829
830 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
831 }
832
833 beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change");
834 {
835 MidiMessageSequence sequence;
836
837 const auto time = 0.0;
838 const auto channel = 1;
839
840 const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x00, 1),
841 MidiMessage::controllerEvent (channel, 0x20, 2),
842 MidiMessage::controllerEvent (channel, 0x65, 0),
843 MidiMessage::controllerEvent (channel, 0x64, 0),
844 MidiMessage::programChange (channel, 5) };
845
846 for (const auto& m : messages)
847 sequence.addEvent (m, time);
848
849 const auto finalTime = 1.0;
850
851 Array<MidiMessage> m;
852 sequence.createControllerUpdatesForTime (channel, finalTime, m);
853
854 const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x00, 1),
855 MidiMessage::controllerEvent (channel, 0x20, 2),
856 MidiMessage::programChange (channel, 5),
857 MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
858 MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) };
859
860
861 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
862 }
863 }
864};
865
866static MidiMessageSequenceTest midiMessageSequenceTests;
867
868#endif
869
870} // namespace juce
void add(const ElementType &newElement)
Definition juce_Array.h:418
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
void addToTimeStamp(double delta) noexcept
static MidiMessage programChange(int channel, int programNumber) noexcept
MidiMessage withTimeStamp(double newTimestamp) const
void expectEquals(ValueType actual, ValueType expected, String failureMessage=String())
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
virtual void runTest()=0