MusicXML ↔ MIDI · one real file, both directions
MusicXML to MIDI: the notes arrive, the performance does not.
MusicXML describes a score. MIDI describes a performance. Converting between them is not a change of file extension, it is a change of what the file is allowed to say — and things that one format has no word for get dropped without a warning. This page measures exactly which ones, by taking a 338-byte MIDI file that contains everything, pushing it through MusicXML, and comparing the two files field by field.
The short answer
Kept: pitches, durations, meter, key signature, every tempo change, track name, instrument, note-on velocity, lyrics.
Lost: sustain pedal, pitch bend, release velocity, the text inside the file, and the exact position of any two notes of the same pitch that overlap.
Changed: the file's time resolution goes from 480 ticks per quarter to 10,080, and a single track becomes two. That is not damage, but it is why a byte comparison of the two files tells you nothing — you have to compare events, not bytes.
The round trip
One file in, one file out, and the five things that changed
The source file is rich.mid, 338 bytes, and it was built to contain one of everything: a track name, a text event, a key signature of two sharps, 3/4 meter, two tempo changes (90 then 60 BPM), an instrument change, a sustain pedal down and up, three pitch-bend events, two different note-on velocities, and a release velocity on every note.
It went through music21 10.5.2 to MusicXML (8,559 bytes) and back to MIDI (310 bytes). Here is the comparison, event by event:
| What | Before | After | |
|---|---|---|---|
| Ticks per quarter note | 480 | 10,080 | changed |
| Format / tracks | format 0, 1 track | format 1, 2 tracks | changed |
| Sustain pedal (CC64) | 2 events | 0 events | lost |
| Pitch bend | 3 events | 1 event | lost |
| Release velocity | 32 on all 24 notes | 0 on all 24 notes | lost |
Text event (FF 01) | “localmidi-ops evidence file” | absent | lost |
Track name (FF 03) | “Waltz in D” | “Waltz in D” | kept |
| Meter / key | 3/4, 2 sharps | 3/4, 2 sharps | kept |
| Tempo, including the change | 90, then 60 | 90, then 60 | kept |
| Instrument | channel 1, program 41 | channel 1, program 41 | kept |
| Note-on velocity | 80 (16 notes), 40 (8 notes) | 80 (16 notes), 40 (8 notes) | kept |
Both files are in this page's directory: rich.mid is the input. Run the two commands above on it and you should reproduce every row.
The losses, one at a time
Each of these is a byte string you can look for
1. The sustain pedal: two control changes in, none out
In the MIDI file the pedal is two events on channel 1: B1 40 7F at the start (pedal down) and B1 40 00 at beat 4 (pedal up). That is controller 64, the damper pedal — the thing that makes a piano part sound like a piano part rather than a series of disconnected hits.
After the round trip the output file contains zero control-change events. The MusicXML in between contains no <pedal> element either: a search of the 8,559-byte file returns nothing. The pedal was not moved, re-timed or approximated — it was not carried at all.
This is the loss that changes how the music sounds the most, and the one with no visible symptom in a score. The notes are all in the right place; the piano simply stops pedalling.
2. Pitch bend: three events in, one meaningless event out
The input has a bend gesture: centred at beat 6 (E1 00 40, value 0), pushed to maximum at beat 7 (E1 7F 7F, value 8191), and released back to centre at beat 8 (E1 00 40, value 0). That is an audible bend up a whole step and back.
The output has one bend event, at beat 13, with value 0 — centred. The gesture is gone and what remains is a single instruction to do nothing. If you play the two files one after the other, the bend simply does not happen.
3. Release velocity: 32 on every note becomes 0 on every note
Every one of the 24 notes in the input ends with a note-off carrying velocity 32, not 0. Some samplers and piano libraries read that value — it is how a note is released, damped or let ring.
After the trip all 24 notes end with velocity 0. There is no element in the MusicXML that holds the value, so there is nowhere for it to survive. If your instrument patch reacts to release, this trip flattens it.
4. Two notes of the same pitch that overlap: the second one moves and shortens
This is the loss that is hardest to believe until you watch it happen. MIDI has no problem with two notes of the same pitch overlapping on one channel: one C is still sounding when the next C is struck. A staff cannot notate that in a single voice, because two noteheads of the same pitch at the same horizontal position are one notehead.
The test file is overlap.mid, 60 bytes: C4 from beat 1 to beat 5, and another C4 from beat 3 to beat 7. Before and after:
| Note 1 | Note 2 | Distance between onsets | |
|---|---|---|---|
| Before | on 0, off 1920 — 4 beats | on 960, off 2880 — 4 beats | 2 beats |
| After | on 10080, off 50400 — 4 beats | on 50400, off 70560 — 2 beats | 4 beats |
The second note was pushed forward until the first one ended, and then cut in half so it would fit the bar. What was a re-strike two beats into a held note is now a note on the next bar line, half as long. The rhythm changed and nothing warned you.
The MusicXML shows why: the two notes were written as two consecutive <note> elements in one measure, with durations 40320 and 20160 — six beats of content inside a bar marked 4/4. The score is metrically overfull, and the reader that imports it resolves the overflow by pushing the second note into the next bar.
5. Resolution and track count change, which is not damage but breaks comparisons
480 ticks per quarter becomes 10,080. Format 0 with one track becomes format 1 with two. Neither changes the music, but both mean that any check based on comparing bytes or offsets will fail even when nothing was lost. Compare events: pitches, onset times in beats, durations in beats.
The other direction
MusicXML to MIDI drops different things: the marks, not the controls
The reverse trip was tested with a four-bar phrase written as MusicXML by hand: four quarter notes, the first carrying a staccato dot and a lyric syllable, the second and third joined by a slur, and a <words>dolce</words> direction above the staff.
| In the MusicXML | In the MIDI | |
|---|---|---|
| Four quarter notes | Four notes, all exactly 10,080 ticks = 1 beat | kept |
| Staccato on the first note | Same length as the other three — not shortened, no marker | lost |
| Slur between notes 2 and 3 | No trace of any kind | lost |
<words>dolce</words> | No text event in the file | lost |
| Lyric syllable “la” | FF 05 lyric event, text “la” | kept |
Two of those are worth separating. The staccato dot is a performance instruction and it does not survive as one: the note keeps its full written length, so a staccato passage comes out sounding legato. That is audible. The slur and the word “dolce” are instructions to a player, and a player is exactly what MIDI does not have — but a lyric syllable does survive, as a lyric event, which shows the loss is not about text in general. It is about which text has a defined slot.
What to do about it
Pick the master file, then check the five things
- If the performance matters, keep the MIDI as the master and export MusicXML from it as a copy. Nothing in the list above hurts you if the MIDI is the file you play and the MusicXML is the file you read.
- If you must round-trip, check the pedal first. Search the output for
B0 40(orB1 40and so on for the channel). Zero matches means the pedal did not survive. - Then check pitch bend — count
E0-family events before and after. A count that dropped is a gesture that is gone. - Then look for overlapping same-pitch notes in the source, because those are the ones that move. If two notes of one pitch overlap in time, expect the second to be displaced to the end of the first.
- Compare events in beats, not bytes. A resolution change from 480 to 10,080 makes every raw offset look wrong while the music is untouched.
FAQ
Questions about MusicXML and MIDI.
Which format is lossless?
Neither. MusicXML carries notation that MIDI has no word for — slurs, staccato, dynamics as text — and MIDI carries performance that MusicXML has no word for — pedal, bend, release velocity. Converting in either direction loses the side the target format does not describe. The question is never “is it lossless”, it is “which of the two do I need to be exact”.
Why did my tempo changes survive but my pedal did not?
Because tempo has a defined element in MusicXML: <sound tempo> inside a direction. Two tempo changes came through as two directions at the right places, 90 then 60. A pedal does not have an element that this converter writes, so there was nothing to write. Survival is decided by whether the target format has a slot, not by how important the thing is musically.
Why is the output resolution 10,080 instead of 480?
Because MusicXML measures durations in <divisions> per quarter note, and 10,080 divides evenly by the note values a score uses, including triplets and septuplets. 480 is a MIDI convention. Both describe the same music; the numbers are in different units, which is why comparing raw tick offsets between the two files is misleading.
My two chords became one. Is that the same problem?
Yes, it is the overlapping-pitch case. If two notes of the same pitch start at the same time in the same voice, a staff has one notehead for them and the importer has to choose. Split them across voices before converting, or accept that one of them will move.
Would a different converter keep the pedal?
Possibly — this page measures one library, music21 10.5.2, and reports only what it did. That is exactly why the test file is published: run your own converter on it and compare the eleven rows above. Any converter that keeps the pedal will show two B0 40-family events in its output where this one showed none.
Can I get the pedal back after the round trip?
Only by re-entering it, and it is easier to re-enter it in the MIDI file than in the score, because the pedal is a performance decision rather than a notated one. That is the practical reason to keep the MIDI as the master and treat the MusicXML as a readable copy.
Keep going