26namespace 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];
241 for (
auto*
ms : tracks)
242 t = jmax (
t,
ms->getEndTime());
259 auto d =
static_cast<const uint8*
> (data.
getData());
262 if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType,
expectedTracks))
264 size -= (
size_t) (d -
static_cast<const uint8*
> (data.
getData()));
281 size -= (
size_t) chunkSize + 8;
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));
371 if (!
out.writeIntBigEndian (6))
return false;
373 if (!
out.writeShortBigEndian ((
short) tracks.size()))
return false;
374 if (!
out.writeShortBigEndian (timeFormat))
return false;
376 for (
auto*
ms : tracks)
377 if (! writeTrack (
out, *
ms))
392 for (
int i = 0; i <
ms.getNumEvents(); ++i)
394 auto&
mm =
ms.getEventPointer(i)->message;
396 if (
mm.isEndOfTrackMetaEvent())
399 auto tick = roundToInt (
mm.getTimeStamp());
401 MidiFileHelpers::writeVariableLengthInt (
out, (uint32)
delta);
404 auto* data =
mm.getRawData();
405 auto dataSize =
mm.getRawDataSize();
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());
Holds a resizable array of primitive or copy-by-value objects.
int size() const noexcept
Returns the current number of elements in the array.
ElementType * begin() const noexcept
Returns a pointer to the first element in the array.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
void clear()
Removes all elements from the array.
ElementType * end() const noexcept
Returns a pointer to the element which follows the last element in the array.
static JUCE_CONSTEXPR uint16 bigEndianShort(const void *bytes) noexcept
Turns 2 bytes into a big-endian integer.
static JUCE_CONSTEXPR uint32 bigEndianInt(const void *bytes) noexcept
Turns 4 bytes into a big-endian integer.
A class to hold a resizable block of raw data.
size_t getSize() const noexcept
Returns the block's current allocated size, in bytes.
void * getData() const noexcept
Returns a void pointer to the data.
Writes data to an internal memory buffer, which grows as required.
size_t getDataSize() const noexcept
Returns the number of bytes of data that have been written to the stream.
bool write(const void *, size_t) override
Writes a block of data to the stream.
Reads/writes standard midi format files.
void convertTimestampTicksToSeconds()
Converts the timestamp of all the midi events from midi ticks to seconds.
void addTrack(const MidiMessageSequence &trackSequence)
Adds a midi track to the file.
void setTicksPerQuarterNote(int ticksPerQuarterNote) noexcept
Sets the time format to use when this file is written to a stream.
int getNumTracks() const noexcept
Returns the number of tracks in the file.
short getTimeFormat() const noexcept
Returns the raw time format code that will be written to a stream.
void setSmpteTimeFormat(int framesPerSecond, int subframeResolution) noexcept
Sets the time format to use when this file is written to a stream.
double getLastTimestamp() const
Returns the latest timestamp in any of the tracks.
bool readFrom(InputStream &sourceStream, bool createMatchingNoteOffs=true)
Reads a midi file format stream.
MidiFile & operator=(const MidiFile &)
Copies from another MidiFile object.
MidiFile()
Creates an empty MidiFile object.
void findAllTimeSigEvents(MidiMessageSequence &timeSigEvents) const
Makes a list of all the time-signature meta-events from all tracks in the midi file.
void findAllKeySigEvents(MidiMessageSequence &keySigEvents) const
Makes a list of all the time-signature meta-events from all tracks in the midi file.
void findAllTempoEvents(MidiMessageSequence &tempoChangeEvents) const
Makes a list of all the tempo-change meta-events from all tracks in the midi file.
void clear()
Removes all midi tracks from the file.
const MidiMessageSequence * getTrack(int index) const noexcept
Returns a pointer to one of the tracks in the file.
bool writeTo(OutputStream &destStream, int midiFileType=1) const
Writes the midi tracks as a standard midi file.
A sequence of timestamped midi messages.
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
Inserts a midi message into the sequence.
Encapsulates a MIDI message.
static int readVariableLengthVal(const uint8 *data, int &numBytesUsed) noexcept
Reads a midi variable-length integer.
bool isKeySignatureMetaEvent() const noexcept
Returns true if this is a 'key-signature' meta-event.
bool isTimeSignatureMetaEvent() const noexcept
Returns true if this is a 'time-signature' meta-event.
bool isTempoMetaEvent() const noexcept
Returns true if this is a 'tempo' meta-event.
static MidiMessage endOfTrack() noexcept
Creates an end-of-track meta-event.
The base class for streams that write data to some kind of destination.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
virtual bool writeIntBigEndian(int value)
Writes a 32-bit integer to the stream in a big-endian byte order.