Skip to content

MIDI Basics ​

MIDI (Musical Instrument Digital Interface) is a language for describing a performance, not the sound itself. A MIDI message says "play note 60 fairly hard now" or "let go of note 60" — it never carries audio. Something downstream (a synthesizer, a SoundFont player, or a piece of hardware) reads those instructions and produces the sound.

That separation is powerful: the same MIDI performance can be played back through a piano sound, a guitar sound, or an orchestra, just by pointing it at a different instrument. The piano roll below makes that concrete — the notes never move, but switching the instrument re-voices the identical MIDI through a different built-in sound.

MIDI · PIANO ROLLIDLE
A MIDI passage — same notes, any instrument

A three-voice phrase — melody, chords, and bass — drawn as a piano roll. The notes never change; switch the instrument and the engine bounces the exact same MIDI through a different built-in voice. Drag the tempo and the whole sequence speeds up or slows down. Press play to hear the passage; the playhead tracks the audio.

Instrument
Tempo
100 BPM

Because MIDI is structured musical information, the same notes can be read as notation just as easily as a grid. The score below is engraved straight from the MIDI the engine plays — same notes, same instrument switch, same tempo — and each note lights up as it sounds. The piano roll and the score are two views of one underlying performance.

MIDI · SCOREIDLE
The same MIDI, written as a score

The very same three-voice phrase as the piano roll above — melody, broken chords, and bass — engraved as standard notation on a grand staff. The notes, durations, beams, and clefs are read straight from the MIDI the engine plays; switch the instrument and the identical notes sound through a different built-in voice, drag the tempo and the reading speeds up. Press play to hear it: every sounding note lights up the instant it plays.

Instrument
Tempo
96 BPM

This page explains the vocabulary you need to read or write MIDI. It is concepts only — no code.

Performance, not audio

A MIDI file is tiny — kilobytes — because it stores instructions, not waveforms. The trade-off is that it sounds different depending on which instrument renders it. An audio file always sounds the same but cannot be re-voiced or transposed for free.

Notes: note-on, note-off, number, velocity ​

The two core messages are note-on (a key went down) and note-off (the key was released). Each carries:

  • A note number from 0 to 127, where each step is one semitone. Middle C = 60, and A4 (the 440 Hz tuning reference) = 69. Add 12 to go up an octave. (Octave names differ between products: note 60 is "C4" in some and "C3" in others, so go by the number.)
  • A velocity from 0 to 127 — how hard the key was struck. An instrument typically maps velocity to loudness and brightness, so a high velocity is louder and punchier. (In MIDI 1.0 a note-on with velocity 0 is treated as a note-off; in MIDI 2.0 it is a real note-on.)

Channels ​

A single MIDI stream is split into 16 channels, like 16 separate players sharing one cable. Each channel can host its own instrument, so channel 1 might be a piano and channel 2 a bass. One channel is special by convention: in General MIDI (below), channel 10 is the drum channel, where each note number triggers a different percussion sound instead of a pitch.

Control Change (CC) ​

CC

A Control Change message turns a numbered knob (0-127) to a value (0-127). It is how expression, pedals, and continuous controllers are sent.

The most common controllers:

CCNameWhat it does
CC1Mod wheelGeneral-purpose modulation (often vibrato depth)
CC7Channel volumeThe channel's main level, like a mixer fader
CC10PanStereo position: 0 = hard left, 64 = center, 127 = hard right
CC11ExpressionA volume-like swell within a phrase, on top of the main level
CC64Sustain pedal0-63 = up, 64-127 = down; holds notes after the keys are released

Seven bits (0-127) is coarse for a smooth fader sweep, so controllers 0-31 each have a partner 32 numbers higher (CC0 with CC32, CC7 with CC39, CC10 with CC42): the first carries the MSB (most significant 7 bits), the partner the LSB (least significant 7 bits). Sent as a pair they form one 14-bit value from 0 to 16383. A device that only needs coarse control sends the MSB alone.

Choosing a sound: Program Change and bank select ​

A Program Change message picks which instrument a channel plays, by program number 0-127. Because 128 sounds is often not enough, bank select widens the address: CC0 is the bank MSB (most significant byte) and CC32 is the bank LSB (least significant byte), sent just before the Program Change to choose a different bank of 128 programs.

GM (General MIDI)

General MIDI is a standard that fixes what each program number means: program 0 is always an acoustic grand piano, 24 a nylon guitar, and so on across a 128-instrument map, plus a standard drum map on channel 10. GM is why a MIDI file made on one device plays the right kinds of instruments on another.

GM2

General MIDI 2 (GM2) is the later revision of GM. It keeps the 128-program map and adds variation banks through Bank Select: MSB 121 selects the melodic set and MSB 120 the drum set, and the LSB picks the variation within it.

Counting from 0 or from 1 ​

The bytes on the wire count from 0, but most manuals, instrument lists and DAWs count from 1. The same value therefore has two names, and mixing them up selects the wrong instrument.

ValueIn the message and in libsonare's APIIn most manuals and DAWsExample
Channel0–151–16the GM drum channel is "channel 10", passed as 9
Program0–1271–128Violin is program 40 here and "41" in a one-based list
Note number0–1270–127the same in both: middle C is 60

These docs write programs and API channels zero-based. When prose says "channel 10" it names the channel the way a manual does, and the code beside it passes 9.

Pitch bend, RPN, and NRPN ​

Pitch bend is its own high-resolution message (not a CC) that slides a note's pitch up or down continuously — for guitar bends, vibrato, or a whammy effect. How far a full bend reaches (the pitch-bend range) is not part of the pitch-bend message itself; it is set separately with the RPN message described below:

RPN / NRPN

A Registered Parameter Number (RPN) is a standardized setting addressed through CC messages. RPN 0 is the pitch-bend range — how many semitones a full bend covers. A Non-Registered Parameter Number (NRPN) uses the same mechanism for device-specific parameters that are not part of the standard.

Setting an RPN takes a short sequence of Control Changes on the channel. CC101 (RPN MSB) and CC100 (RPN LSB) select the parameter, then CC6 (Data Entry MSB) and CC38 (Data Entry LSB) write its value. For a pitch-bend range of ±12 semitones:

StepMessageMeaning
1CC101 = 0select RPN, MSB 0
2CC100 = 0select RPN, LSB 0: pitch-bend range
3CC6 = 1212 semitones
4CC38 = 0plus 0 cents
5CC101 = 127, CC100 = 127deselect (the "null" RPN), so a stray data-entry message cannot change the range later

An NRPN is selected the same way with CC99 (MSB) and CC98 (LSB) instead of CC101 and CC100.

Timing: ticks and PPQ ​

A MIDI message says what happens; a file also needs when. A Standard MIDI File counts time in ticks, whole-number steps with no fixed length in seconds. PPQ (pulses per quarter note, also written PPQN or division) says how many ticks make one quarter note; 480 is a common choice. At 480 PPQ a quarter note is 480 ticks, an eighth note 240, a sixteenth 120.

Ticks become seconds only through the tempo: at 120 quarter notes per minute a quarter note lasts 0.5 s, so one tick at 480 PPQ lasts about 1.04 ms. A tempo map (tempo changes at given ticks) lets one file speed up and slow down, and the same ticks then land at different times.

libsonare's ppq is a position, not a resolution

In the arrangement API a ppq argument is a position measured in quarter notes (a float): 1 is one quarter note in, 0.5 an eighth. It is not "pulses per quarter" in the file-format sense. When you convert from a file, divide its ticks by its PPQ first (tick 960 at 480 PPQ is ppq 2). The SMF exporter writes 480 ticks per quarter note.

MIDI files and MIDI 2.0 ​

UMP

MIDI 2.0 is the modern revision of the protocol. It uses the Universal MIDI Packet (UMP) format and adds much higher resolution (16-bit velocity, finer controllers) and two-way communication, while staying compatible with the MIDI 1.0 ideas above.

A UMP stream also widens the channel space: it carries 16 groups, each with its own 16 channels, so a single connection can address 256 channels. API calls that take a group argument are choosing one of those groups; a plain MIDI 1.0 setup uses group 0.

MPE (MIDI Polyphonic Expression)

In MIDI 1.0, pitch bend and pressure (aftertouch, how hard a held key is pressed) apply to a whole channel, so bending one note of a chord bends all of them. MPE works around that by giving each sounding note its own channel. A zone is a block of channels set up for this: one manager channel (channel 1 for the lower zone, 16 for the upper) carries messages meant for the whole zone, such as the sustain pedal, and the member channels each carry one note and its own bend, pressure and timbre (CC74). MIDI 2.0 can address a note directly with per-note messages, which reaches the same result without spending a channel per note.

A Standard MIDI File (SMF, the .mid extension) stores a whole performance — every note-on/off, CC, and Program Change with its timing — so it can be saved, shared, and replayed through any instrument.

How it fits together ​

MIDI's separation of performance from sound shows up as a straight pipeline: a source emits note and controller messages, an instrument interprets them, and only the last step produces audio.

MIDI message flow
MIDI sourceNote-on/off, CC,Program ChangeInstrument (synth/ SoundFont)Audio output
The source (keyboard or file) and the instrument (synth or SoundFont) are interchangeable — only the messages in the middle carry the performance.
How libsonare implements this

libsonare's realtime engine accepts live MIDI through pushMidiNoteOn(destinationId, group, channel, note, velocity) and pushMidiNoteOff(...) for notes, and pushMidiCc(destinationId, group, channel, controller, value) for Control Change messages — the destinationId selects which loaded instrument receives the event, and channel is the 0-15 MIDI channel. In the browser, a Web MIDI bridge (bindWebMidi) connects a physical MIDI keyboard's events straight into those engine inputs, so a hardware controller can play NativeSynth or a loaded SoundFont live. The same note/CC vocabulary is used by both the live engine and offline arrangement bounces.

Related: MIDI Input, SoundFont and Sampled Instruments, Editing Basics