Tutorial · DIY Lab
MIDI vs MusicXML in Python: what survives and what doesn't
An experiment with music21 and pretty_midi: the same phrase with fingering, a slur, and a dynamic marking, exported to both MIDI and MusicXML. What survives in each format, with real figures.
MIDI is the format anyone who has ever played an electronic keyboard knows, even if only by name. MusicXML is much less known, and yet one question comes up as soon as you need more than “just make it sound”: what if what you need is the score, not the sound? Fingering, slurs, dynamic markings —everything a performer reads and a computer may need to keep—. This tutorial doesn’t explain each format again —that’s what What MIDI is? and What MusicXML is? are for— it demonstrates it instead: it builds the same musical phrase once, exports it to both formats, and checks, with real data, what survives in each one.
The experiment
The idea is simple: write a short phrase with music21 —fingering on each note, a slur, and a dynamic marking included— and export it twice, once to MusicXML and once to MIDI. Then read each file back and see what’s left.
from music21 import stream, note, meter, tempo, dynamics, articulations, spanner, clef
s = stream.Part()
s.append(clef.TrebleClef())
s.append(meter.TimeSignature('4/4'))
s.append(tempo.MetronomeMark(number=100))
n1 = note.Note('C5', quarterLength=1)
n1.articulations.append(articulations.Fingering(1))
n2 = note.Note('E5', quarterLength=1)
n2.articulations.append(articulations.Fingering(3))
n3 = note.Note('G5', quarterLength=0.5)
n3.articulations.append(articulations.Fingering(5))
n4 = note.Note('E5', quarterLength=0.5)
n4.articulations.append(articulations.Fingering(3))
n1.dynamic = dynamics.Dynamic('mp')
slur = spanner.Slur(n3, n4)
s.append(n1); s.append(n2); s.append(n3); s.append(n4)
s.insert(0, dynamics.Dynamic('mp'))
s.insert(0, slur)
s.write('musicxml', fp='ejemplo.musicxml')
s.write('midi', fp='ejemplo.mid')
Four notes —C5, E5, G5, E5—, each with its indicated finger, the last two slurred together, and the whole phrase marked mezzo-piano. Nothing exotic: it’s exactly the kind of information a performer expects to see printed on a score.

Reading the MIDI back
With pretty_midi you can open ejemplo.mid and list the notes exactly
as they were encoded:
pitch=72 start=0.00 end=0.60 velocity=81 (C5)
pitch=76 start=0.60 end=1.20 velocity=81 (E5)
pitch=79 start=1.20 end=1.50 velocity=81 (G5)
pitch=76 start=1.50 end=1.80 velocity=81 (E5)
Four note events, each with a pitch, a start and end instant in seconds,
and a velocity —81 for all four, the result of translating the phrase’s
single mp dynamic into a fixed MIDI value—. This is exactly what you’d
expect from MIDI: instructions for when something sounds, and how hard.
No trace of the fingering, the slur, or the fact that an “mp” marking was
ever there: that information has nowhere to live in the protocol. It plays
just as well, but it was lost for good the moment it was saved.
Reading the MusicXML back
With music21.converter.parse on ejemplo.musicxml, the same phrase
returns much more:
C5: fingering=[1]
E5: fingering=[3]
G5: fingering=[5]
E5: fingering=[3]
Slurs explicitly encoded: 2
Dynamic markings: ['mp']
All four fingerings are intact, the slur is still encoded as such —not as a
coincidence of timing, but as an explicit <slur> element— and the “mp”
marking is still “mp”, not a velocity number. Everything MIDI had nowhere to
store, MusicXML keeps, because it describes the score, not the performance.
A fragment of the generated XML shows why: each of those elements is a readable tag, not a protocol number.
<dynamics default-x="-36" default-y="-80">
<mp />
</dynamics>
...
<fingering alternate="no" substitution="no">1</fingering>
...
<slur number="1" type="start" />
What the bytes say
The difference also shows up, literally, in the size and shape of each
file. ejemplo.mid weighs 99 bytes; ejemplo.musicxml, 3560 bytes
—36 times more for the same four-note phrase—. That’s not inefficiency: it’s
the difference between storing only playback instructions and storing a
full description of the written notation.
The first bytes of the MIDI file, in hexadecimal, are pure binary:
4d54 6864 0000 0006 0001 0002 2760 4d54
726b 0000 0014 00ff 5103 0927 c000 ff58
Without a parser in between, this says nothing. MusicXML, by contrast, is
XML text that anyone can open in an editor and read —the fragments above
(<dynamics>, <fingering>, <slur>) make sense with no context beyond
the tag’s own name—.
Comparison table for the experiment
MIDI (ejemplo.mid) | MusicXML (ejemplo.musicxml) | |
|---|---|---|
| Notes (pitch, timing) | ✔ Kept | ✔ Kept |
| Fingering | ✘ Lost | ✔ Kept ([1], [3], [5], [3]) |
| Slur | ✘ Lost (only contiguous timing) | ✔ Kept as explicit <slur> |
| Dynamic (“mp”) | Approximated as velocity=81 | ✔ Kept as text (mp) |
| Size | 99 bytes | 3560 bytes |
| Human-readable | ✘ Binary | ✔ XML text |
Why it matters in practice
This isn’t a format curiosity: it decides which tool to use depending on what needs to be preserved. To play back, sequence, or drive a synthesiser, MIDI is enough, and lighter. For notation, teaching —a fingering a student needs to see printed— or archiving a score meant to be edited later, MIDI falls short by design, not by neglect: it never promised to keep that.
It’s also why an automatic music transcription system has to choose its output format carefully depending on the intended use: MIDI if the goal is for the transcription to sound, MusicXML if the goal is for someone to read it, correct it, or use it to teach. In my own doctoral research I work with piano as my corpus, and this same dilemma comes up as soon as an AMT system finishes deciding which notes sounded: the next step is which format to deliver that result in, and it’s not a neutral decision.
References
The references this article draws on, and where to read further:
- The MIDI Association. The Official MIDI Specifications (MIDI 1.0, General MIDI, MIDI 2.0).
- W3C Music Notation Community Group. (2021). MusicXML 4.0.
- Cuthbert, M. S., & Ariza, C. (2010). music21: A Toolkit for Computer-Aided Musicology and Symbolic Music Data. Proceedings of the 11th International Society for Music Information Retrieval Conference (ISMIR).
- Raffel, C., & Ellis, D. P. W. pretty_midi: A Python Library for Handling MIDI Data — official repository and documentation.