26MidiMessageSequence::MidiEventHolder::MidiEventHolder (
const MidiMessage& mm) : message (mm) {}
27MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
30MidiMessageSequence::MidiMessageSequence()
36 list.addCopiesOf (other.list);
38 for (
int i = 0; i < list.size(); ++i)
40 auto noteOffIndex = other.getIndexOfMatchingKeyUp (i);
42 if (noteOffIndex >= 0)
43 list.getUnchecked (i)->noteOffObject = list.getUnchecked (noteOffIndex);
55 : list (std::move (other.list))
61 list = std::move (other.list);
67 list.swapWith (other.list);
70void MidiMessageSequence::clear()
75int MidiMessageSequence::getNumEvents() const noexcept
90double MidiMessageSequence::getTimeOfMatchingKeyUp (
int index)
const noexcept
92 if (
auto* meh = list[index])
93 if (
auto* noteOff = meh->noteOffObject)
99int MidiMessageSequence::getIndexOfMatchingKeyUp (
int index)
const noexcept
101 if (
auto* meh = list[index])
103 if (
auto* noteOff = meh->noteOffObject)
105 for (
int i = index; i < list.size(); ++i)
106 if (list.getUnchecked (i) == noteOff)
118 return list.indexOf (event);
121int MidiMessageSequence::getNextIndexAtTime (
double timeStamp)
const noexcept
123 auto numEvents = list.size();
126 for (i = 0; i < numEvents; ++i)
127 if (list.getUnchecked (i)->message.getTimeStamp() >= timeStamp)
134double MidiMessageSequence::getStartTime() const noexcept
136 return getEventTime (0);
139double MidiMessageSequence::getEndTime() const noexcept
141 return getEventTime (list.size() - 1);
144double MidiMessageSequence::getEventTime (
const int index)
const noexcept
146 if (
auto* meh = list[index])
147 return meh->message.getTimeStamp();
156 auto time = newEvent->message.getTimeStamp();
159 for (i = list.size(); --i >= 0;)
160 if (list.getUnchecked (i)->message.getTimeStamp() <= time)
163 list.insert (i + 1, newEvent);
174 return addEvent (
new MidiEventHolder (std::move (newMessage)), timeAdjustment);
177void MidiMessageSequence::deleteEvent (
int index,
bool deleteMatchingNoteUp)
179 if (isPositiveAndBelow (index, list.size()))
181 if (deleteMatchingNoteUp)
182 deleteEvent (getIndexOfMatchingKeyUp (index),
false);
190 for (
auto* m : other)
193 newOne->message.addToTimeStamp (timeAdjustment);
201 double timeAdjustment,
202 double firstAllowableTime,
203 double endOfAllowableDestTimes)
205 for (
auto* m : other)
207 auto t = m->message.getTimeStamp() + timeAdjustment;
209 if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
212 newOne->message.setTimeStamp (t);
220void MidiMessageSequence::sort() noexcept
222 std::stable_sort (list.begin(), list.end(),
226void MidiMessageSequence::updateMatchedPairs() noexcept
228 for (
int i = 0; i < list.size(); ++i)
230 auto* meh = list.getUnchecked (i);
231 auto& m1 = meh->message;
235 meh->noteOffObject =
nullptr;
236 auto note = m1.getNoteNumber();
237 auto chan = m1.getChannel();
238 auto len = list.size();
240 for (
int j = i + 1; j < len; ++j)
242 auto* meh2 = list.getUnchecked (j);
243 auto& m = meh2->message;
245 if (m.getNoteNumber() == note && m.getChannel() == chan)
249 meh->noteOffObject = meh2;
255 auto newEvent =
new MidiEventHolder (MidiMessage::noteOff (chan, note));
256 list.insert (j, newEvent);
257 newEvent->message.setTimeStamp (m.getTimeStamp());
258 meh->noteOffObject = newEvent;
267void MidiMessageSequence::addTimeToMessages (
double delta)
noexcept
269 if (! approximatelyEqual (delta, 0.0))
271 m->message.addToTimeStamp (delta);
275void MidiMessageSequence::extractMidiChannelMessages (
const int channelNumberToExtract,
277 const bool alsoIncludeMetaEvents)
const
279 for (
auto* meh : list)
280 if (meh->message.isForChannel (channelNumberToExtract)
281 || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
282 destSequence.
addEvent (meh->message);
287 for (
auto* meh : list)
288 if (meh->message.isSysEx())
289 destSequence.
addEvent (meh->message);
292void MidiMessageSequence::deleteMidiChannelMessages (
const int channelNumberToRemove)
294 for (
int i = list.size(); --i >= 0;)
295 if (list.getUnchecked (i)->message.isForChannel (channelNumberToRemove))
299void MidiMessageSequence::deleteSysExMessages()
301 for (
int i = list.size(); --i >= 0;)
302 if (list.getUnchecked (i)->message.isSysEx())
307class OptionalPitchWheel
314 if (value.hasValue())
315 out.
add (MidiMessage::pitchWheel (channel, *value));
324class OptionalControllerValues
326 Optional<char> values[128];
329 void emit (
int channel, Array<MidiMessage>& out)
const
331 for (
auto it = std::begin (values); it != std::end (values); ++it)
333 out.add (MidiMessage::controllerEvent (channel, (
int) std::distance (std::begin (values), it), **it));
336 void set (
int controller,
int value)
338 values[controller] = (char) value;
342class OptionalProgramChange
344 Optional<char> value, bankLSB, bankMSB;
347 void emit (
int channel,
double time, Array<MidiMessage>& out)
const
349 if (! value.hasValue())
352 if (bankLSB.hasValue() && bankMSB.hasValue())
354 out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time));
355 out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time));
358 out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time));
362 bool trySetBank (
int controller,
int v)
366 case 0x00: bankMSB = (char) v;
return true;
367 case 0x20: bankLSB = (char) v;
return true;
373 void setProgram (
int v) { value = (char) v; }
376class ParameterNumberState
378 enum class Kind { rpn, nrpn };
380 Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb;
381 Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn;
388 void sendIfNecessary (
int channel,
double time, Array<MidiMessage>& out)
390 const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb;
391 const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb;
393 auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb);
394 const auto newest = std::tie (newestKind, newestMsb, newestLsb);
396 if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue())
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));
406 bool trySetProgramNumber (
int controller,
int value)
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;
420void MidiMessageSequence::createControllerUpdatesForTime (
int channel,
double time,
Array<MidiMessage>& dest)
422 OptionalProgramChange programChange;
423 OptionalControllerValues controllers;
424 OptionalPitchWheel pitchWheel;
425 ParameterNumberState parameterNumberState;
427 for (
const auto& item : list)
429 const auto& mm = item->message;
431 if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time))
434 if (mm.isController())
436 const auto num = mm.getControllerNumber();
438 if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue()))
441 if (programChange.trySetBank (num, mm.getControllerValue()))
444 constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 };
446 if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs))
448 parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest);
453 controllers.set (num, mm.getControllerValue());
456 else if (mm.isProgramChange())
458 programChange.setProgram (mm.getProgramChangeNumber());
460 else if (mm.isPitchWheel())
462 pitchWheel.set (mm.getPitchWheelValue());
466 pitchWheel.emit (channel, dest);
467 controllers.emit (channel, dest);
470 programChange.emit (channel, time, dest);
473 parameterNumberState.sendIfNecessary (channel, time, dest);
480struct MidiMessageSequenceTest final :
public UnitTest
482 MidiMessageSequenceTest()
483 :
UnitTest (
"MidiMessageSequence", UnitTestCategories::midi)
488 MidiMessageSequence s;
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));
501 s.updateMatchedPairs();
513 s.deleteEvent (0,
true);
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));
525 s.addSequence (s2, 0.0, 0.0, 8.0);
526 s.updateMatchedPairs();
532 struct ControlValue {
int control, value; };
536 int controllerBase, channel, parameter, value;
539 std::array<ControlValue, 4> getControlValues()
const
541 return { { { controllerBase + 1, (parameter >> 7) & 0x7f },
542 { controllerBase + 0, (parameter >> 0) & 0x7f },
543 { 0x06, (value >> 7) & 0x7f },
544 { 0x26, (value >> 0) & 0x7f } } };
547 void addToSequence (MidiMessageSequence& s)
const
549 for (
const auto& pair : getControlValues())
550 s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time);
553 bool matches (
const MidiMessage* begin,
const MidiMessage* end)
const
555 const auto isEqual = [
this] (
const ControlValue& cv,
const MidiMessage& msg)
557 return exactlyEqual (msg.getTimeStamp(), time)
558 && msg.isController()
559 && msg.getChannel() == channel
560 && msg.getControllerNumber() == cv.control
561 && msg.getControllerValue() == cv.value;
564 const auto pairs = getControlValues();
565 return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual);
569 const auto addNrpn = [&] (MidiMessageSequence& seq,
int channel,
int parameter,
int value,
double time = 0.0)
571 DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq);
574 const auto addRpn = [&] (MidiMessageSequence& seq,
int channel,
int parameter,
int value,
double time = 0.0)
576 DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq);
579 const auto checkNrpn = [&] (
const MidiMessage* begin,
const MidiMessage* end,
int channel,
int parameter,
int value,
double time = 0.0)
581 expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end));
584 const auto checkRpn = [&] (
const MidiMessage* begin,
const MidiMessage* end,
int channel,
int parameter,
int value,
double time = 0.0)
586 expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end));
589 beginTest (
"createControllerUpdatesForTime should emit (N)RPN components in the correct order");
591 const auto channel = 1;
592 const auto number = 200;
593 const auto value = 300;
595 MidiMessageSequence sequence;
596 addNrpn (sequence, channel, number, value);
598 Array<MidiMessage> m;
599 sequence.createControllerUpdatesForTime (channel, 1.0, m);
601 checkNrpn (m.begin(), m.end(), channel, number, value);
604 beginTest (
"createControllerUpdatesForTime ignores (N)RPNs after the final requested time");
606 const auto channel = 2;
607 const auto number = 123;
608 const auto value = 456;
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);
615 Array<MidiMessage> m;
616 sequence.createControllerUpdatesForTime (channel, 1.0, m);
618 checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5);
621 beginTest (
"createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate");
623 const auto channel = 2;
624 const auto numberA = 1111;
625 const auto valueA = 9999;
627 const auto numberB = 8888;
628 const auto valueB = 2222;
630 const auto numberC = 7777;
631 const auto valueC = 3333;
633 const auto numberD = 6666;
634 const auto valueD = 4444;
636 const auto time = 0.5;
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);
644 Array<MidiMessage> m;
645 sequence.createControllerUpdatesForTime (channel, time * 2, m);
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);
653 beginTest (
"createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels");
655 struct Info {
int channel, number, value; };
657 const Info infos[] { { 2, 1111, 9999 },
662 const auto time = 0.5;
664 MidiMessageSequence sequence;
666 for (
const auto& info : infos)
667 addRpn (sequence, info.channel, info.number, info.value, time);
669 for (
const auto& info : infos)
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);
677 const auto messagesAreEqual = [] (
const MidiMessage& a,
const MidiMessage& b)
679 return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
680 b.getRawData(), b.getRawData() + b.getRawDataSize());
683 beginTest (
"createControllerUpdatesForTime sends bank select messages when the next program is in a new bank");
685 MidiMessageSequence sequence;
687 const auto time = 0.0;
688 const auto channel = 1;
690 sequence.addEvent (MidiMessage::programChange (channel, 5), time);
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);
700 for (
const auto& e : finalEvents)
701 sequence.addEvent (e);
703 Array<MidiMessage> m;
704 sequence.createControllerUpdatesForTime (channel, 1.0, m);
706 expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual));
709 beginTest (
"createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages");
711 MidiMessageSequence sequence;
713 const auto time = 0.0;
714 const auto channel = 1;
726 for (
const auto& m : messages)
727 sequence.addEvent (m, time);
729 Array<MidiMessage> m;
730 sequence.createControllerUpdatesForTime (channel, 1.0, m);
732 expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual));
735 beginTest (
"createControllerUpdatesForTime does not emit redundant parameter number changes");
737 MidiMessageSequence sequence;
739 const auto time = 0.0;
740 const auto channel = 1;
748 for (
const auto& m : messages)
749 sequence.addEvent (m, time);
751 Array<MidiMessage> m;
752 sequence.createControllerUpdatesForTime (channel, 1.0, m);
758 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
761 beginTest (
"createControllerUpdatesForTime sets parameter number correctly at end of sequence");
763 MidiMessageSequence sequence;
765 const auto time = 0.0;
766 const auto channel = 1;
775 for (
const auto& m : messages)
776 sequence.addEvent (m, time);
778 const auto finalTime = 1.0;
780 Array<MidiMessage> m;
781 sequence.createControllerUpdatesForTime (channel, finalTime, m);
790 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
793 beginTest (
"createControllerUpdatesForTime does not emit duplicate parameter number change messages");
795 MidiMessageSequence sequence;
797 const auto time = 0.0;
798 const auto channel = 1;
812 for (
const auto& m : messages)
813 sequence.addEvent (m, time);
815 const auto finalTime = 1.0;
817 Array<MidiMessage> m;
818 sequence.createControllerUpdatesForTime (channel, finalTime, m);
830 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
833 beginTest (
"createControllerUpdatesForTime emits bank change messages immediately before program change");
835 MidiMessageSequence sequence;
837 const auto time = 0.0;
838 const auto channel = 1;
846 for (
const auto& m : messages)
847 sequence.addEvent (m, time);
849 const auto finalTime = 1.0;
851 Array<MidiMessage> m;
852 sequence.createControllerUpdatesForTime (channel, finalTime, m);
861 expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
866static MidiMessageSequenceTest midiMessageSequenceTests;
void add(const ElementType &newElement)
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
double getTimeStamp() const noexcept
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())