DEV Community

Alex Creatune
Alex Creatune

Posted on

How to Read a MIDI File with Python Before Editing It

A MIDI file can open without an obvious error and still contain a track you did not expect, a pitch outside the part you meant to keep, or a note event that never closes. A quick file-level pass gives you a map before you move notes in a piano roll.

I’m Alex from the Creatune team. This is a reusable inspection template based on the public MIDI Editor guide, checked on November 15, 2026. The code below is a method, not a report from a file I processed.

What this check can tell you

MIDI stores events rather than recorded sound. A note event has a pitch, a start position, a duration and a velocity value. The file can also contain several tracks and channels. A piano-roll editor makes those values visible; a short script can summarize them before you start editing.

Public MIDI Editor workflow from an audio idea to a DAW

This pass reports each track’s event count, note count, pitch range, velocity range and unmatched note starts or ends. It does not decide whether a phrase is musical, identify the instrument sound, or prove that a conversion is correct.

Install the reader

Use Python 3 and the mido package. Save the MIDI file locally and install the package in the same environment where you will run the script:

python -m pip install mido
Enter fullscreen mode Exit fullscreen mode

The file stays on your computer for this inspection. The script only reads the MIDI structure; it does not render audio or change the file.

Run this reusable inspection template

Save the following as inspect_midi.py, then replace phrase.mid with the path to your own MIDI file:

from collections import defaultdict, deque
from pathlib import Path
import sys
import mido

path = Path(sys.argv[1] if len(sys.argv) > 1 else "phrase.mid")
midi = mido.MidiFile(path)
print(f"file: {path.name}")
print(f"format: {midi.type}; ticks per beat: {midi.ticks_per_beat}")

for number, track in enumerate(midi.tracks, start=1):
    tick = 0
    notes = 0
    pitches = []
    velocities = []
    active = defaultdict(deque)
    unmatched_off = 0

    for message in track:
        tick += message.time
        if message.type == "note_on" and message.velocity > 0:
            key = (getattr(message, "channel", 0), message.note)
            active[key].append(tick)
            pitches.append(message.note)
            velocities.append(message.velocity)
            notes += 1
        elif message.type == "note_off" or (
            message.type == "note_on" and message.velocity == 0
        ):
            key = (getattr(message, "channel", 0), message.note)
            if active[key]:
                active[key].popleft()
            else:
                unmatched_off += 1

    unmatched_on = sum(len(starts) for starts in active.values())
    pitch_range = f"{min(pitches)}–{max(pitches)}" if pitches else "none"
    velocity_range = f"{min(velocities)}–{max(velocities)}" if velocities else "none"
    print(
        f"track {number}: events={len(track)}, notes={notes}, "
        f"pitch={pitch_range}, velocity={velocity_range}, "
        f"unmatched_on={unmatched_on}, unmatched_off={unmatched_off}"
    )
Enter fullscreen mode Exit fullscreen mode

Run it with:

python inspect_midi.py phrase.mid
Enter fullscreen mode Exit fullscreen mode

mido exposes note numbers, not note names, in this summary. The pitch range is still a useful first signal: if you expected a single melody in a narrow register but see a very wide range, inspect the track and channels before editing. Different projects may use different octave labels for the same MIDI note number, so compare by note number or verify the editor’s convention before translating the range into a named octave.

Turn the summary into an editing checklist

Open the file in a piano roll and compare the notes with the part you intended to preserve. Creatune’s public MIDI Editor page describes pitch, start time, duration and velocity as separate controls. Work through them in that order only when the phrase calls for it:

  1. Pitch: check whether the notes belong to the intended melody, chord or bass part.
  2. Start time: look for notes that begin noticeably before or after the phrase’s pulse. Keep small offsets if they are part of the groove.
  3. Duration: check overlaps and note endings; a note can start correctly but still obscure the next event if it lasts too long.
  4. Velocity: use the velocity lane for note-to-note emphasis. It is not the same as changing the rendered audio’s overall volume.

The public MIDI Editor guide separates pitch, start time, duration, and velocity

A file summary is a navigation aid, not a correction command. Change one category at a time and keep a copy of the original MIDI so you can compare your edits.

For the next layer, read a Japanese guide to MIDI velocity and musical dynamics to distinguish note velocity from track loudness, then see a Japanese five-step checklist for reviewing converted MIDI for a broader post-conversion review.

What to do when the counts look unusual

  • No notes in a track: it may contain tempo or marker events only. Check the other tracks before treating the file as empty.
  • Unmatched note events: inspect the affected track and channel; do not silently interpret a missing pair as a valid duration.
  • A broad pitch range: a mixed or polyphonic part may be expected. Compare the intended source and the specific track rather than deleting notes based on range alone.
  • A high or low velocity range: this describes MIDI note values. It does not tell you the audio’s measured loudness.

The useful outcome is a short list of questions for the piano roll. Let the file structure tell you where to look, then use musical context to decide what to keep.

Top comments (0)