io.musicxml.canonical

canonical

Comparing MusicXML documents for the music they hold, not how they spell it.

canonicalize reduces a <score-partwise> to a canonical form, and compare lists the Differences between two canonical forms. Two exports of one score by different programs, or a file and the writer’s version of what the reader made of it, differ in a hundred ways that say nothing about the music: the order of voices in a measure, <divisions>, numbering, where a direction sits among the notes. Those are normalized away, in this order:

  1. Presentation is dropped: the elements _plans/musicxml.md §5.10 lists (<defaults>, <print>, layouts, <appearance>, <credit>, MIDI devices, <bookmark>, <link>, <figured-bass>), <sound>, <encoding>, positions, fonts, colours, placement, <stem>, <beam>, <accidental>, noteheads other than slash, and ids.
  2. Each measure becomes a flat list of items, each at an explicit position: its distance from the measure’s start in whole notes, folding in any <offset>. So <backup>, <forward> and <divisions> disappear, and a <forward> and a hidden rest are alike: both leave nothing.
  3. Voices are renumbered per staff by first appearance; measures by their index (implicit dropped); parts P1, P2, … in order; numbered spans (slurs, hairpins, pedals, part groups) lose their numbers.
  4. A key, time or transposition with no number stands for each staff; <tie> and <tied> are one item; a <dynamics> inside <notations> is the same item as one in a <direction> at the note; the notes of a chord take the first note’s duration, as the reader does.
  5. A rest keeps only its length (its <type> is conventional: a whole rest in a 3/4 bar), and adjacent rests of a voice are one silence; <harmony> keeps root, kind, bass and degrees; <lyric> number, syllabic, text, extend and elision; text is trimmed, and words are compared in lower case.
  6. What the reader reads as one thing is one item: a mark of a whole chord repeated on each of its notes, a tone written twice in one chord, a hidden staff (for the whole part, wherever the file hid it). What it reads across a bar line goes where it puts it: a direction or a grace note at a measure’s end is at the next one’s start, and a stop at a measure’s start ends the measure before.
  7. Drawing is dropped where it hides in musical elements: a fermata’s “normal” shape (the default), an accent’s up or down, a pedal’s line or sign, a percussion clef’s line, a normal-type equal to the note’s type.

The item tags are the kinds a Difference can have, with four more for what is not in a measure: “header”, “part-list”, “parts” and “measures”.

>>> import io
>>> from openmusickit.io.musicxml.container import load
>>> one = load(io.BytesIO(b'''<score-partwise><part-list><score-part id="A"><part-name>Fl</part-name>
...   </score-part></part-list><part id="A"><measure number="1"><attributes><divisions>1</divisions>
...   </attributes><note><pitch><step>C</step><octave>4</octave></pitch><duration>4</duration>
...   <voice>1</voice><type>whole</type><stem>up</stem></note></measure></part></score-partwise>'''))
>>> two = load(io.BytesIO(b'''<score-partwise><part-list><score-part id="X"><part-name>Fl</part-name>
...   </score-part></part-list><part id="X"><measure number="0" implicit="yes"><attributes>
...   <divisions>4</divisions></attributes><note><pitch><step>C</step><octave>4</octave></pitch>
...   <duration>16</duration><voice>3</voice><type>whole</type></note></measure></part></score-partwise>'''))
>>> compare(canonicalize(one), canonicalize(two))
[]

Attributes

Name Description
NOTE_ITEMS
STOPS
DIRECTION_ITEMS

Classes

Name Description
Difference One thing two canonical documents disagree on: its kind (an item

Functions

Name Description
canonicalize The canonical form of a <score-partwise> element (see the module docstring).
compare The differences between two canonical forms (from canonicalize):