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()));
268 while (size > 0 && track < expectedTracks)
279 readNextTrack (d, chunkSize, createMatchingNoteOffs);
281 size -= (size_t) chunkSize + 8;
293 void MidiFile::readNextTrack (
const uint8* data,
int size,
bool createMatchingNoteOffs)
296 uint8 lastStatusByte = 0;
309 const MidiMessage mm (data, size, messSize, lastStatusByte, time);
321 if ((firstByte & 0xf0) != 0xf0)
322 lastStatusByte = firstByte;
326 std::stable_sort (result.list.begin(), result.list.end(),
331 auto t2 = b->message.getTimeStamp();
333 if (t1 < t2)
return true;
334 if (t2 < t1)
return false;
341 if (createMatchingNoteOffs)
342 tracks.getLast()->updateMatchedPairs();
354 for (
auto* ms : tracks)
356 for (
int j = ms->getNumEvents(); --j >= 0;)
358 auto& m = ms->getEventPointer(j)->message;
359 m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat));
368 jassert (midiFileType >= 0 && midiFileType <= 2);
376 for (
auto* ms : tracks)
377 if (! writeTrack (out, *ms))
389 uint8 lastStatusByte = 0;
390 bool endOfTrackEventWritten =
false;
396 if (mm.isEndOfTrackMetaEvent())
397 endOfTrackEventWritten =
true;
399 auto tick = roundToInt (mm.getTimeStamp());
400 auto delta = jmax (0, tick - lastTick);
401 MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta);
404 auto* data = mm.getRawData();
405 auto dataSize = mm.getRawDataSize();
406 auto statusByte = data[0];
408 if (statusByte == lastStatusByte
409 && (statusByte & 0xf0) != 0xf0
416 else if (statusByte == 0xf0)
423 MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize);
426 out.
write (data, (
size_t) dataSize);
427 lastStatusByte = statusByte;
430 if (! endOfTrackEventWritten)
434 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.
void * getData() const noexcept
Returns a void pointer to the data.
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.
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.