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
95double MidiMessageSequence::getTimeOfMatchingKeyUp (
int index)
const noexcept
97 if (
auto* meh = list[index])
98 if (
auto* noteOff = meh->noteOffObject)
104int MidiMessageSequence::getIndexOfMatchingKeyUp (
int index)
const noexcept
106 if (
auto* meh = list[index])
108 if (
auto* noteOff = meh->noteOffObject)
110 for (
int i = index; i < list.size(); ++i)
111 if (list.getUnchecked(i) == noteOff)
123 return list.indexOf (event);
126int MidiMessageSequence::getNextIndexAtTime (
double timeStamp)
const noexcept
128 auto numEvents = list.size();
131 for (i = 0; i < numEvents; ++i)
132 if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
139double MidiMessageSequence::getStartTime() const noexcept
141 return getEventTime (0);
144double MidiMessageSequence::getEndTime() const noexcept
146 return getEventTime (list.size() - 1);
149double MidiMessageSequence::getEventTime (
const int index)
const noexcept
151 if (
auto* meh = list[index])
152 return meh->message.getTimeStamp();
161 auto time = newEvent->message.getTimeStamp();
164 for (i = list.size(); --i >= 0;)
165 if (list.getUnchecked(i)->message.getTimeStamp() <= time)
168 list.insert (i + 1, newEvent);
179 return addEvent (
new MidiEventHolder (std::move (newMessage)), timeAdjustment);
182void MidiMessageSequence::deleteEvent (
int index,
bool deleteMatchingNoteUp)
184 if (isPositiveAndBelow (index, list.size()))
186 if (deleteMatchingNoteUp)
187 deleteEvent (getIndexOfMatchingKeyUp (index),
false);
195 for (
auto* m : other)
198 newOne->message.addToTimeStamp (timeAdjustment);
206 double timeAdjustment,
207 double firstAllowableTime,
208 double endOfAllowableDestTimes)
210 for (
auto* m : other)
212 auto t = m->message.getTimeStamp() + timeAdjustment;
214 if (t >= firstAllowableTime && t < endOfAllowableDestTimes)
217 newOne->message.setTimeStamp (t);
225void MidiMessageSequence::sort() noexcept
227 std::stable_sort (list.begin(), list.end(),
231void MidiMessageSequence::updateMatchedPairs() noexcept
233 for (
int i = 0; i < list.size(); ++i)
235 auto* meh = list.getUnchecked(i);
236 auto& m1 = meh->message;
240 meh->noteOffObject =
nullptr;
241 auto note = m1.getNoteNumber();
242 auto chan = m1.getChannel();
243 auto len = list.size();
245 for (
int j = i + 1; j < len; ++j)
247 auto* meh2 = list.getUnchecked(j);
248 auto& m = meh2->message;
250 if (m.getNoteNumber() == note && m.getChannel() == chan)
254 meh->noteOffObject = meh2;
260 auto newEvent =
new MidiEventHolder (MidiMessage::noteOff (chan, note));
261 list.insert (j, newEvent);
262 newEvent->message.setTimeStamp (m.getTimeStamp());
263 meh->noteOffObject = newEvent;
272void MidiMessageSequence::addTimeToMessages (
double delta)
noexcept
276 m->message.addToTimeStamp (delta);
280void MidiMessageSequence::extractMidiChannelMessages (
const int channelNumberToExtract,
282 const bool alsoIncludeMetaEvents)
const
284 for (
auto* meh : list)
285 if (meh->message.isForChannel (channelNumberToExtract)
286 || (alsoIncludeMetaEvents && meh->message.isMetaEvent()))
287 destSequence.
addEvent (meh->message);
292 for (
auto* meh : list)
293 if (meh->message.isSysEx())
294 destSequence.
addEvent (meh->message);
297void MidiMessageSequence::deleteMidiChannelMessages (
const int channelNumberToRemove)
299 for (
int i = list.size(); --i >= 0;)
300 if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
304void MidiMessageSequence::deleteSysExMessages()
306 for (
int i = list.size(); --i >= 0;)
307 if (list.getUnchecked(i)->message.isSysEx())
312void MidiMessageSequence::createControllerUpdatesForTime (
int channelNumber,
double time,
Array<MidiMessage>& dest)
314 bool doneProg =
false;
315 bool donePitchWheel =
false;
316 bool doneControllers[128] = {};
318 for (
int i = list.size(); --i >= 0;)
320 auto& mm = list.getUnchecked(i)->
message;
322 if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
324 if (mm.isProgramChange() && ! doneProg)
329 else if (mm.isPitchWheel() && ! donePitchWheel)
331 donePitchWheel =
true;
334 else if (mm.isController())
336 auto controllerNumber = mm.getControllerNumber();
337 jassert (isPositiveAndBelow (controllerNumber, 128));
339 if (! doneControllers[controllerNumber])
341 doneControllers[controllerNumber] =
true;
354struct MidiMessageSequenceTest :
public UnitTest
356 MidiMessageSequenceTest()
357 :
UnitTest (
"MidiMessageSequence", UnitTestCategories::midi)
362 MidiMessageSequence s;
364 s.addEvent (MidiMessage::noteOn (1, 60, 0.5f).withTimeStamp (0.0));
365 s.addEvent (MidiMessage::noteOff (1, 60, 0.5f).withTimeStamp (4.0));
366 s.addEvent (MidiMessage::noteOn (1, 30, 0.5f).withTimeStamp (2.0));
367 s.addEvent (MidiMessage::noteOff (1, 30, 0.5f).withTimeStamp (8.0));
375 s.updateMatchedPairs();
387 s.deleteEvent (0,
true);
391 MidiMessageSequence s2;
392 s2.addEvent (MidiMessage::noteOn (2, 25, 0.5f).withTimeStamp (0.0));
393 s2.addEvent (MidiMessage::noteOn (2, 40, 0.5f).withTimeStamp (1.0));
394 s2.addEvent (MidiMessage::noteOff (2, 40, 0.5f).withTimeStamp (5.0));
395 s2.addEvent (MidiMessage::noteOn (2, 80, 0.5f).withTimeStamp (3.0));
396 s2.addEvent (MidiMessage::noteOff (2, 80, 0.5f).withTimeStamp (7.0));
397 s2.addEvent (MidiMessage::noteOff (2, 25, 0.5f).withTimeStamp (9.0));
399 s.addSequence (s2, 0.0, 0.0, 8.0);
400 s.updateMatchedPairs();
408static 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.