Notation input / output

  • Output: maelzel can export musicxml, lilypond and MIDI and generate pdf, png and svg files from the rendered notation.

  • Input: At the moment the only supported format for input is musicxml. Other formats, like MIDI or MEI, can be easily converted to musicxml using external tools and imported in that way.

[1]:
from maelzel.core import *
import os
from IPython.display import Image

This is the output produced by MuseScore when importing the file assets/schoenberg-op19-II.musicxml

[2]:
Image("assets/schoenberg-op19.png", width=800)
[2]:
../_images/notebooks_musicxmlio_3_0.png

In order to parse a musicxml score we use Score.read (a classmethod). At the moment there are some musicxml elements which are not fully imported. In particular, multiple voices per part or multistaff parts are still not well supported. This can be seen in the example below, were the upper voice in the upper staff (containing only one chord at the end) is extracted to its own staff.

The Score.read method parses the musicxml file and generates a maelzel.core Score. A Score and its contents are not quantized: durations are expressed in terms of quarternotes and are anware of any tuplet structure. They are quantized when represented as notation.

If you need to import a musicxml score with all its quantization information you can use QuantizedScore.read. This will be discussed later.

[2]:
s = Score.read("assets/schoenberg-op19-II.musicxml")
s.show(backend='lilypond')
[maelzel.core:musicxmlparser.py:429:_parseNote:ERROR] No start spanner found for key slur-1
../_images/notebooks_musicxmlio_5_1.png

Musicxml rendering depends heavily on the rendering application. This, for example, is the output produced by MuseScore 4

[3]:
s.show(backend='musicxml')
../_images/notebooks_musicxmlio_7_0.png

Export to musicxml

Exporting to musicxml (as well as any other supported format) is done via the .write method

[14]:
s.write("~/tmp/schoenberg.musicxml")

! head -n 200 ~/tmp/schoenberg.musicxml
<?xml version="1.0" ?>
<!DOCTYPE score-partwise
  PUBLIC '-//Recordare//DTD MusicXML 4.0 Partwise//EN'
  'http://www.musicxml.org/dtds/partwise.dtd'>
<score-partwise version="4.0">
  <defaults>
    <scaling>
      <millimeters>5.291666666666667</millimeters>
      <tenths>40</tenths>
    </scaling>
    <page-layout>
      <page-height>11880</page-height>
      <page-width>8400</page-width>
    </page-layout>
  </defaults>
  <part-list>
    <score-part id="P1">
      <part-name>MusicXML Part/1</part-name>
    </score-part>
    <score-part id="P2">
      <part-name>MusicXML Part/3</part-name>
    </score-part>
    <score-part id="P3">
      <part-name>MusicXML Part/2</part-name>
    </score-part>
  </part-list>
  <part id="P1">
    <measure number="1">
      <attributes>
        <divisions>1</divisions>
        <time>
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
        <clef>
          <sign>G</sign>
          <line>2</line>
        </clef>
        <staff-details>
          <staff-size scaling="100">83</staff-size>
        </staff-details>
      </attributes>
      <direction placement="above">
        <direction-type>
          <metronome>
            <beat-unit>quarter</beat-unit>
            <per-minute>60</per-minute>
          </metronome>
        </direction-type>
      </direction>
      <direction placement="above">
        <direction-type>
          <words>Langsam (</words>
        </direction-type>
      </direction>
      <note>
        <rest/>
        <duration>4</duration>
        <type>whole</type>
      </note>
    </measure>
    <measure number="2">
      <attributes>
        <divisions>2</divisions>
        <staff-details>
          <staff-size scaling="100">83</staff-size>
        </staff-details>
      </attributes>
      <direction placement="below">
        <direction-type>
          <dynamics>
            <mf/>
          </dynamics>
        </direction-type>
      </direction>
      <note>
        <rest/>
        <duration>6</duration>
        <type>half</type>
        <dot/>
      </note>
      <direction placement="below">
        <direction-type>
          <wedge type="crescendo" niente="no"/>
        </direction-type>
      </direction>
      <note>
        <pitch>
          <step>B</step>
          <alter>0</alter>
          <octave>5</octave>
        </pitch>
        <duration>1</duration>
        <type>eighth</type>
        <notations>
          <slur number="1" type="start" line-type="solid"/>
        </notations>
      </note>
      <note>
        <chord/>
        <pitch>
          <step>D</step>
          <alter>0</alter>
          <octave>6</octave>
        </pitch>
        <duration>1</duration>
        <type>eighth</type>
      </note>
      <note>
        <pitch>
          <step>F</step>
          <alter>1</alter>
          <octave>4</octave>
        </pitch>
        <duration>1</duration>
        <type>eighth</type>
      </note>
    </measure>
    <measure number="3">
      <attributes>
        <staff-details>
          <staff-size scaling="100">83</staff-size>
        </staff-details>
      </attributes>
      <direction>
        <direction-type>
          <wedge type="stop"/>
        </direction-type>
      </direction>
      <direction placement="above">
        <direction-type>
          <words font-style="italic">espress.</words>
        </direction-type>
      </direction>
      <direction placement="below">
        <direction-type>
          <dynamics>
            <p/>
          </dynamics>
        </direction-type>
      </direction>
      <note>
        <pitch>
          <step>D</step>
          <alter>1</alter>
          <octave>4</octave>
        </pitch>
        <duration>3</duration>
        <type>quarter</type>
        <dot/>
      </note>
      <direction placement="below">
        <direction-type>
          <wedge type="crescendo" niente="no"/>
        </direction-type>
      </direction>
      <note>
        <pitch>
          <step>A</step>
          <alter>0</alter>
          <octave>3</octave>
        </pitch>
        <duration>1</duration>
        <type>eighth</type>
      </note>
      <direction>
        <direction-type>
          <wedge type="stop"/>
        </direction-type>
      </direction>
      <direction placement="below">
        <direction-type>
          <wedge type="diminuendo" niente="no"/>
        </direction-type>
      </direction>
      <note>
        <pitch>
          <step>C</step>
          <alter>0</alter>
          <octave>4</octave>
        </pitch>
        <duration>2</duration>
        <type>quarter</type>
      </note>
      <direction>
        <direction-type>
          <wedge type="stop"/>
        </direction-type>
      </direction>
      <note>
        <pitch>
          <step>A</step>
          <alter>-1</alter>
          <octave>3</octave>
        </pitch>
        <duration>2</duration>
        <type>quarter</type>
        <notations>
          <slur number="1" type="stop" line-type="solid"/>
        </notations>

MusicXML vs Lilypond

Most of the features supported by lilypond are supported by musicxml as well. For example, microtones, complex glissandi, gracenotes, as well as notehead shapes, articulations and spanners (slurs, hairpins, etc) are all exported correctly and interpreted correctly by all tested rendering applications (MuseScore, Finale, Dorico). There are some features which are supported by musicxml itself but not by some of these applications. In particular, nested tuplets are not supported by MuseScore at the moment. For that reason, the default is not to generate nested tuplets in the first place when rendering musicxml.

Other minor features not supported by some of the musicxml renderers include notehead sizes, some element colors and probably many more which have not been tested yet.

[4]:
v = Voice([
    "4C+20:1/3:gliss",
    "3A:0:stemless",
    "4Eb-25:1/15:slur",
    "4D:1/15",
    "4F+:3/15:gliss",
    "4G+13:1/3:~slur",
])
v.show(backend='lilypond')
../_images/notebooks_musicxmlio_11_0.png

As said before, when rendered via musicxml using MuseScore, nested tuplets are disabled. In this case, with the default quantization complexity (‘high’), the quantization is not perfectly accurate. The first eighth note is divided into 9, which results in a duration of 1/18 of a quarternote for the 2nd and 3rd notes, instead of 1/15. This is innacurate by 1/90th of a quarternote.

[5]:
v.show(backend='musicxml')
../_images/notebooks_musicxmlio_13_0.png

If we instead use the highest quantization complexity, then a correct subdivision is found while still avoiding nested tuplets

Notice that it is possible to disable the display of cents as annotations via the config

[6]:
with CoreConfig() as cfg:
    cfg['quant.complexity'] = 'highest'
    cfg['show.centsDeviationAsTextAnnotation'] = False
    v.show(backend='musicxml')
../_images/notebooks_musicxmlio_15_0.png
[ ]: