| @@ -3,7 +3,7 @@ | |||
| This file is part of the Water library. | |||
| Copyright (c) 2016 ROLI Ltd. | |||
| Copyright (C) 2017 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2017-2022 Filipe Coelho <falktx@falktx.com> | |||
| Permission is granted to use this software under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
| @@ -32,27 +32,6 @@ namespace water { | |||
| namespace MidiFileHelpers | |||
| { | |||
| static void writeVariableLengthInt (OutputStream& out, unsigned int v) | |||
| { | |||
| unsigned int buffer = v & 0x7f; | |||
| while ((v >>= 7) != 0) | |||
| { | |||
| buffer <<= 8; | |||
| buffer |= ((v & 0x7f) | 0x80); | |||
| } | |||
| for (;;) | |||
| { | |||
| out.writeByte ((char) buffer); | |||
| if (buffer & 0x80) | |||
| buffer >>= 8; | |||
| else | |||
| break; | |||
| } | |||
| } | |||
| static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept | |||
| { | |||
| unsigned int ch = ByteOrder::bigEndianInt (data); | |||
| @@ -372,86 +351,4 @@ void MidiFile::convertTimestampTicksToSeconds() | |||
| } | |||
| } | |||
| //============================================================================== | |||
| bool MidiFile::writeTo (OutputStream& out, int midiFileType) | |||
| { | |||
| wassert (midiFileType >= 0 && midiFileType <= 2); | |||
| if (! out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"))) return false; | |||
| if (! out.writeIntBigEndian (6)) return false; | |||
| if (! out.writeShortBigEndian ((short) midiFileType)) return false; | |||
| if (! out.writeShortBigEndian ((short) tracks.size())) return false; | |||
| if (! out.writeShortBigEndian (timeFormat)) return false; | |||
| for (size_t i = 0; i < tracks.size(); ++i) | |||
| if (! writeTrack (out, i)) | |||
| return false; | |||
| out.flush(); | |||
| return true; | |||
| } | |||
| bool MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) | |||
| { | |||
| MemoryOutputStream out; | |||
| const MidiMessageSequence& ms = *tracks.getUnchecked (trackNum); | |||
| int lastTick = 0; | |||
| uint8 lastStatusByte = 0; | |||
| bool endOfTrackEventWritten = false; | |||
| for (int i = 0; i < ms.getNumEvents(); ++i) | |||
| { | |||
| const MidiMessage& mm = ms.getEventPointer(i)->message; | |||
| if (mm.isEndOfTrackMetaEvent()) | |||
| endOfTrackEventWritten = true; | |||
| const int tick = roundToInt (mm.getTimeStamp()); | |||
| const int delta = jmax (0, tick - lastTick); | |||
| MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); | |||
| lastTick = tick; | |||
| const uint8* data = mm.getRawData(); | |||
| int dataSize = mm.getRawDataSize(); | |||
| const uint8 statusByte = data[0]; | |||
| if (statusByte == lastStatusByte | |||
| && (statusByte & 0xf0) != 0xf0 | |||
| && dataSize > 1 | |||
| && i > 0) | |||
| { | |||
| ++data; | |||
| --dataSize; | |||
| } | |||
| else if (statusByte == 0xf0) // Write sysex message with length bytes. | |||
| { | |||
| out.writeByte ((char) statusByte); | |||
| ++data; | |||
| --dataSize; | |||
| MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); | |||
| } | |||
| out.write (data, (size_t) dataSize); | |||
| lastStatusByte = statusByte; | |||
| } | |||
| if (! endOfTrackEventWritten) | |||
| { | |||
| out.writeByte (0); // (tick delta) | |||
| const MidiMessage m (MidiMessage::endOfTrack()); | |||
| out.write (m.getRawData(), (size_t) m.getRawDataSize()); | |||
| } | |||
| if (! mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk"))) return false; | |||
| if (! mainOut.writeIntBigEndian ((int) out.getDataSize())) return false; | |||
| mainOut << out; | |||
| return true; | |||
| } | |||
| } | |||
| @@ -3,7 +3,7 @@ | |||
| This file is part of the Water library. | |||
| Copyright (c) 2016 ROLI Ltd. | |||
| Copyright (C) 2017 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2017-2022 Filipe Coelho <falktx@falktx.com> | |||
| Permission is granted to use this software under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
| @@ -32,15 +32,11 @@ namespace water { | |||
| //============================================================================== | |||
| /** | |||
| Reads/writes standard midi format files. | |||
| Reads standard midi format files. | |||
| To read a midi file, create a MidiFile object and call its readFrom() method. You | |||
| can then get the individual midi tracks from it using the getTrack() method. | |||
| To write a file, create a MidiFile object, add some MidiMessageSequence objects | |||
| to it using the addTrack() method, and then call its writeTo() method to stream | |||
| it out. | |||
| @see MidiMessageSequence | |||
| */ | |||
| class MidiFile | |||
| @@ -160,13 +156,6 @@ public: | |||
| */ | |||
| bool readFrom (InputStream& sourceStream); | |||
| /** Writes the midi tracks as a standard midi file. | |||
| The midiFileType value is written as the file's format type, which can be 0, 1 | |||
| or 2 - see the midi file spec for more info about that. | |||
| @returns true if the operation succeeded. | |||
| */ | |||
| bool writeTo (OutputStream& destStream, int midiFileType = 1); | |||
| /** Converts the timestamp of all the midi events from midi ticks to seconds. | |||
| This will use the midi time format and tempo/time signature info in the | |||
| @@ -181,7 +170,6 @@ private: | |||
| short timeFormat; | |||
| void readNextTrack (const uint8*, int size); | |||
| bool writeTrack (OutputStream&, int trackNum); | |||
| }; | |||
| } | |||
| @@ -3,7 +3,7 @@ | |||
| This file is part of the Water library. | |||
| Copyright (c) 2016 ROLI Ltd. | |||
| Copyright (C) 2017 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2017-2022 Filipe Coelho <falktx@falktx.com> | |||
| Permission is granted to use this software under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
| @@ -657,54 +657,6 @@ const uint8* MidiMessage::getMetaEventData() const noexcept | |||
| return d + n; | |||
| } | |||
| bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; } | |||
| bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; } | |||
| bool MidiMessage::isTextMetaEvent() const noexcept | |||
| { | |||
| const int t = getMetaEventType(); | |||
| return t > 0 && t < 16; | |||
| } | |||
| String MidiMessage::getTextFromTextMetaEvent() const | |||
| { | |||
| const char* const textData = reinterpret_cast<const char*> (getMetaEventData()); | |||
| return String (CharPointer_UTF8 (textData), | |||
| CharPointer_UTF8 (textData + getMetaEventLength())); | |||
| } | |||
| MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) | |||
| { | |||
| wassert (type > 0 && type < 16); | |||
| MidiMessage result; | |||
| const size_t textSize = text.text.sizeInBytes() - 1; | |||
| uint8 header[8]; | |||
| size_t n = sizeof (header); | |||
| header[--n] = (uint8) (textSize & 0x7f); | |||
| for (size_t i = textSize; (i >>= 7) != 0;) | |||
| header[--n] = (uint8) ((i & 0x7f) | 0x80); | |||
| header[--n] = (uint8) type; | |||
| header[--n] = 0xff; | |||
| const size_t headerLen = sizeof (header) - n; | |||
| const int totalSize = (int) (headerLen + textSize); | |||
| uint8* const dest = result.allocateSpace (totalSize); | |||
| result.size = totalSize; | |||
| memcpy (dest, header + n, headerLen); | |||
| memcpy (dest + headerLen, text.text.getAddress(), textSize); | |||
| return result; | |||
| } | |||
| bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getData(); return (data[1] == 3) && (*data == 0xff); } | |||
| bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getData(); return (data[1] == 81) && (*data == 0xff); } | |||
| bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } | |||
| @@ -829,11 +781,6 @@ MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool | |||
| return MidiMessage (d, 5, 0.0); | |||
| } | |||
| MidiMessage MidiMessage::endOfTrack() noexcept | |||
| { | |||
| return MidiMessage (0xff, 0x2f, 0, 0.0); | |||
| } | |||
| //============================================================================== | |||
| bool MidiMessage::isSongPositionPointer() const noexcept { return *getData() == 0xf2; } | |||
| int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getData(); return data[1] | (data[2] << 7); } | |||
| @@ -963,25 +910,6 @@ MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int sec | |||
| } | |||
| //============================================================================== | |||
| String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC) | |||
| { | |||
| static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; | |||
| static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" }; | |||
| if (isPositiveAndBelow (note, (int) 128)) | |||
| { | |||
| String s (useSharps ? sharpNoteNames [note % 12] | |||
| : flatNoteNames [note % 12]); | |||
| if (includeOctaveNumber) | |||
| s << (note / 12 + (octaveNumForMiddleC - 5)); | |||
| return s; | |||
| } | |||
| return String(); | |||
| } | |||
| double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept | |||
| { | |||
| return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); | |||
| @@ -3,7 +3,7 @@ | |||
| This file is part of the Water library. | |||
| Copyright (c) 2016 ROLI Ltd. | |||
| Copyright (C) 2017 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2017-2022 Filipe Coelho <falktx@falktx.com> | |||
| Permission is granted to use this software under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
| @@ -505,8 +505,7 @@ public: | |||
| Meta-events are things like tempo changes, track names, etc. | |||
| @see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent, | |||
| isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, | |||
| @see getMetaEventType, isTempoMetaEvent, isTimeSignatureMetaEvent, | |||
| isKeySignatureMetaEvent, isMidiChannelMetaEvent | |||
| */ | |||
| bool isMetaEvent() const noexcept; | |||
| @@ -515,8 +514,7 @@ public: | |||
| If the message isn't a meta-event, this will return -1. | |||
| @see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent, | |||
| isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, | |||
| @see isMetaEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, | |||
| isKeySignatureMetaEvent, isMidiChannelMetaEvent | |||
| */ | |||
| int getMetaEventType() const noexcept; | |||
| @@ -531,36 +529,6 @@ public: | |||
| */ | |||
| int getMetaEventLength() const noexcept; | |||
| //============================================================================== | |||
| /** Returns true if this is a 'track' meta-event. */ | |||
| bool isTrackMetaEvent() const noexcept; | |||
| /** Returns true if this is an 'end-of-track' meta-event. */ | |||
| bool isEndOfTrackMetaEvent() const noexcept; | |||
| /** Creates an end-of-track meta-event. | |||
| @see isEndOfTrackMetaEvent | |||
| */ | |||
| static MidiMessage endOfTrack() noexcept; | |||
| /** Returns true if this is an 'track name' meta-event. | |||
| You can use the getTextFromTextMetaEvent() method to get the track's name. | |||
| */ | |||
| bool isTrackNameEvent() const noexcept; | |||
| /** Returns true if this is a 'text' meta-event. | |||
| @see getTextFromTextMetaEvent | |||
| */ | |||
| bool isTextMetaEvent() const noexcept; | |||
| /** Returns the text from a text meta-event. | |||
| @see isTextMetaEvent | |||
| */ | |||
| String getTextFromTextMetaEvent() const; | |||
| /** Creates a text meta-event. */ | |||
| static MidiMessage textMetaEvent (int type, StringRef text); | |||
| //============================================================================== | |||
| /** Returns true if this is a 'tempo' meta-event. | |||
| @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote | |||
| @@ -847,25 +815,6 @@ public: | |||
| static int getMessageLengthFromFirstByte (uint8 firstByte) noexcept; | |||
| //============================================================================== | |||
| /** Returns the name of a midi note number. | |||
| E.g "C", "D#", etc. | |||
| @param noteNumber the midi note number, 0 to 127 | |||
| @param useSharps if true, sharpened notes are used, e.g. "C#", otherwise | |||
| they'll be flattened, e.g. "Db" | |||
| @param includeOctaveNumber if true, the octave number will be appended to the string, | |||
| e.g. "C#4" | |||
| @param octaveNumForMiddleC if an octave number is being appended, this indicates the | |||
| number that will be used for middle C's octave | |||
| @see getMidiNoteInHertz | |||
| */ | |||
| static String getMidiNoteName (int noteNumber, | |||
| bool useSharps, | |||
| bool includeOctaveNumber, | |||
| int octaveNumForMiddleC); | |||
| /** Returns the frequency of a midi note number. | |||
| The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch. | |||
| @@ -3,7 +3,7 @@ | |||
| This file is part of the Water library. | |||
| Copyright (c) 2016 ROLI Ltd. | |||
| Copyright (C) 2017-2019 Filipe Coelho <falktx@falktx.com> | |||
| Copyright (C) 2017-2022 Filipe Coelho <falktx@falktx.com> | |||
| Permission is granted to use this software under the terms of the ISC license | |||
| http://www.isc.org/downloads/software-support-policy/isc-license/ | |||
| @@ -68,40 +68,6 @@ MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (cons | |||
| return list [index]; | |||
| } | |||
| double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept | |||
| { | |||
| if (const MidiEventHolder* const meh = list [index]) | |||
| if (meh->noteOffObject != nullptr) | |||
| return meh->noteOffObject->message.getTimeStamp(); | |||
| return 0.0; | |||
| } | |||
| int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept | |||
| { | |||
| if (const MidiEventHolder* const meh = list [index]) | |||
| return list.indexOf (meh->noteOffObject); | |||
| return -1; | |||
| } | |||
| int MidiMessageSequence::getIndexOf (const MidiEventHolder* const event) const noexcept | |||
| { | |||
| return list.indexOf (event); | |||
| } | |||
| int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept | |||
| { | |||
| const int 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 | |||
| { | |||
| @@ -139,18 +105,6 @@ MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiM | |||
| return newOne; | |||
| } | |||
| void MidiMessageSequence::deleteEvent (const int index, | |||
| const bool deleteMatchingNoteUp) | |||
| { | |||
| if (isPositiveAndBelow (index, static_cast<int>(list.size()))) | |||
| { | |||
| if (deleteMatchingNoteUp) | |||
| deleteEvent (getIndexOfMatchingKeyUp (index), false); | |||
| list.remove (index); | |||
| } | |||
| } | |||
| struct MidiMessageSequenceSorter | |||
| { | |||
| static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, | |||
| @@ -175,28 +129,6 @@ void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double | |||
| sort(); | |||
| } | |||
| void MidiMessageSequence::addSequence (const MidiMessageSequence& other, | |||
| double timeAdjustment, | |||
| double firstAllowableTime, | |||
| double endOfAllowableDestTimes) | |||
| { | |||
| for (int i = 0; i < static_cast<int>(other.list.size()); ++i) | |||
| { | |||
| const MidiMessage& m = other.list.getUnchecked(i)->message; | |||
| const double t = m.getTimeStamp() + timeAdjustment; | |||
| if (t >= firstAllowableTime && t < endOfAllowableDestTimes) | |||
| { | |||
| MidiEventHolder* const newOne = new MidiEventHolder (m); | |||
| newOne->message.setTimeStamp (t); | |||
| list.add (newOne); | |||
| } | |||
| } | |||
| sort(); | |||
| } | |||
| //============================================================================== | |||
| void MidiMessageSequence::sort() noexcept | |||
| { | |||
| @@ -243,93 +175,6 @@ void MidiMessageSequence::updateMatchedPairs() noexcept | |||
| } | |||
| } | |||
| void MidiMessageSequence::addTimeToMessages (const double delta) noexcept | |||
| { | |||
| for (int i = static_cast<int>(list.size()); --i >= 0;) | |||
| { | |||
| MidiMessage& mm = list.getUnchecked(i)->message; | |||
| mm.setTimeStamp (mm.getTimeStamp() + delta); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, | |||
| MidiMessageSequence& destSequence, | |||
| const bool alsoIncludeMetaEvents) const | |||
| { | |||
| for (int i = 0; i < static_cast<int>(list.size()); ++i) | |||
| { | |||
| const MidiMessage& mm = list.getUnchecked(i)->message; | |||
| if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent())) | |||
| destSequence.addEvent (mm); | |||
| } | |||
| } | |||
| void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const | |||
| { | |||
| for (int i = 0; i < static_cast<int>(list.size()); ++i) | |||
| { | |||
| const MidiMessage& mm = list.getUnchecked(i)->message; | |||
| if (mm.isSysEx()) | |||
| destSequence.addEvent (mm); | |||
| } | |||
| } | |||
| 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); | |||
| } | |||
| //============================================================================== | |||
| void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber, const double time, Array<MidiMessage>& dest) | |||
| { | |||
| bool doneProg = false; | |||
| bool donePitchWheel = false; | |||
| bool doneControllers[128] = { 0 }; | |||
| for (int i = list.size(); --i >= 0;) | |||
| { | |||
| const MidiMessage& mm = list.getUnchecked(i)->message; | |||
| if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) | |||
| { | |||
| if (mm.isProgramChange() && ! doneProg) | |||
| { | |||
| doneProg = true; | |||
| dest.add (MidiMessage (mm, 0.0)); | |||
| } | |||
| else if (mm.isPitchWheel() && ! donePitchWheel) | |||
| { | |||
| donePitchWheel = true; | |||
| dest.add (MidiMessage (mm, 0.0)); | |||
| } | |||
| else if (mm.isController()) | |||
| { | |||
| const int controllerNumber = mm.getControllerNumber(); | |||
| wassert (isPositiveAndBelow (controllerNumber, 128)); | |||
| if (! doneControllers[controllerNumber]) | |||
| { | |||
| doneControllers[controllerNumber] = true; | |||
| dest.add (MidiMessage (mm, 0.0)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) | |||
| : message (mm), noteOffObject (nullptr) | |||
| @@ -103,27 +103,6 @@ public: | |||
| /** Returns a pointer to one of the events. */ | |||
| MidiEventHolder* getEventPointer (int index) const noexcept; | |||
| /** Returns the time of the note-up that matches the note-on at this index. | |||
| If the event at this index isn't a note-on, it'll just return 0. | |||
| @see MidiMessageSequence::MidiEventHolder::noteOffObject | |||
| */ | |||
| double getTimeOfMatchingKeyUp (int index) const noexcept; | |||
| /** Returns the index of the note-up that matches the note-on at this index. | |||
| If the event at this index isn't a note-on, it'll just return -1. | |||
| @see MidiMessageSequence::MidiEventHolder::noteOffObject | |||
| */ | |||
| int getIndexOfMatchingKeyUp (int index) const noexcept; | |||
| /** Returns the index of an event. */ | |||
| int getIndexOf (const MidiEventHolder* event) const noexcept; | |||
| /** Returns the index of the first event on or after the given timestamp. | |||
| If the time is beyond the end of the sequence, this will return the | |||
| number of events. | |||
| */ | |||
| int getNextIndexAtTime (double timeStamp) const noexcept; | |||
| //============================================================================== | |||
| /** Returns the timestamp of the first event in the sequence. | |||
| @see getEndTime | |||
| @@ -156,34 +135,6 @@ public: | |||
| MidiEventHolder* addEvent (const MidiMessage& newMessage, | |||
| double timeAdjustment = 0); | |||
| /** Deletes one of the events in the sequence. | |||
| Remember to call updateMatchedPairs() after removing events. | |||
| @param index the index of the event to delete | |||
| @param deleteMatchingNoteUp whether to also remove the matching note-off | |||
| if the event you're removing is a note-on | |||
| */ | |||
| void deleteEvent (int index, bool deleteMatchingNoteUp); | |||
| /** Merges another sequence into this one. | |||
| Remember to call updateMatchedPairs() after using this method. | |||
| @param other the sequence to add from | |||
| @param timeAdjustmentDelta an amount to add to the timestamps of the midi events | |||
| as they are read from the other sequence | |||
| @param firstAllowableDestTime events will not be added if their time is earlier | |||
| than this time. (This is after their time has been adjusted | |||
| by the timeAdjustmentDelta) | |||
| @param endOfAllowableDestTimes events will not be added if their time is equal to | |||
| or greater than this time. (This is after their time has | |||
| been adjusted by the timeAdjustmentDelta) | |||
| */ | |||
| void addSequence (const MidiMessageSequence& other, | |||
| double timeAdjustmentDelta, | |||
| double firstAllowableDestTime, | |||
| double endOfAllowableDestTimes); | |||
| /** Merges another sequence into this one. | |||
| Remember to call updateMatchedPairs() after using this method. | |||
| @@ -209,62 +160,6 @@ public: | |||
| */ | |||
| void sort() noexcept; | |||
| //============================================================================== | |||
| /** Copies all the messages for a particular midi channel to another sequence. | |||
| @param channelNumberToExtract the midi channel to look for, in the range 1 to 16 | |||
| @param destSequence the sequence that the chosen events should be copied to | |||
| @param alsoIncludeMetaEvents if true, any meta-events (which don't apply to a specific | |||
| channel) will also be copied across. | |||
| @see extractSysExMessages | |||
| */ | |||
| void extractMidiChannelMessages (int channelNumberToExtract, | |||
| MidiMessageSequence& destSequence, | |||
| bool alsoIncludeMetaEvents) const; | |||
| /** Copies all midi sys-ex messages to another sequence. | |||
| @param destSequence this is the sequence to which any sys-exes in this sequence | |||
| will be added | |||
| @see extractMidiChannelMessages | |||
| */ | |||
| void extractSysExMessages (MidiMessageSequence& destSequence) const; | |||
| /** Removes any messages in this sequence that have a specific midi channel. | |||
| @param channelNumberToRemove the midi channel to look for, in the range 1 to 16 | |||
| */ | |||
| void deleteMidiChannelMessages (int channelNumberToRemove); | |||
| /** Removes any sys-ex messages from this sequence. */ | |||
| void deleteSysExMessages(); | |||
| /** Adds an offset to the timestamps of all events in the sequence. | |||
| @param deltaTime the amount to add to each timestamp. | |||
| */ | |||
| void addTimeToMessages (double deltaTime) noexcept; | |||
| //============================================================================== | |||
| /** Scans through the sequence to determine the state of any midi controllers at | |||
| a given time. | |||
| This will create a sequence of midi controller changes that can be | |||
| used to set all midi controllers to the state they would be in at the | |||
| specified time within this sequence. | |||
| As well as controllers, it will also recreate the midi program number | |||
| and pitch bend position. | |||
| @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers | |||
| for other channels will be ignored. | |||
| @param time the time at which you want to find out the state - there are | |||
| no explicit units for this time measurement, it's the same units | |||
| as used for the timestamps of the messages | |||
| @param resultMessages an array to which midi controller-change messages will be added. This | |||
| will be the minimum number of controller changes to recreate the | |||
| state at the required time. | |||
| */ | |||
| void createControllerUpdatesForTime (int channelNumber, double time, | |||
| Array<MidiMessage>& resultMessages); | |||
| //============================================================================== | |||
| /** Swaps this sequence with another one. */ | |||
| void swapWith (MidiMessageSequence&) noexcept; | |||
| @@ -384,12 +384,12 @@ private: | |||
| for (size_t i=0; i<numTracks; ++i) | |||
| { | |||
| const MidiMessageSequence* const track(midiFile.getTrack(i)); | |||
| const MidiMessageSequence* const track = midiFile.getTrack(i); | |||
| CARLA_SAFE_ASSERT_CONTINUE(track != nullptr); | |||
| for (int j=0, numEvents = track->getNumEvents(); j<numEvents; ++j) | |||
| { | |||
| const MidiMessageSequence::MidiEventHolder* const midiEventHolder(track->getEventPointer(j)); | |||
| const MidiMessageSequence::MidiEventHolder* const midiEventHolder = track->getEventPointer(j); | |||
| CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr); | |||
| const MidiMessage& midiMessage(midiEventHolder->message); | |||