Python MIDI Processing — Core Concepts

What MIDI is

MIDI (Musical Instrument Digital Interface) is a protocol and file format that describes musical events — note on, note off, pitch bend, program change — rather than audio waveforms. A MIDI file is tiny (kilobytes) because it stores instructions, not sound. The same MIDI file can sound like a grand piano or a chiptune synth depending on the playback engine.

MIDI file structure

A standard MIDI file contains:

  • Header: file type (0, 1, or 2), number of tracks, timing resolution (ticks per beat)
  • Tracks: ordered lists of timestamped messages
  • Messages: note_on, note_off, control_change, program_change, tempo changes, etc.

Type 0 files have a single track. Type 1 files have multiple tracks that play simultaneously (most common). Timing is measured in ticks, and a tempo meta-message maps ticks to real time (microseconds per beat).

Key Python libraries

LibraryStyleBest for
midoLow-level, message-orientedReading/writing MIDI, real-time MIDI I/O
pretty_midiHigh-level, note-orientedAnalysis, visualization, ML feature extraction
music21MusicologicalTheory, notation, score analysis

Working with mido

import mido

mid = mido.MidiFile("song.mid")
for track in mid.tracks:
    for msg in track:
        if msg.type == "note_on" and msg.velocity > 0:
            print(f"Note {msg.note}, velocity {msg.velocity}, time +{msg.time} ticks")

Mido represents every MIDI event as a Message object with named attributes. Time values are delta times — ticks since the previous message in the same track.

Creating MIDI from scratch

mid = mido.MidiFile()
track = mido.MidiTrack()
mid.tracks.append(track)

track.append(mido.Message("program_change", program=0, time=0))  # Piano
track.append(mido.Message("note_on", note=60, velocity=100, time=0))
track.append(mido.Message("note_off", note=60, velocity=0, time=480))
track.append(mido.Message("note_on", note=64, velocity=100, time=0))
track.append(mido.Message("note_off", note=64, velocity=0, time=480))

mid.save("output.mid")

Working with pretty_midi

Pretty_midi abstracts MIDI into Instrument objects containing Note objects with start time, end time, pitch, and velocity in seconds — much easier for analysis.

import pretty_midi

pm = pretty_midi.PrettyMIDI("song.mid")
for instrument in pm.instruments:
    print(instrument.program, instrument.name)
    for note in instrument.notes[:5]:
        print(f"  Pitch {note.pitch}, {note.start:.2f}s–{note.end:.2f}s")

It also provides piano-roll extraction (pm.get_piano_roll()), tempo estimation, key detection, and chroma features — useful for feeding MIDI data into ML models.

Common operations

  • Transpose: Add/subtract from every note number (mido: msg.note += 2, pretty_midi: note.pitch += 2)
  • Change tempo: Modify the tempo meta-message or scale all tick durations
  • Quantize: Snap note start times to the nearest grid division (eighth note, sixteenth note)
  • Merge tracks: Combine instruments into a single track or split a busy track
  • Filter: Remove drums (channel 10 in General MIDI), isolate melody, extract bass line

Common misconception

MIDI note numbers are not frequencies. Note 60 is Middle C (261.6 Hz), note 69 is A4 (440 Hz). The formula is frequency = 440 × 2^((note - 69) / 12). Libraries handle this conversion, but understanding it helps when mixing MIDI analysis with audio analysis.

How it fits with other tools

MIDI analysis pairs naturally with Librosa (audio features), music21 (music theory), and FluidSynth (rendering MIDI to audio). For ML pipelines, convert MIDI to piano-roll matrices and use them as model inputs for music generation, transcription, or style transfer.

One thing to remember: MIDI files are structured musical instructions — Python libraries like mido and pretty_midi let you read, create, modify, and analyze these instructions to build anything from music generators to analytical tools.

pythonmidimusicprocessingmido

See Also

  • Python Arcade Library Think of a magical art table that draws your game characters, listens when you press buttons, and cleans up the mess — that's Python Arcade.
  • Python Audio Fingerprinting Ever wonder how Shazam identifies a song from just a few seconds of noisy audio? Audio fingerprinting is the magic behind it, and Python can do it too.
  • Python Barcode Generation Picture the stripy labels on grocery items to understand how Python can create those machine-readable barcodes from numbers.
  • Python Cellular Automata Imagine a checkerboard where each square follows simple rules to turn on or off — and suddenly complex patterns emerge like magic.
  • Python Godot Gdscript Bridge Imagine speaking English to a friend who speaks French, with a translator in the middle — that's how Python talks to the Godot game engine.