| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - 
 - MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {}
 - MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
 - 
 - //==============================================================================
 - MidiMessageSequence::MidiMessageSequence()
 - {
 - }
 - 
 - MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other)
 - {
 -     list.addCopiesOf (other.list);
 - 
 -     for (int i = 0; i < list.size(); ++i)
 -     {
 -         auto noteOffIndex = other.getIndexOfMatchingKeyUp (i);
 - 
 -         if (noteOffIndex >= 0)
 -             list.getUnchecked(i)->noteOffObject = list.getUnchecked (noteOffIndex);
 -     }
 - }
 - 
 - MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other)
 - {
 -     MidiMessageSequence otherCopy (other);
 -     swapWith (otherCopy);
 -     return *this;
 - }
 - 
 - MidiMessageSequence::MidiMessageSequence (MidiMessageSequence&& other) noexcept
 -     : list (std::move (other.list))
 - {
 - }
 - 
 - MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other) noexcept
 - {
 -     list = std::move (other.list);
 -     return *this;
 - }
 - 
 - void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
 - {
 -     list.swapWith (other.list);
 - }
 - 
 - void MidiMessageSequence::clear()
 - {
 -     list.clear();
 - }
 - 
 - int MidiMessageSequence::getNumEvents() const noexcept
 - {
 -     return list.size();
 - }
 - 
 - MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (int index) const noexcept
 - {
 -     return list[index];
 - }
 - 
 - MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() noexcept               { return list.begin(); }
 - MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::begin() const noexcept   { return list.begin(); }
 - MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() noexcept                 { return list.end(); }
 - MidiMessageSequence::MidiEventHolder* const* MidiMessageSequence::end() const noexcept     { return list.end(); }
 - 
 - double MidiMessageSequence::getTimeOfMatchingKeyUp (int index) const noexcept
 - {
 -     if (auto* meh = list[index])
 -         if (auto* noteOff = meh->noteOffObject)
 -             return noteOff->message.getTimeStamp();
 - 
 -     return 0;
 - }
 - 
 - int MidiMessageSequence::getIndexOfMatchingKeyUp (int index) const noexcept
 - {
 -     if (auto* meh = list[index])
 -     {
 -         if (auto* noteOff = meh->noteOffObject)
 -         {
 -             for (int i = index; i < list.size(); ++i)
 -                 if (list.getUnchecked(i) == noteOff)
 -                     return i;
 - 
 -             jassertfalse; // we've somehow got a pointer to a note-off object that isn't in the sequence
 -         }
 -     }
 - 
 -     return -1;
 - }
 - 
 - int MidiMessageSequence::getIndexOf (const MidiEventHolder* event) const noexcept
 - {
 -     return list.indexOf (event);
 - }
 - 
 - int MidiMessageSequence::getNextIndexAtTime (double timeStamp) const noexcept
 - {
 -     auto numEvents = list.size();
 -     int i;
 - 
 -     for (i = 0; i < numEvents; ++i)
 -         if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
 -             break;
 - 
 -     return i;
 - }
 - 
 - //==============================================================================
 - double MidiMessageSequence::getStartTime() const noexcept
 - {
 -     return getEventTime (0);
 - }
 - 
 - double MidiMessageSequence::getEndTime() const noexcept
 - {
 -     return getEventTime (list.size() - 1);
 - }
 - 
 - double MidiMessageSequence::getEventTime (const int index) const noexcept
 - {
 -     if (auto* meh = list[index])
 -         return meh->message.getTimeStamp();
 - 
 -     return 0;
 - }
 - 
 - //==============================================================================
 - MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiEventHolder* newEvent, double timeAdjustment)
 - {
 -     newEvent->message.addToTimeStamp (timeAdjustment);
 -     auto time = newEvent->message.getTimeStamp();
 -     int i;
 - 
 -     for (i = list.size(); --i >= 0;)
 -         if (list.getUnchecked(i)->message.getTimeStamp() <= time)
 -             break;
 - 
 -     list.insert (i + 1, newEvent);
 -     return newEvent;
 - }
 - 
 - MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, double timeAdjustment)
 - {
 -     return addEvent (new MidiEventHolder (newMessage), timeAdjustment);
 - }
 - 
 - MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiMessage&& newMessage, double timeAdjustment)
 - {
 -     return addEvent (new MidiEventHolder (std::move (newMessage)), timeAdjustment);
 - }
 - 
 - void MidiMessageSequence::deleteEvent (int index, bool deleteMatchingNoteUp)
 - {
 -     if (isPositiveAndBelow (index, list.size()))
 -     {
 -         if (deleteMatchingNoteUp)
 -             deleteEvent (getIndexOfMatchingKeyUp (index), false);
 - 
 -         list.remove (index);
 -     }
 - }
 - 
 - void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment)
 - {
 -     for (auto* m : other)
 -     {
 -         auto newOne = new MidiEventHolder (m->message);
 -         newOne->message.addToTimeStamp (timeAdjustment);
 -         list.add (newOne);
 -     }
 - 
 -     sort();
 - }
 - 
 - void MidiMessageSequence::addSequence (const MidiMessageSequence& other,
 -                                        double timeAdjustment,
 -                                        double firstAllowableTime,
 -                                        double endOfAllowableDestTimes)
 - {
 -     for (auto* m : other)
 -     {
 -         auto t = m->message.getTimeStamp() + timeAdjustment;
 - 
 -         if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
 -         {
 -             auto newOne = new MidiEventHolder (m->message);
 -             newOne->message.setTimeStamp (t);
 -             list.add (newOne);
 -         }
 -     }
 - 
 -     sort();
 - }
 - 
 - void MidiMessageSequence::sort() noexcept
 - {
 -     std::stable_sort (list.begin(), list.end(),
 -                       [] (const MidiEventHolder* a, const MidiEventHolder* b) { return a->message.getTimeStamp() < b->message.getTimeStamp(); });
 - }
 - 
 - void MidiMessageSequence::updateMatchedPairs() noexcept
 - {
 -     for (int i = 0; i < list.size(); ++i)
 -     {
 -         auto* meh = list.getUnchecked(i);
 -         auto& m1 = meh->message;
 - 
 -         if (m1.isNoteOn())
 -         {
 -             meh->noteOffObject = nullptr;
 -             auto note = m1.getNoteNumber();
 -             auto chan = m1.getChannel();
 -             auto len = list.size();
 - 
 -             for (int j = i + 1; j < len; ++j)
 -             {
 -                 auto* meh2 = list.getUnchecked(j);
 -                 auto& m = meh2->message;
 - 
 -                 if (m.getNoteNumber() == note && m.getChannel() == chan)
 -                 {
 -                     if (m.isNoteOff())
 -                     {
 -                         meh->noteOffObject = meh2;
 -                         break;
 -                     }
 - 
 -                     if (m.isNoteOn())
 -                     {
 -                         auto newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note));
 -                         list.insert (j, newEvent);
 -                         newEvent->message.setTimeStamp (m.getTimeStamp());
 -                         meh->noteOffObject = newEvent;
 -                         break;
 -                     }
 -                 }
 -             }
 -         }
 -     }
 - }
 - 
 - void MidiMessageSequence::addTimeToMessages (double delta) noexcept
 - {
 -     if (! approximatelyEqual (delta, 0.0))
 -         for (auto* m : list)
 -             m->message.addToTimeStamp (delta);
 - }
 - 
 - //==============================================================================
 - void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract,
 -                                                       MidiMessageSequence& destSequence,
 -                                                       const bool alsoIncludeMetaEvents) const
 - {
 -     for (auto* meh : list)
 -         if (meh->message.isForChannel (channelNumberToExtract)
 -              || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
 -             destSequence.addEvent (meh->message);
 - }
 - 
 - void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const
 - {
 -     for (auto* meh : list)
 -         if (meh->message.isSysEx())
 -             destSequence.addEvent (meh->message);
 - }
 - 
 - void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove)
 - {
 -     for (int i = list.size(); --i >= 0;)
 -         if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
 -             list.remove(i);
 - }
 - 
 - void MidiMessageSequence::deleteSysExMessages()
 - {
 -     for (int i = list.size(); --i >= 0;)
 -         if (list.getUnchecked(i)->message.isSysEx())
 -             list.remove(i);
 - }
 - 
 - //==============================================================================
 - class OptionalPitchWheel
 - {
 -     Optional<int> value;
 - 
 - public:
 -     void emit (int channel, Array<MidiMessage>& out) const
 -     {
 -         if (value.hasValue())
 -             out.add (MidiMessage::pitchWheel (channel, *value));
 -     }
 - 
 -     void set (int v)
 -     {
 -         value = v;
 -     }
 - };
 - 
 - class OptionalControllerValues
 - {
 -     Optional<char> values[128];
 - 
 - public:
 -     void emit (int channel, Array<MidiMessage>& out) const
 -     {
 -         for (auto it = std::begin (values); it != std::end (values); ++it)
 -             if (it->hasValue())
 -                 out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it));
 -     }
 - 
 -     void set (int controller, int value)
 -     {
 -         values[controller] = (char) value;
 -     }
 - };
 - 
 - class OptionalProgramChange
 - {
 -     Optional<char> value, bankLSB, bankMSB;
 - 
 - public:
 -     void emit (int channel, double time, Array<MidiMessage>& out) const
 -     {
 -         if (! value.hasValue())
 -             return;
 - 
 -         if (bankLSB.hasValue() && bankMSB.hasValue())
 -         {
 -             out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time));
 -             out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time));
 -         }
 - 
 -         out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time));
 -     }
 - 
 -     // Returns true if this is a bank number change, and false otherwise.
 -     bool trySetBank (int controller, int v)
 -     {
 -         switch (controller)
 -         {
 -             case 0x00: bankMSB = (char) v; return true;
 -             case 0x20: bankLSB = (char) v; return true;
 -         }
 - 
 -         return false;
 -     }
 - 
 -     void setProgram (int v) { value = (char) v; }
 - };
 - 
 - class ParameterNumberState
 - {
 -     enum class Kind { rpn, nrpn };
 - 
 -     Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb;
 -     Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn;
 - 
 - public:
 -     // If the effective parameter number has changed since the last time this function was called,
 -     // this will emit the current parameter in full (MSB and LSB).
 -     // This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61)
 -     // to ensure that the data message operates on the correct parameter number.
 -     void sendIfNecessary (int channel, double time, Array<MidiMessage>& out)
 -     {
 -         const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb;
 -         const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb;
 - 
 -         auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb);
 -         const auto newest = std::tie (newestKind, newestMsb, newestLsb);
 - 
 -         if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue())
 -             return;
 - 
 -         out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time));
 -         out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time));
 - 
 -         lastSent = newest;
 -     }
 - 
 -     // Returns true if this is a parameter number change, and false otherwise.
 -     bool trySetProgramNumber (int controller, int value)
 -     {
 -         switch (controller)
 -         {
 -             case 0x65: newestRpnMsb  = (char) value; newestKind = Kind::rpn;  return true;
 -             case 0x64: newestRpnLsb  = (char) value; newestKind = Kind::rpn;  return true;
 -             case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true;
 -             case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true;
 -         }
 - 
 -         return false;
 -     }
 - };
 - 
 - void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array<MidiMessage>& dest)
 - {
 -     OptionalProgramChange programChange;
 -     OptionalControllerValues controllers;
 -     OptionalPitchWheel pitchWheel;
 -     ParameterNumberState parameterNumberState;
 - 
 -     for (const auto& item : list)
 -     {
 -         const auto& mm = item->message;
 - 
 -         if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time))
 -             continue;
 - 
 -         if (mm.isController())
 -         {
 -             const auto num = mm.getControllerNumber();
 - 
 -             if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue()))
 -                 continue;
 - 
 -             if (programChange.trySetBank (num, mm.getControllerValue()))
 -                 continue;
 - 
 -             constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 };
 - 
 -             if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs))
 -             {
 -                 parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest);
 -                 dest.add (mm);
 -             }
 -             else
 -             {
 -                 controllers.set (num, mm.getControllerValue());
 -             }
 -         }
 -         else if (mm.isProgramChange())
 -         {
 -             programChange.setProgram (mm.getProgramChangeNumber());
 -         }
 -         else if (mm.isPitchWheel())
 -         {
 -             pitchWheel.set (mm.getPitchWheelValue());
 -         }
 -     }
 - 
 -     pitchWheel.emit (channel, dest);
 -     controllers.emit (channel, dest);
 - 
 -     // Also emits bank change messages if necessary.
 -     programChange.emit (channel, time, dest);
 - 
 -     // Set the parameter number to its final state.
 -     parameterNumberState.sendIfNecessary (channel, time, dest);
 - }
 - 
 - //==============================================================================
 - //==============================================================================
 - #if JUCE_UNIT_TESTS
 - 
 - struct MidiMessageSequenceTest  : public UnitTest
 - {
 -     MidiMessageSequenceTest()
 -         : UnitTest ("MidiMessageSequence", UnitTestCategories::midi)
 -     {}
 - 
 -     void runTest() override
 -     {
 -         MidiMessageSequence s;
 - 
 -         s.addEvent (MidiMessage::noteOn  (1, 60, 0.5f).withTimeStamp (0.0));
 -         s.addEvent (MidiMessage::noteOff (1, 60, 0.5f).withTimeStamp (4.0));
 -         s.addEvent (MidiMessage::noteOn  (1, 30, 0.5f).withTimeStamp (2.0));
 -         s.addEvent (MidiMessage::noteOff (1, 30, 0.5f).withTimeStamp (8.0));
 - 
 -         beginTest ("Start & end time");
 -         expectEquals (s.getStartTime(), 0.0);
 -         expectEquals (s.getEndTime(), 8.0);
 -         expectEquals (s.getEventTime (1), 2.0);
 - 
 -         beginTest ("Matching note off & ons");
 -         s.updateMatchedPairs();
 -         expectEquals (s.getTimeOfMatchingKeyUp (0), 4.0);
 -         expectEquals (s.getTimeOfMatchingKeyUp (1), 8.0);
 -         expectEquals (s.getIndexOfMatchingKeyUp (0), 2);
 -         expectEquals (s.getIndexOfMatchingKeyUp (1), 3);
 - 
 -         beginTest ("Time & indices");
 -         expectEquals (s.getNextIndexAtTime (0.5), 1);
 -         expectEquals (s.getNextIndexAtTime (2.5), 2);
 -         expectEquals (s.getNextIndexAtTime (9.0), 4);
 - 
 -         beginTest ("Deleting events");
 -         s.deleteEvent (0, true);
 -         expectEquals (s.getNumEvents(), 2);
 - 
 -         beginTest ("Merging sequences");
 -         MidiMessageSequence s2;
 -         s2.addEvent (MidiMessage::noteOn  (2, 25, 0.5f).withTimeStamp (0.0));
 -         s2.addEvent (MidiMessage::noteOn  (2, 40, 0.5f).withTimeStamp (1.0));
 -         s2.addEvent (MidiMessage::noteOff (2, 40, 0.5f).withTimeStamp (5.0));
 -         s2.addEvent (MidiMessage::noteOn  (2, 80, 0.5f).withTimeStamp (3.0));
 -         s2.addEvent (MidiMessage::noteOff (2, 80, 0.5f).withTimeStamp (7.0));
 -         s2.addEvent (MidiMessage::noteOff (2, 25, 0.5f).withTimeStamp (9.0));
 - 
 -         s.addSequence (s2, 0.0, 0.0, 8.0); // Intentionally cut off the last note off
 -         s.updateMatchedPairs();
 - 
 -         expectEquals (s.getNumEvents(), 7);
 -         expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off
 -         expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0);
 - 
 -         struct ControlValue { int control, value; };
 - 
 -         struct DataEntry
 -         {
 -             int controllerBase, channel, parameter, value;
 -             double time;
 - 
 -             std::array<ControlValue, 4> getControlValues() const
 -             {
 -                 return { { { controllerBase + 1, (parameter >> 7) & 0x7f },
 -                            { controllerBase + 0, (parameter >> 0) & 0x7f },
 -                            { 0x06,               (value     >> 7) & 0x7f },
 -                            { 0x26,               (value     >> 0) & 0x7f } } };
 -             }
 - 
 -             void addToSequence (MidiMessageSequence& s) const
 -             {
 -                 for (const auto& pair : getControlValues())
 -                     s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time);
 -             }
 - 
 -             bool matches (const MidiMessage* begin, const MidiMessage* end) const
 -             {
 -                 const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg)
 -                 {
 -                     return exactlyEqual (msg.getTimeStamp(), time)
 -                         && msg.isController()
 -                         && msg.getChannel() == channel
 -                         && msg.getControllerNumber() == cv.control
 -                         && msg.getControllerValue()  == cv.value;
 -                 };
 - 
 -                 const auto pairs = getControlValues();
 -                 return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual);
 -             }
 -         };
 - 
 -         const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
 -         {
 -             DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq);
 -         };
 - 
 -         const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
 -         {
 -             DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq);
 -         };
 - 
 -         const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
 -         {
 -             expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end));
 -         };
 - 
 -         const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
 -         {
 -             expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end));
 -         };
 - 
 -         beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order");
 -         {
 -             const auto channel = 1;
 -             const auto number = 200;
 -             const auto value = 300;
 - 
 -             MidiMessageSequence sequence;
 -             addNrpn (sequence, channel, number, value);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, 1.0, m);
 - 
 -             checkNrpn (m.begin(), m.end(), channel, number, value);
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time");
 -         {
 -             const auto channel = 2;
 -             const auto number = 123;
 -             const auto value = 456;
 - 
 -             MidiMessageSequence sequence;
 -             addRpn (sequence, channel, number, value, 0.5);
 -             addRpn (sequence, channel, 111,    222,   1.5);
 -             addRpn (sequence, channel, 333,    444,   2.5);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, 1.0, m);
 - 
 -             checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5);
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate");
 -         {
 -             const auto channel = 2;
 -             const auto numberA = 1111;
 -             const auto valueA = 9999;
 - 
 -             const auto numberB = 8888;
 -             const auto valueB = 2222;
 - 
 -             const auto numberC = 7777;
 -             const auto valueC = 3333;
 - 
 -             const auto numberD = 6666;
 -             const auto valueD = 4444;
 - 
 -             const auto time = 0.5;
 - 
 -             MidiMessageSequence sequence;
 -             addRpn  (sequence, channel, numberA, valueA, time);
 -             addRpn  (sequence, channel, numberB, valueB, time);
 -             addNrpn (sequence, channel, numberC, valueC, time);
 -             addNrpn (sequence, channel, numberD, valueD, time);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, time * 2, m);
 - 
 -             checkRpn  (std::next (m.begin(), 0),  std::next (m.begin(), 4),  channel, numberA, valueA, time);
 -             checkRpn  (std::next (m.begin(), 4),  std::next (m.begin(), 8),  channel, numberB, valueB, time);
 -             checkNrpn (std::next (m.begin(), 8),  std::next (m.begin(), 12), channel, numberC, valueC, time);
 -             checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time);
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels");
 -         {
 -             struct Info { int channel, number, value; };
 - 
 -             const Info infos[] { { 2, 1111, 9999 },
 -                                  { 8, 8888, 2222 },
 -                                  { 5, 7777, 3333 },
 -                                  { 1, 6666, 4444 } };
 - 
 -             const auto time = 0.5;
 - 
 -             MidiMessageSequence sequence;
 - 
 -             for (const auto& info : infos)
 -                 addRpn (sequence, info.channel, info.number, info.value, time);
 - 
 -             for (const auto& info : infos)
 -             {
 -                 Array<MidiMessage> m;
 -                 sequence.createControllerUpdatesForTime (info.channel, time * 2, m);
 -                 checkRpn  (std::next (m.begin(), 0),  std::next (m.begin(), 4),  info.channel, info.number, info.value, time);
 -             }
 -         }
 - 
 -         const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b)
 -         {
 -             return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
 -                                b.getRawData(), b.getRawData() + b.getRawDataSize());
 -         };
 - 
 -         beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             sequence.addEvent (MidiMessage::programChange (channel, 5), time);
 - 
 -             sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time);
 -             sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time);
 -             sequence.addEvent (MidiMessage::programChange (channel, 63), time);
 - 
 -             const Array<MidiMessage> finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50),
 -                                                    MidiMessage::controllerEvent (channel, 0x20, 40),
 -                                                    MidiMessage::programChange (channel, 30) };
 - 
 -             for (const auto& e : finalEvents)
 -                 sequence.addEvent (e);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, 1.0, m);
 - 
 -             expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual));
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x60, 0),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 100),
 -                                                 MidiMessage::controllerEvent (channel, 0x26, 50),
 -                                                 MidiMessage::controllerEvent (channel, 0x60, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x61, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 20),
 -                                                 MidiMessage::controllerEvent (channel, 0x26, 30),
 -                                                 MidiMessage::controllerEvent (channel, 0x61, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x61, 20) };
 - 
 -             for (const auto& m : messages)
 -                 sequence.addEvent (m, time);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, 1.0, m);
 - 
 -             expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual));
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 100),
 -                                                 MidiMessage::controllerEvent (channel, 0x63, 50),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10) };
 - 
 -             for (const auto& m : messages)
 -                 sequence.addEvent (m, time);
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, 1.0, m);
 - 
 -             const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10) };
 - 
 -             expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 100),
 -                                                 MidiMessage::controllerEvent (channel, 0x63, 50),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 5) };
 - 
 -             for (const auto& m : messages)
 -                 sequence.addEvent (m, time);
 - 
 -             const auto finalTime = 1.0;
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, finalTime, m);
 - 
 -             const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10),
 -                                                 // Note: we should send both the MSB and LSB!
 -                                                 MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) };
 - 
 -             expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 1),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 2),
 -                                                 MidiMessage::controllerEvent (channel, 0x63, 3),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 4),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10),
 -                                                 MidiMessage::controllerEvent (channel, 0x63, 30),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 40),
 -                                                 MidiMessage::controllerEvent (channel, 0x63, 3),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 4),
 -                                                 MidiMessage::controllerEvent (channel, 0x60, 5),
 -                                                 MidiMessage::controllerEvent (channel, 0x65, 10) };
 - 
 -             for (const auto& m : messages)
 -                 sequence.addEvent (m, time);
 - 
 -             const auto finalTime = 1.0;
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, finalTime, m);
 - 
 -             const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 3),
 -                                                 MidiMessage::controllerEvent (channel, 0x62, 4),
 -                                                 MidiMessage::controllerEvent (channel, 0x06, 10),
 -                                                 // Parameter number is set to (30, 40) then back to (3, 4),
 -                                                 // so there is no need to resend it
 -                                                 MidiMessage::controllerEvent (channel, 0x60, 5),
 -                                                 // Set parameter number to final value
 -                                                 MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) };
 - 
 -             expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
 -         }
 - 
 -         beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change");
 -         {
 -             MidiMessageSequence sequence;
 - 
 -             const auto time = 0.0;
 -             const auto channel = 1;
 - 
 -             const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x00, 1),
 -                                                 MidiMessage::controllerEvent (channel, 0x20, 2),
 -                                                 MidiMessage::controllerEvent (channel, 0x65, 0),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 0),
 -                                                 MidiMessage::programChange (channel, 5) };
 - 
 -             for (const auto& m : messages)
 -                 sequence.addEvent (m, time);
 - 
 -             const auto finalTime = 1.0;
 - 
 -             Array<MidiMessage> m;
 -             sequence.createControllerUpdatesForTime (channel, finalTime, m);
 - 
 -             const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x00, 1),
 -                                                 MidiMessage::controllerEvent (channel, 0x20, 2),
 -                                                 MidiMessage::programChange (channel, 5),
 -                                                 MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
 -                                                 MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) };
 - 
 - 
 -             expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
 -         }
 -     }
 - };
 - 
 - static MidiMessageSequenceTest midiMessageSequenceTests;
 - 
 - #endif
 - 
 - } // namespace juce
 
 
  |