Here's the smallest MIDI file that plays a note. Thirty-four bytes: middle C, one quarter note long, at 120 beats per minute.
4D 54 68 64 00 00 00 06 00 00 00 01 00 60
4D 54 72 6B 00 00 00 0C
00 90 3C 40
60 80 3C 40
00 FF 2F 00The first four bytes are ASCII for MThd, which is the file introducing itself with a header chunk. The next four are a 32-bit big-endian length, 6, and it's always 6, because the header holds three 16-bit fields and never grew. 00 00 is the format, 0. 00 01 means one track. 00 60 is 96, the number of ticks in a quarter note, and that number is the ruler the whole file gets measured with.
Then comes a track: MTrk, 12 bytes long. From here to the end the pattern never changes: a delay, then an event. 00 means wait zero ticks. 90 is a status byte: the high nibble 9 means Note On, the low nibble 0 means channel 1 (channels run 0–15 inside the file and 1–16 on screen). 3C is note 60, middle C. 40 is velocity 64, a medium press. Then 60: wait 96 ticks, one quarter note. 80 3C 40: Note Off, channel 1, the same note, release velocity 64. And finally 00 FF 2F 00: wait zero, meta event, type 2F, length 0. That's End of Track, which every track has to end with.
Notice that nothing in the file said 120 BPM. When there's no tempo event you get the default, 500,000 microseconds per quarter note, which works out to 120 BPM, and 96 ticks at that tempo is half a second. One note, half a second, 34 bytes. Every MIDI file you've ever opened is this, repeated. (The smallest valid MIDI file is actually 8 bytes shorter: a header and a track that holds nothing but End of Track. Twenty-six bytes of silence.)
Everything else in the format answers a question this little file raises.
Why MThd and a length? Chunks
A MIDI file is a sequence of chunks, and a chunk is a 4-byte ASCII type, a 4-byte big-endian length, and then that many bytes of data. Only two types exist: MThd, exactly one, first; and MTrk, one per track. A reader that runs into a type it doesn't know just reads the length and skips it, which is how a program written in 1990 survives a file written in 2026. The length doesn't count the chunk's own 8-byte header.
Here are the header's three fields:
4D 54 68 64 'MThd'
00 00 00 06 length, always 6
ff ff format: 0, 1 or 2
nn nn number of track chunks (always 1 for format 0)
dd dd division: the timing rulerThe division comes in two shapes. If bit 15 is 0, the lower 15 bits are ticks per quarter note: 01 E0 is 480, 03 C0 is 960. If bit 15 is 1, the file runs on SMPTE time: bits 8–14 hold the frame rate as a negative number (−24, −25, −29 for 29.97 drop-frame, −30) and bits 0–7 the ticks per frame. So E7 28 means −25 frames per second and 40 ticks per frame, which is exactly 1,000 ticks per second: one tick per millisecond.
Why 60 and not 96? Delta times
The delay before each event counts ticks since the previous event, not since the start of the song. 96 was written as the single byte 60 because it fits in 7 bits. Anything bigger uses a variable-length quantity: 7 bits of value per byte, most significant first, with the high bit set on every byte except the last. Four bytes at most, so the longest delay is 0x0FFFFFFF, or 268,435,455 ticks.
| Ticks | Bytes |
|---|---|
| 0 | 00 |
| 127 | 7F |
| 128 | 81 00 |
| 192 | 81 40 |
| 8,192 | C0 00 |
| 16,383 | FF 7F |
| 16,384 | 81 80 00 |
| 268,435,455 | FF FF FF 7F |
Decoding it takes four lines: start at zero; for each byte, shift the value left by 7 and add the low 7 bits; stop after the first byte whose high bit is clear. The reason for the trick is file size. A sixteenth note at 96 ticks per quarter is 24 ticks, so most delays fit in a single byte, and a one-byte delay sitting next to a two- or three-byte event is what keeps a whole song under 50 KB.
Why 90 and not "note on"? Status bytes and data bytes
A MIDI message is one status byte followed by one or two data bytes. A status byte has its high bit set (0x80 to 0xFF), and a data byte never does (0x00 to 0x7F). That rule comes straight from the 1983 cable protocol, it's why every MIDI value stops at 127, and it makes the stream self-synchronizing: drop into it anywhere you like and the next byte with its high bit set starts a message.
In a file, the first byte after a delay tells you which of three families you're in:
| First byte | Family |
|---|---|
| 0x80–0xEF | A channel message: a note, a controller, an instrument change, a pitch bend |
| 0xF0 or 0xF7 | System Exclusive: raw manufacturer-specific data |
| 0xFF | A meta event: information for the file, never sent down a cable. On a cable 0xFF means System Reset, which no file has a use for, so the file format took the byte. |
Channel messages: the seven things a channel can be told
The high nibble of the status byte is the message, and the low nibble is the channel.
| Status | Message | Data bytes | Notes |
|---|---|---|---|
| 8n | Note Off | note, release velocity | release velocity is usually 64 or 0 |
| 9n | Note On | note, velocity | velocity 0 means Note Off |
| An | Polyphonic key pressure | note, pressure | aftertouch on one key |
| Bn | Control Change | controller, value | knobs, pedals, volume, pan: see below |
| Cn | Program Change | program | one data byte: 0–127 in the file, 1–128 in GM tables |
| Dn | Channel pressure | pressure | aftertouch for the whole channel |
| En | Pitch Bend | low 7 bits, high 7 bits | the one 14-bit value: 0–16,383, 8,192 means no bend |
Notes. A note number runs from 0 to 127, one per semitone. 60 is middle C, 69 is A4 at 440 Hz. The bottom is note 0, C-1 at 8.18 Hz, which is below hearing; the top is 127, G9 at 12,544 Hz. The frequency of note n is
f = 440 × 2(n − 69) / 12 Hz
There's nothing in between two note numbers. A sound that's a quarter-tone sharp is note n plus pitch bend, and a bent guitar string is one note with a bend curve on it, not a series of new notes.
Velocity runs from 1 to 127 and means how hard the key was hit. The file gives it no unit, so each synth turns it into loudness and tone in its own way.
Program Change picks the instrument, and it's off by one: the byte 0x00 is GM program 1, Acoustic Grand Piano, and 0x18 (24) is program 25, Nylon Guitar. General MIDI groups the 128 programs into 16 families of 8:
| Programs | Family | Programs | Family |
|---|---|---|---|
| 1–8 | Piano | 65–72 | Reed |
| 9–16 | Chromatic percussion | 73–80 | Pipe |
| 17–24 | Organ | 81–88 | Synth lead |
| 25–32 | Guitar | 89–96 | Synth pad |
| 33–40 | Bass | 97–104 | Synth effects |
| 41–48 | Strings | 105–112 | Ethnic |
| 49–56 | Ensemble | 113–120 | Percussive |
| 57–64 | Brass | 121–128 | Sound effects |
On channel 10 the note number isn't a pitch but a drum: 36 is the kick, 38 the snare, 42 a closed hi-hat, 46 an open hi-hat, 49 a crash, 51 a ride. To reach more than 128 sounds, a file sends Bank Select (controller 0, then controller 32) before the Program Change, which gives you 16,384 banks of 128.
Pitch Bend is the only 14-bit value in the whole format. The two data bytes are the low and high 7 bits, so value = high × 128 + low. 8,192 (00 40) is centre, 0 is full down and 16,383 is full up. How far "full" goes depends on the bend range, which is ±2 semitones by default and can be changed through RPN 0. One semitone up with the default range is 8,192 + 4,096 = 12,288, written E0 00 60.
Control Change is where the rest of a performance lives. These are the ones that matter:
| Controller | Meaning |
|---|---|
| 0, 32 | Bank Select, high and low byte |
| 1 | Modulation wheel |
| 7 | Channel volume |
| 10 | Pan (64 = centre) |
| 11 | Expression |
| 64 | Sustain pedal (64 and above = down) |
| 91, 93 | Reverb send, chorus send |
| 101, 100, 6, 38 | RPN select and data entry (pitch-bend range, tuning) |
| 120 | All Sound Off |
| 121 | Reset All Controllers |
| 123 | All Notes Off |
Running status: the byte you are allowed to leave out
The spec ships with its own worked example, and it happens to demonstrate the format's one real trick. Three channels, four notes, 81 bytes. Here it is, line by line.
4D 54 68 64 00 00 00 06 MThd, length 6
00 00 format 0
00 01 one track
00 60 96 ticks per quarter note
4D 54 72 6B 00 00 00 3B MTrk, 59 bytes follow
00 FF 58 04 04 02 18 08 wait 0 | time signature 4/4, 24 clocks per metronome click, 8 thirty-seconds per quarter
00 FF 51 03 07 A1 20 wait 0 | tempo 500,000 us per quarter note = 120 BPM
00 C0 05 wait 0 | channel 1 plays program 5 (GM 6, Electric Piano 2)
00 C1 2E wait 0 | channel 2 plays program 46 (GM 47, Harp)
00 C2 46 wait 0 | channel 3 plays program 70 (GM 71, Bassoon)
00 92 30 60 wait 0 | channel 3 | note 48 (C3) on, velocity 96
00 3C 60 wait 0 | no status byte: still channel 3 Note On | note 60 (C4), velocity 96
60 91 43 40 wait 96 | channel 2 | note 67 (G4) on, velocity 64
60 90 4C 20 wait 96 | channel 1 | note 76 (E5) on, velocity 32
81 40 82 30 40 wait 192 (two bytes: 81 40) | channel 3 | note 48 off
00 3C 40 wait 0 | running status: channel 3 Note Off | note 60 off
00 81 43 40 wait 0 | channel 2 | note 67 off
00 80 4C 40 wait 0 | channel 1 | note 76 off
00 FF 2F 00 end of trackNow look at 00 3C 60. It has no status byte. That's running status: when the first byte of an event is a data byte, the message reuses the previous status byte. A run of notes on one channel then costs 2 bytes each instead of 3, and that's why so much software turns notes off with a Note On at velocity 0 rather than a real Note Off: it keeps the status byte the same, so it never has to be repeated. Running status carries across delays. The spec says a meta or SysEx event cancels it, but a careful parser remembers the last channel status anyway, because some files assume it survives.
Notice how little the file actually knows. It knows that a C3 and a C4 start together and that a G4 joins them a quarter note later. It doesn't know they form a C major chord, and it doesn't know that the bassoon is being asked to play two notes at once, which a real bassoon can't do.
Meta events: what the cable never carried
A meta event is FF, then a type byte, a variable-length length, and the data. It only ever exists in files.
| Type | Name | Data |
|---|---|---|
| 00 | Sequence number | 2 bytes |
| 01 | Text | any text |
| 02 | Copyright | text |
| 03 | Track name | text |
| 04 | Instrument name | text |
| 05 | Lyric | text, usually one syllable per event; this is how .kar works |
| 06 | Marker | text |
| 07 | Cue point | text |
| 20 | MIDI channel prefix | 1 byte |
| 21 | MIDI port | 1 byte; not in the spec, used by everyone to get past 16 channels |
| 2F | End of track | length 0; mandatory, the last event of every track |
| 51 | Set tempo | 3 bytes: microseconds per quarter note |
| 54 | SMPTE offset | 5 bytes: hour, minute, second, frame, fractional frame |
| 58 | Time signature | 4 bytes: numerator, denominator as a power of two, MIDI clocks per metronome click, thirty-second notes per 24 MIDI clocks |
| 59 | Key signature | 2 bytes: sharps (positive) or flats (negative) from −7 to 7, then 0 for major or 1 for minor |
| 7F | Sequencer-specific | manufacturer ID, then anything |
Tempo is stored as microseconds per quarter note, not as BPM, so BPM = 60,000,000 ÷ tempo. 07 A1 20 is 500,000, which is 120 BPM; 0F 42 40 is 1,000,000, or 60 BPM. Three bytes cap the value at 16,777,215, so the slowest tempo a MIDI file can express is about 3.58 BPM.
A 4/4 time signature is FF 58 04 04 02 18 08: four beats, a denominator of 2² = 4, 24 clocks per click, 8 thirty-seconds per quarter. 6/8 with the click on the dotted quarter is FF 58 04 06 03 24 08. Key signatures take two bytes: 00 00 is C major, 00 01 A minor, 02 00 D major (two sharps), FE 00 B-flat major (two flats).
System Exclusive: the escape hatch
F0, a variable-length length, then the raw bytes including the closing F7. It carries whatever a manufacturer wants it to, and the length is there so a reader can skip it. The one most files carry is the General MIDI reset, which puts a synth into a known state before the song starts: F0 05 7E 7F 09 01 F7. Roland's GS reset and Yamaha's XG reset do the same for their extensions. An F7 event has the same shape and carries raw bytes with no leading F0, for messages that have been split into packets.
From ticks to seconds
A tick lasts tempo ÷ division microseconds, so the time of an event is the sum of its delays, each one converted through whatever tempo is in force at that point:
Δtseconds = Δticks × tempoµsdivision × 106
At 480 ticks per quarter and 120 BPM a tick is 1.04 ms; at 140 BPM (tempo 428,571) it's 0.89 ms. In a format 1 file the tempo map lives in the first track and applies to every track, so a player merges the tracks by absolute tick and then walks through the tempo changes.
That resolution is why audio-to-MIDI conversion loses nothing you can hear in the timing. A note onset found in the audio gets rounded to the nearest tick, which is about a millisecond at 480 ticks per quarter, well under what the ear can tell apart. What the format can't hold is everything in between the notes: the timbre, the room, the noise.
A parser in 50 lines
Everything above fits on a page of Python. This reads any format 0 or 1 file and prints its notes with absolute ticks, running status included.
import struct, sys
def read_vlq(data, i):
value = 0
while True:
b = data[i]; i += 1
value = (value << 7) | (b & 0x7F)
if not b & 0x80:
return value, i
def parse(path):
data = open(path, "rb").read()
assert data[:4] == b"MThd"
fmt, ntracks, division = struct.unpack(">HHH", data[8:14])
print(f"format {fmt}, {ntracks} track(s), {division} ticks per quarter note")
i = 14
for _ in range(ntracks):
assert data[i:i+4] == b"MTrk"
length = struct.unpack(">I", data[i+4:i+8])[0]
i += 8
end = i + length
tick, status = 0, None
while i < end:
delta, i = read_vlq(data, i)
tick += delta
b = data[i]
if b == 0xFF: # meta event
kind = data[i+1]
n, i = read_vlq(data, i+2)
payload = data[i:i+n]; i += n
if kind == 0x51:
print(f"{tick:6} tempo {60_000_000 / int.from_bytes(payload, 'big'):.1f} bpm")
elif kind == 0x03:
print(f"{tick:6} track name: {payload.decode('latin-1')}")
continue
if b in (0xF0, 0xF7): # system exclusive
n, i = read_vlq(data, i+1); i += n
continue
if b & 0x80: # a new status byte...
status, i = b, i + 1 # ...otherwise running status
kind, ch = status >> 4, (status & 0x0F) + 1
nbytes = 1 if kind in (0xC, 0xD) else 2
d = data[i:i+nbytes]; i += nbytes
if kind == 0x9 and d[1] > 0:
print(f"{tick:6} ch {ch:2} note on {d[0]:3} vel {d[1]}")
elif kind in (0x8, 0x9):
print(f"{tick:6} ch {ch:2} note off {d[0]:3}")
elif kind == 0xC:
print(f"{tick:6} ch {ch:2} program {d[0]:3}")
i = end
parse(sys.argv[1])Run it on the 81-byte file above and you get the tempo, three program changes, four notes switching on, and four notes switching off at tick 384.
Where parsers die
- Note On with velocity 0 is a Note Off. Miss this and no note in most files ever ends.
- Everything is off by one somewhere: programs are 0–127 in the file and 1–128 in GM tables; channels are 0–15 in the file and 1–16 on screen. The drum channel is status nibble 9:
99 24 7Fis a kick on "channel 10". - Running status survives across delays. The spec says a meta or SysEx event cancels it; some files do not honour that, so track the last channel status regardless.
- Trust the chunk length, not End of Track, to find the next chunk. Some files lack the End of Track event; some have junk after it.
- In a format 1 file, put tempo changes in the first track. The spec puts the tempo map there, and players disagree about tempo events found anywhere else.
- Two Note Ons for the same pitch on the same channel before a Note Off: the spec does not say which Note Off ends which, and players disagree. Exporters should never write them.
- Text is ASCII according to the spec and Latin-1, Shift-JIS or UTF-8 in real files. Guess, and do not crash.
- There is no checksum and no compression. One wrong length byte makes the rest of the file unreadable, and nothing will tell you which byte.
Sources
- Standard MIDI Files 0.06, Dave Oppenheim, March 1, 1988
- Interpreting a Standard MIDI File, Dave Sebald (adoption by the MMA, July 1988)
Got the audio? Get the MIDI.
Upload any recording — guitar, piano, vocals, a full mix — and download the MIDI file. Free.
Convert audio to MIDI →