26 namespace MidiFileHelpers
28 static void writeVariableLengthInt (OutputStream& out, uint32 v)
30 auto buffer = v & 0x7f;
32 while ((v >>= 7) != 0)
35 buffer |= ((v & 0x7f) | 0x80);
40 out.writeByte ((
char) buffer);
49 static bool parseMidiHeader (
const uint8* &data,
short& timeFormat,
short& fileType,
short& numberOfTracks) noexcept
60 for (
int i = 0; i < 8; ++i)
86 data += bytesRemaining;
91 static double convertTicksToSeconds (
double time,
92 const MidiMessageSequence& tempoEvents,
96 return time / (-(timeFormat >> 8) * (timeFormat & 0xff));
98 double lastTime = 0, correctedTime = 0;
99 auto tickLen = 1.0 / (timeFormat & 0x7fff);
100 auto secsPerTick = 0.5 * tickLen;
101 auto numEvents = tempoEvents.getNumEvents();
103 for (
int i = 0; i < numEvents; ++i)
105 auto& m = tempoEvents.getEventPointer(i)->message;
106 auto eventTime = m.getTimeStamp();
108 if (eventTime >= time)
111 correctedTime += (eventTime - lastTime) * secsPerTick;
112 lastTime = eventTime;
114 if (m.isTempoMetaEvent())
115 secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote();
117 while (i + 1 < numEvents)
119 auto& m2 = tempoEvents.getEventPointer(i + 1)->message;
121 if (m2.getTimeStamp() != eventTime)
124 if (m2.isTempoMetaEvent())
125 secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote();
131 return correctedTime + (time - lastTime) * secsPerTick;
134 template <
typename MethodType>
135 static void findAllMatchingEvents (
const OwnedArray<MidiMessageSequence>& tracks,
136 MidiMessageSequence& results,
139 for (
auto* track : tracks)
141 auto numEvents = track->getNumEvents();
143 for (
int j = 0; j < numEvents; ++j)
145 auto& m = track->getEventPointer(j)->message;
148 results.addEvent (m);
160 tracks.addCopiesOf (other.tracks);
166 tracks.addCopiesOf (other.tracks);
167 timeFormat = other.timeFormat;
172 : tracks (
std::move (other.tracks)),
173 timeFormat (other.timeFormat)
179 tracks = std::move (other.tracks);
180 timeFormat = other.timeFormat;
192 return tracks.size();
197 return tracks[index];
213 timeFormat = (short) ticks;
218 timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution);
241 for (
auto* ms : tracks)
242 t = jmax (t, ms->getEndTime());
253 const int maxSensibleMidiFileSize = 200 * 1024 * 1024;
259 auto d =
static_cast<const uint8*
> (data.
getData());
260 short fileType, expectedTracks;
262 if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks))
264 size -= (size_t) (d - static_cast<const uint8*> (data.
getData()));
274 if (chunkSize <= 0 || (
size_t) chunkSize > size)
278 readNextTrack (d, chunkSize, createMatchingNoteOffs);
280 if (++track >= expectedTracks)
283 size -= (size_t) chunkSize + 8;
294 void MidiFile::readNextTrack (
const uint8* data,
int size,
bool createMatchingNoteOffs)
297 uint8 lastStatusByte = 0;
310 const MidiMessage mm (data, size, messSize, lastStatusByte, time);
322 if ((firstByte & 0xf0) != 0xf0)
323 lastStatusByte = firstByte;
327 std::stable_sort (result.list.begin(), result.list.end(),
332 auto t2 = b->message.getTimeStamp();
334 if (t1 < t2)
return true;
335 if (t2 < t1)
return false;
342 if (createMatchingNoteOffs)
343 tracks.getLast()->updateMatchedPairs();
355 for (
auto* ms : tracks)
357 for (
int j = ms->getNumEvents(); --j >= 0;)
359 auto& m = ms->getEventPointer(j)->message;
360 m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat));
369 jassert (midiFileType >= 0 && midiFileType <= 2);
377 for (
auto* ms : tracks)
378 if (! writeTrack (out, *ms))
390 uint8 lastStatusByte = 0;
391 bool endOfTrackEventWritten =
false;
397 if (mm.isEndOfTrackMetaEvent())
398 endOfTrackEventWritten =
true;
400 auto tick = roundToInt (mm.getTimeStamp());
401 auto delta = jmax (0, tick - lastTick);
402 MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta);
405 auto* data = mm.getRawData();
406 auto dataSize = mm.getRawDataSize();
407 auto statusByte = data[0];
409 if (statusByte == lastStatusByte
410 && (statusByte & 0xf0) != 0xf0
417 else if (statusByte == 0xf0)
424 MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize);
427 out.
write (data, (
size_t) dataSize);
428 lastStatusByte = statusByte;
431 if (! endOfTrackEventWritten)
435 out.
write (m.getRawData(), (size_t) m.getRawDataSize());
size_t getSize() const noexcept
Returns the block's current allocated size, in bytes.
virtual bool writeShortBigEndian(short value)
Writes a 16-bit integer to the stream in a big-endian byte order.
void findAllTempoEvents(MidiMessageSequence &tempoChangeEvents) const
Makes a list of all the tempo-change meta-events from all tracks in the midi file.
Reads/writes standard midi format files.
Encapsulates a MIDI message.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
bool readFrom(InputStream &sourceStream, bool createMatchingNoteOffs=true)
Reads a midi file format stream.
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
Inserts a midi message into the sequence.
void clear()
Removes all midi tracks from the file.
static JUCE_CONSTEXPR uint32 bigEndianInt(const void *bytes) noexcept
Turns 4 bytes into a big-endian integer.
int getNumTracks() const noexcept
Returns the number of tracks in the file.
MidiFile()
Creates an empty MidiFile object.
MidiEventHolder * getEventPointer(int index) const noexcept
Returns a pointer to one of the events.
bool isTimeSignatureMetaEvent() const noexcept
Returns true if this is a 'time-signature' meta-event.
static MidiMessage endOfTrack() noexcept
Creates an end-of-track meta-event.
virtual void flush()=0
If the stream is using a buffer, this will ensure it gets written out to the destination.
double getLastTimestamp() const
Returns the latest timestamp in any of the tracks.
void addTrack(const MidiMessageSequence &trackSequence)
Adds a midi track to the file.
MidiFile & operator=(const MidiFile &)
Copies from another MidiFile object.
bool isTempoMetaEvent() const noexcept
Returns true if this is a 'tempo' meta-event.
void * getData() noexcept
Returns a void pointer to the data.
virtual bool writeIntBigEndian(int value)
Writes a 32-bit integer to the stream in a big-endian byte order.
bool writeTo(OutputStream &destStream, int midiFileType=1) const
Writes the midi tracks as a standard midi file.
void setSmpteTimeFormat(int framesPerSecond, int subframeResolution) noexcept
Sets the time format to use when this file is written to a stream.
bool write(const void *, size_t) override
Writes a block of data to the stream.
MidiMessage message
The message itself, whose timestamp is used to specify the event's time.
void setTicksPerQuarterNote(int ticksPerQuarterNote) noexcept
Sets the time format to use when this file is written to a stream.
The base class for streams that write data to some kind of destination.
static JUCE_CONSTEXPR uint16 bigEndianShort(const void *bytes) noexcept
Turns 2 bytes into a big-endian integer.
bool isNoteOff(bool returnTrueForNoteOnVelocity0=true) const noexcept
Returns true if this message is a 'key-up' event.
void convertTimestampTicksToSeconds()
Converts the timestamp of all the midi events from midi ticks to seconds.
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
static int readVariableLengthVal(const uint8 *data, int &numBytesUsed) noexcept
Reads a midi variable-length integer.
A sequence of timestamped midi messages.
Structure used to hold midi events in the sequence.
const uint8 * getRawData() const noexcept
Returns a pointer to the raw midi data.
double getTimeStamp() const noexcept
Returns the timestamp associated with this message.
bool isKeySignatureMetaEvent() const noexcept
Returns true if this is a 'key-signature' meta-event.
short getTimeFormat() const noexcept
Returns the raw time format code that will be written to a stream.
void findAllTimeSigEvents(MidiMessageSequence &timeSigEvents) const
Makes a list of all the time-signature meta-events from all tracks in the midi file.
Writes data to an internal memory buffer, which grows as required.
void findAllKeySigEvents(MidiMessageSequence &keySigEvents) const
Makes a list of all the time-signature meta-events from all tracks in the midi file.
A class to hold a resizable block of raw data.
int getNumEvents() const noexcept
Returns the number of events in the sequence.
const MidiMessageSequence * getTrack(int index) const noexcept
Returns a pointer to one of the tracks in the file.