26 MidiMessageSequence::MidiEventHolder::MidiEventHolder (
const MidiMessage& mm) : message (mm) {}
27 MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (
std::move (mm)) {}
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);
72 list.swapWith (other.list);
97 if (
auto* meh = list[index])
98 if (
auto* noteOff = meh->noteOffObject)
99 return noteOff->message.getTimeStamp();
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);
128 auto numEvents = list.size();
131 for (i = 0; i < numEvents; ++i)
132 if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
141 return getEventTime (0);
146 return getEventTime (list.size() - 1);
151 if (
auto* meh = list[index])
152 return meh->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);
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);
227 std::stable_sort (list.begin(), list.end(),
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;
261 list.insert (j, newEvent);
263 meh->noteOffObject = newEvent;
276 m->message.addToTimeStamp (delta);
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);
299 for (
int i = list.size(); --i >= 0;)
300 if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
306 for (
int i = list.size(); --i >= 0;)
307 if (list.getUnchecked(i)->message.isSysEx())
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;
354 struct MidiMessageSequenceTest :
public UnitTest 356 MidiMessageSequenceTest()
357 :
UnitTest (
"MidiMessageSequence", UnitTestCategories::midi)
360 void runTest()
override 369 beginTest (
"Start & end time");
374 beginTest (
"Matching note off & ons");
381 beginTest (
"Time & indices");
386 beginTest (
"Deleting events");
390 beginTest (
"Merging sequences");
408 static MidiMessageSequenceTest midiMessageSequenceTests;
void addTimeToMessages(double deltaTime) noexcept
Adds an offset to the timestamps of all events in the sequence.
void extractSysExMessages(MidiMessageSequence &destSequence) const
Copies all midi sys-ex messages to another sequence.
void updateMatchedPairs() noexcept
Makes sure all the note-on and note-off pairs are up-to-date.
void addSequence(const MidiMessageSequence &other, double timeAdjustmentDelta, double firstAllowableDestTime, double endOfAllowableDestTimes)
Merges another sequence into this one.
void setTimeStamp(double newTimestamp) noexcept
Changes the message's associated timestamp.
void deleteSysExMessages()
Removes any sys-ex messages from this sequence.
Encapsulates a MIDI message.
void sort() noexcept
Forces a sort of the sequence.
MidiEventHolder ** end() noexcept
Iterator for the list of MidiEventHolders.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
int getIndexOfMatchingKeyUp(int index) const noexcept
Returns the index of the note-up that matches the note-on at this index.
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
Inserts a midi message into the sequence.
void deleteEvent(int index, bool deleteMatchingNoteUp)
Deletes one of the events in the sequence.
~MidiMessageSequence()
Destructor.
void swapWith(MidiMessageSequence &) noexcept
Swaps this sequence with another one.
MidiEventHolder ** begin() noexcept
Iterator for the list of MidiEventHolders.
void deleteMidiChannelMessages(int channelNumberToRemove)
Removes any messages in this sequence that have a specific midi channel.
MidiEventHolder * getEventPointer(int index) const noexcept
Returns a pointer to one of the events.
double getStartTime() const noexcept
Returns the timestamp of the first event in the sequence.
This is a base class for classes that perform a unit test.
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
Creates a key-up message.
double getTimeOfMatchingKeyUp(int index) const noexcept
Returns the time of the note-up that matches the note-on at this index.
void addToTimeStamp(double delta) noexcept
Adds a value to the message's timestamp.
MidiMessageSequence & operator=(const MidiMessageSequence &)
Replaces this sequence with another one.
void createControllerUpdatesForTime(int channelNumber, double time, Array< MidiMessage > &resultMessages)
Scans through the sequence to determine the state of any midi controllers at a given time...
MidiMessage message
The message itself, whose timestamp is used to specify the event's time.
Holds a resizable array of primitive or copy-by-value objects.
~MidiEventHolder()
Destructor.
int getIndexOf(const MidiEventHolder *event) const noexcept
Returns the index of an event.
A sequence of timestamped midi messages.
Structure used to hold midi events in the sequence.
void extractMidiChannelMessages(int channelNumberToExtract, MidiMessageSequence &destSequence, bool alsoIncludeMetaEvents) const
Copies all the messages for a particular midi channel to another sequence.
double getTimeStamp() const noexcept
Returns the timestamp associated with this message.
int getNextIndexAtTime(double timeStamp) const noexcept
Returns the index of the first event on or after the given timestamp.
double getEndTime() const noexcept
Returns the timestamp of the last event in the sequence.
void clear()
Clears the sequence.
MidiMessageSequence()
Creates an empty midi sequence object.
int getNumEvents() const noexcept
Returns the number of events in the sequence.
double getEventTime(int index) const noexcept
Returns the timestamp of the event at a given index.
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
Creates a key-down message (using a floating-point velocity).