MIDI 1.0

Timing & Sync

Spec: MIDI 1.0 Detailed Specification Standard MIDI Files

MIDI handles time in two ways. Files store musical time as ticks, converted to seconds through a tempo map. Live systems sync through a stream of clock messages (tempo-relative) or MIDI Time Code (absolute, SMPTE-based).

Ticks, PPQ and tempo

A Standard MIDI File's header sets the division, usually as PPQ (pulses or ticks per quarter note). Common values are 96, 480, and 960. Every event's delta-time counts ticks. Tempo is stored separately, in microseconds per quarter note, by the Set Tempo meta event FF 51 03. With no tempo event, assume 500,000 µs (120 BPM).

QuantityFormula
BPM60,000,000 / µsPerQuarter
µs per quarter60,000,000 / BPM
ms per tickµsPerQuarter / PPQ / 1000
seconds for n ticksn × µsPerQuarter / PPQ / 1,000,000

Example: at 120 BPM (500,000 µs) and 480 PPQ, one tick lasts 500,000 / 480 / 1000 ≈ 1.042 ms. A tempo change affects only the ticks after it, so finding absolute time means walking the tempo map and summing each segment.

BPM means quarter notes

MIDI tempo is always defined per quarter note, whatever the time signature. In 6/8, a "dotted-quarter = 80" feel is a MIDI tempo of 120 quarter notes per minute.

Tempo calculator

BPM · µs/quarter · ticks

SMPTE-based division

A file can use absolute time instead of PPQ. When bit 15 of the division word is set, the high byte is a negative frame rate in two's complement (E8 = −24, E7 = −25, E3 = −29 for 29.97 drop frame, E2 = −30) and the low byte is ticks per frame. E7 28 (25 fps × 40 ticks) gives exactly 1 ms per tick. Tempo events do not affect timing in this mode.

MIDI Clock

MIDI Clock is a tempo-following pulse. A leader sends F8 (Timing Clock) at 24 pulses per quarter note. Followers derive tempo from the spacing between pulses. There is no tempo value in the stream, so tempo changes appear as changes in pulse spacing.

StatusNameMeaning
F8Timing Clock24 per quarter note while the leader runs. Many devices send it even while stopped, so followers can show tempo.
FAStartStart from the beginning (song position 0). The first F8 after Start marks the first beat.
FBContinueResume from the current song position, for example one just set by SPP. Also begins at the next clock.
FCStopStop. The follower keeps its position so a later Continue resumes there.
F2 ll mmSong Position PointerSets the position in MIDI beats. Send while stopped.

These are System Real-Time messages. They are a single byte and may appear anywhere, even between the bytes of another message.

Song Position Pointer

SPP is a 14-bit value, LSB first, counting MIDI beats. One MIDI beat is a sixteenth note, which is 6 clocks. The maximum, 16,383 sixteenths, is just under 1,024 bars of 4/4.

position (16ths) = ll | (mm << 7)
clocks from start = position × 6
F2 20 00  →  32 sixteenths  →  bar 3, beat 1 in 4/4

The usual way to locate: FC (Stop), F2 ll mm, then FB (Continue). Allow the follower time to seek before sending clocks.

Jitter and latency

At 120 BPM, clocks arrive every 20.8 ms. Followers usually smooth the incoming intervals (moving average or a phase-locked loop) instead of reacting to each pulse, because USB scheduling, OS timers and merges add jitter. If tight sync matters, use a dedicated hardware clock source, avoid long chains of MIDI Thru, and compensate for fixed latency on the follower. On a network, Ableton Link is a common alternative.

MIDI Time Code

MIDI Time Code (MTC) carries absolute SMPTE time (hours:minutes:seconds:frames), used to lock MIDI devices to video, tape and other timecode-driven systems. It is independent of tempo.

Quarter-frame messages

While running, the leader sends F1 0nnn dddd four times per frame. nnn is the piece number (0–7) and dddd a 4-bit nibble:

PieceData byteCarries
00000 ffffFrames, low nibble
10001 000fFrames, high bit
20010 ssssSeconds, low nibble
30011 00ssSeconds, high 2 bits
40100 mmmmMinutes, low nibble
50101 00mmMinutes, high 2 bits
60110 hhhhHours, low nibble
70111 0rrhHours, high bit (h) and frame rate (rr)
rrFrame rate
0024 fps (film)
0125 fps (PAL/EBU)
1029.97 fps drop-frame (NTSC)
1130 fps non-drop

Eight quarter frames, so two frames, make one complete time value. When piece 7 arrives, the assembled time is the time at which piece 0 was sent. A receiver must add 2 frames to get current time. In reverse playback the pieces arrive 7→0.

Full-frame message

When locating, rewinding, or jumping, a full time stamp is sent at once as Universal Real-Time SysEx:

F0 7F 7F 01 01 hh mm ss ff F7

hh is 0rrhhhhh, with the rate bits as above and the hour in the low 5 bits. The other bytes hold minutes (0–59), seconds (0–59), and frames.

Drop-frame timecode

NTSC video runs at 30000/1001 ≈ 29.97 fps. Drop-frame timecode keeps the displayed time close to wall-clock time by skipping frame numbers 00 and 01 at the start of every minute, except minutes divisible by 10. No video frames are dropped, only labels. 29.97 non-drop also exists in video, but MTC has no code for it. It is usually sent with the 30 fps code and runs 0.1% slow.

Aside: Active Sensing

FE Active Sensing is not a timing message, but it belongs to the same Real-Time family. It is optional. Once a receiver has seen one, it expects a message of some kind at least every 300 ms. If none arrives, it assumes the cable was pulled and turns off all voices. Most software filters FE out of its monitors.