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);
319 auto firstByte = *(mm.getRawData());
321 if ((firstByte & 0xf0) != 0xf0)
322 lastStatusByte = firstByte;
326 std::stable_sort (result.list.begin(), result.list.end(),
327 [] (
const MidiMessageSequence::MidiEventHolder* a,
328 const MidiMessageSequence::MidiEventHolder* b)
330 auto t1 = a->message.getTimeStamp();
331 auto t2 = b->message.getTimeStamp();
333 if (t1 < t2) return true;
334 if (t2 < t1) return false;
336 return a->message.isNoteOff() && b->message.isNoteOn();
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());