26MidiMessageSequence::MidiEventHolder::MidiEventHolder (
const MidiMessage& mm) : message (mm) {}
27MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
28MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {}
31MidiMessageSequence::MidiMessageSequence()
37 list.addCopiesOf (other.list);
39 for (
int i = 0; i < list.size(); ++i)
43 if (noteOffIndex >= 0)
44 list.getUnchecked(i)->noteOffObject = list.getUnchecked (noteOffIndex);
56 : list (std::move (other.list))
62 list = std::move (other.list);
66MidiMessageSequence::~MidiMessageSequence()
72 list.swapWith (other.list);
75void MidiMessageSequence::clear()
80int MidiMessageSequence::getNumEvents() const noexcept
93double MidiMessageSequence::getTimeOfMatchingKeyUp (
int index)
const noexcept
95 if (
auto* meh = list[index])
96 if (
auto* noteOff = meh->noteOffObject)
102int MidiMessageSequence::getIndexOfMatchingKeyUp (
int index)
const noexcept
104 if (
auto* meh = list[index])
106 if (
auto* noteOff = meh->noteOffObject)
108 for (
int i = index; i < list.size(); ++i)
109 if (list.getUnchecked(i) == noteOff)
121 return list.indexOf (event);
124int MidiMessageSequence::getNextIndexAtTime (
double timeStamp)
const noexcept
126 auto numEvents = list.size();
129 for (i = 0; i < numEvents; ++i)
130 if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
137double MidiMessageSequence::getStartTime() const noexcept
139 return getEventTime (0);
142double MidiMessageSequence::getEndTime() const noexcept
144 return getEventTime (list.size() - 1);
147double MidiMessageSequence::getEventTime (
const int index)
const noexcept
149 if (
auto* meh = list[index])
150 return meh->message.getTimeStamp();
159 auto time = newEvent->message.getTimeStamp();
162 for (i = list.size(); --i >= 0;)
163 if (list.getUnchecked(i)->message.getTimeStamp() <= time)
166 list.insert (i + 1, newEvent);
177 return addEvent (
new MidiEventHolder (std::move (newMessage)), timeAdjustment);
180void MidiMessageSequence::deleteEvent (
int index,
bool deleteMatchingNoteUp)
182 if (isPositiveAndBelow (index, list.size()))
184 if (deleteMatchingNoteUp)
185 deleteEvent (getIndexOfMatchingKeyUp (index),
false);
193 for (
auto* m : other)
196 newOne->message.addToTimeStamp (timeAdjustment);
204 double timeAdjustment,
205 double firstAllowableTime,
206 double endOfAllowableDestTimes)
208 for (
auto* m : other)
210 auto t = m->message.getTimeStamp() + timeAdjustment;
212 if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
215 newOne->message.setTimeStamp (t);
223void MidiMessageSequence::sort() noexcept
225 std::stable_sort (list.begin(), list.end(),
229void MidiMessageSequence::updateMatchedPairs() noexcept
231 for (
int i = 0; i < list.size(); ++i)
233 auto* meh = list.getUnchecked(i);
234 auto& m1 = meh->message;
238 meh->noteOffObject =
nullptr;
239 auto note = m1.getNoteNumber();
240 auto chan = m1.getChannel();
241 auto len = list.size();
243 for (
int j = i + 1; j < len; ++j)
245 auto* meh2 = list.getUnchecked(j);
246 auto& m = meh2->message;
248 if (m.getNoteNumber() == note && m.getChannel() == chan)
252 meh->noteOffObject = meh2;
258 auto newEvent =
new MidiEventHolder (MidiMessage::noteOff (chan, note));
259 list.insert (j, newEvent);
260 newEvent->message.setTimeStamp (m.getTimeStamp());
261 meh->noteOffObject = newEvent;
270void MidiMessageSequence::addTimeToMessages (
double delta)
noexcept
274 m->message.addToTimeStamp (delta);
278void MidiMessageSequence::extractMidiChannelMessages (
const int channelNumberToExtract,
280 const bool alsoIncludeMetaEvents)
const
282 for (
auto* meh : list)
283 if (meh->message.isForChannel (channelNumberToExtract)
284 || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
285 destSequence.
addEvent (meh->message);
290 for (
auto* meh : list)
291 if (meh->message.isSysEx())
292 destSequence.
addEvent (meh->message);
295void MidiMessageSequence::deleteMidiChannelMessages (
const int channelNumberToRemove)
297 for (
int i = list.size(); --i >= 0;)
298 if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
302void MidiMessageSequence::deleteSysExMessages()
304 for (
int i = list.size(); --i >= 0;)
305 if (list.getUnchecked(i)->message.isSysEx())
310void MidiMessageSequence::createControllerUpdatesForTime (
int channelNumber,
double time,
Array<MidiMessage>& dest)
312 bool doneProg =
false;
313 bool donePitchWheel =
false;
314 bool doneControllers[128] = {};
316 for (
int i = list.size(); --i >= 0;)
318 auto& mm = list.getUnchecked(i)->
message;
320 if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
322 if (mm.isProgramChange() && ! doneProg)
327 else if (mm.isPitchWheel() && ! donePitchWheel)
329 donePitchWheel =
true;
332 else if (mm.isController())
334 auto controllerNumber = mm.getControllerNumber();
335 jassert (isPositiveAndBelow (controllerNumber, 128));
337 if (! doneControllers[controllerNumber])
339 doneControllers[controllerNumber] =
true;
351 MidiMessageSequenceTest() : juce::
UnitTest (
"MidiMessageSequence") {}
355 MidiMessageSequence s;
357 s.addEvent (MidiMessage::noteOn (1, 60, 0.5f).withTimeStamp (0.0));
358 s.addEvent (MidiMessage::noteOff (1, 60, 0.5f).withTimeStamp (4.0));
359 s.addEvent (MidiMessage::noteOn (1, 30, 0.5f).withTimeStamp (2.0));
360 s.addEvent (MidiMessage::noteOff (1, 30, 0.5f).withTimeStamp (8.0));
368 s.updateMatchedPairs();
380 s.deleteEvent (0,
true);
384 MidiMessageSequence s2;
385 s2.addEvent (MidiMessage::noteOn (2, 25, 0.5f).withTimeStamp (0.0));
386 s2.addEvent (MidiMessage::noteOn (2, 40, 0.5f).withTimeStamp (1.0));
387 s2.addEvent (MidiMessage::noteOff (2, 40, 0.5f).withTimeStamp (5.0));
388 s2.addEvent (MidiMessage::noteOn (2, 80, 0.5f).withTimeStamp (3.0));
389 s2.addEvent (MidiMessage::noteOff (2, 80, 0.5f).withTimeStamp (7.0));
390 s2.addEvent (MidiMessage::noteOff (2, 25, 0.5f).withTimeStamp (9.0));
392 s.addSequence (s2, 0.0, 0.0, 8.0);
393 s.updateMatchedPairs();
401static MidiMessageSequenceTest midiMessageSequenceTests;
Holds a resizable array of primitive or copy-by-value objects.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Structure used to hold midi events in the sequence.
MidiMessage message
The message itself, whose timestamp is used to specify the event's time.
A sequence of timestamped midi messages.
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
Inserts a midi message into the sequence.
int getIndexOfMatchingKeyUp(int index) const noexcept
Returns the index of the note-up that matches the note-on at this index.
Encapsulates a MIDI message.
double getTimeStamp() const noexcept
Returns the timestamp associated with this message.
void addToTimeStamp(double delta) noexcept
Adds a value to the message's timestamp.
This is a base class for classes that perform a unit test.
void expectEquals(ValueType actual, ValueType expected, String failureMessage=String())
Compares a value to an expected value.
void beginTest(const String &testName)
Tells the system that a new subsection of tests is beginning.
virtual void runTest()=0
Implement this method in your subclass to actually run your tests.