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);
95 if (
auto* meh = list[index])
96 if (
auto* noteOff = meh->noteOffObject)
97 return noteOff->message.getTimeStamp();
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);
126 auto numEvents = list.size();
129 for (i = 0; i < numEvents; ++i)
130 if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp)
139 return getEventTime (0);
144 return getEventTime (list.size() - 1);
149 if (
auto* meh = list[index])
150 return meh->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);
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);
225 std::stable_sort (list.begin(), list.end(),
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;
259 list.insert (j, newEvent);
261 meh->noteOffObject = newEvent;
274 m->message.addToTimeStamp (delta);
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);
297 for (
int i = list.size(); --i >= 0;)
298 if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove))
304 for (
int i = list.size(); --i >= 0;)
305 if (list.getUnchecked(i)->message.isSysEx())
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") {}
353 void runTest()
override 362 beginTest (
"Start & end time");
367 beginTest (
"Matching note off & ons");
374 beginTest (
"Time & indeces");
379 beginTest (
"Deleting events");
383 beginTest (
"Merging sequences");
401 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.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
MidiEventHolder ** begin() const noexcept
Iterator for the list of MidiEventHolders.
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.
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.
MidiEventHolder ** end() const noexcept
Iterator for the list of MidiEventHolders.
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).