to20.xsl

<?xml version="1.0" encoding="UTF-8"?>

<!--
  MusicXML to20.xsl stylesheet

  Version 4.0

  Copyright © 2004-2021 the Contributors to the MusicXML 
  Specification, published by the W3C Music Notation Community
  Group under the W3C Community Final Specification Agreement 
  (FSA): 

     https://www.w3.org/community/about/agreements/final/

  A human-readable summary is available:

     https://www.w3.org/community/about/agreements/fsa-deed/
-->

<!--
  To20.xsl converts from MusicXML 3.0 to 2.0 for
  compatibility with older products.
-->

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!--
    XML output, with a DOCTYPE referring the partwise DTD.
    Here we use the full Internet URL.
  -->

  <xsl:output method="xml" indent="yes" encoding="UTF-8"
    omit-xml-declaration="no" standalone="no"
    doctype-system="http://www.musicxml.org/dtds/partwise.dtd"
    doctype-public="-//Recordare//DTD MusicXML 2.0 Partwise//EN"/>

  <!--
    For the root, only look for score-partwise. Anything else 
    as a root element gets ignored.
  -->  
  <xsl:template match="/">
    <xsl:apply-templates select="./score-partwise"/>
  </xsl:template>

  <!--
    Transformations that remove post-2.0 elements and 
    attributes.
  -->
  
  <!-- Additions in note.mod -->
  <xsl:template 
    match="notehead-text |
      tied/@dash-length | tied/@space-length |
      slur/@dash-length | slur/@space-length |
      glissando/@dash-length | glissando/@space-length |
      slide/@dash-length | slide/@space-length |
      scoop/@dash-length | scoop/@space-length |
      plop/@dash-length | plop/@space-length |
      doit/@dash-length | doit/@space-length |
      falloff/@dash-length | falloff/@space-length |
      extend/@default-x | extend/@default-y |
      extend/@relative-x | extend/@relative-y |
      extend/@type | rest/@measure | 
      breath-mark/text() | tie/@time-only |
      turn/@slash | delayed-turn/@slash |
      inverted-turn/@slash | delayed-inverted-turn |
      vertical-turn | hole | arrow | handbell | 
      mordent/@approach | inverted-mordent/@approach |
      mordent/@departure | inverted-mordent/@departure |
      notations/@print-object | lyric/@print-object"/>

  <!--
    Remove accidental and accidental-mark elements with 
    new values.
  -->
  <xsl:template 
    match="accidental[. = 'sharp-down' or . = 'sharp-up' or 
        . = 'natural-down' or . = 'natural-up' or 
        . = 'flat-down' or . = 'flat-up' or
        . = 'triple-sharp' or . = 'triple-flat' or
        . = 'slash-quarter-sharp' or . = 'slash-sharp' or
        . = 'slash-flat' or . = 'double-slash-flat' or
        . = 'sharp-1' or . = 'sharp-2' or
        . = 'sharp-3' or . = 'sharp-5' or
        . = 'flat-1' or . = 'flat-2' or
        . = 'flat-3' or . = 'flat-4' or
        . = 'sori' or . = 'koron']"/>

  <xsl:template 
    match="accidental-mark[. = 'sharp-down' or . = 'sharp-up' or 
        . = 'natural-down' or . = 'natural-up' or 
        . = 'flat-down' or . = 'flat-up' or
        . = 'triple-sharp' or . = 'triple-flat' or
        . = 'slash-quarter-sharp' or . = 'slash-sharp' or
        . = 'slash-flat' or . = 'double-slash-flat' or
        . = 'sharp-1' or . = 'sharp-2' or
        . = 'sharp-3' or . = 'sharp-5' or
        . = 'flat-1' or . = 'flat-2' or
        . = 'flat-3' or . = 'flat-4' or
        . = 'sori' or . = 'koron']"/>

  <!-- Remove type elements with new values. -->
  <xsl:template 
    match="type[. = '1024th' or . = '512th' or 
        . = 'maxima']"/> 

  <!-- Remove notehead elements with new values. -->
  <xsl:template 
    match="notehead[. = 'fa up' or . = 'circle dot' or 
          . = 'left triangle' or . = 'rectangle']"/> 

  <!-- Remove beam element with new number values -->
  <xsl:template 
    match="beam[@number[. = '7' or . = '8']]"/>

  <!-- Remove tremolo element with new number values -->
  <xsl:template 
    match="tremolo[. = '7' or . = '8']"/>

  <!-- 
    Remove tied elements with new continue value for the 
    for the type attribute.
  -->
  <xsl:template 
    match="tied[@type[. = 'continue']]"/>

  <!-- Additions in attributes.mod -->

  <xsl:template 
    match="key-accidental | interchangeable | 
      senza-misura/text() | transpose/@number |
      time/@halign | time/@valign |
      time/@separator | clef/@after-barline"/>

  <!-- Remove cancel location attribute with new values -->
  <xsl:template 
    match="cancel/@location[. = 'before-barline']"/>

  <!-- Remove time symbol attribute with new values -->
  <xsl:template 
    match="time/@symbol[. = 'note' or . = 'dotted-note']"/>

  <!-- Remove all but first transpose element -->
  <xsl:template 
    match="attributes/transpose[position() > 1]"/>

  <!-- Remove jianpu clefs altogether -->
  <xsl:template 
    match="clef[//sign[.='jianpu']]"/>

  <!-- Remove part-symbol element with new square value -->
  <xsl:template 
    match="part-symbol[.='square']"/>

  <!-- Additions in barline.mod -->
  <xsl:template 
    match="repeat/@winged"/>

  <!-- Additions in common.mod -->

  <xsl:template 
    match="dynamics/@halign | dynamics/@valign |
      dynamics/@underline | dynamics/@overline |
      dynamics/@line-through | dynamics/@enclosure |
      coda/@halign | coda/@valign |
      segno/@halign | segno/@valign |
      midi-device/@id | play | @xml:space"/>
  
  <!-- 
    Remove display-text and accidental-text enclosure 
    attributes that have values of square, circle, or bracket.
  -->
  <xsl:template 
    match="display-text/@enclosure[. = 'square' or . = 'circle' or
                   . = 'bracket' or . = 'triangle' or
                   . = 'diamond'] "/>
  <xsl:template 
    match="accidental-text/@enclosure[. = 'square' or . = 'circle' or
                    . = 'bracket' or . = 'triangle' or
                    . = 'diamond'] "/>

  <!-- Remove accidental-text elements with new values. -->
  <xsl:template 
    match="accidental-text[. = 'sharp-down' or . = 'sharp-up' or 
        . = 'natural-down' or . = 'natural-up' or 
        . = 'flat-down' or . = 'flat-up' or
        . = 'triple-sharp' or . = 'triple-flat' or
        . = 'slash-quarter-sharp' or . = 'slash-sharp' or
        . = 'slash-flat' or . = 'double-slash-flat' or
        . = 'sharp-1' or . = 'sharp-2' or
        . = 'sharp-3' or . = 'sharp-5' or
        . = 'flat-1' or . = 'flat-2' or
        . = 'flat-3' or . = 'flat-4' or
        . = 'sori' or . = 'koron']"/>

  <!-- Additions in direction.mod -->

  <xsl:template 
    match="rehearsal/@justify | rehearsal/@halign |
      rehearsal/@valign | rehearsal/@letter-spacing |
      rehearsal/@line-height | 
      wedge/@line-type | wedge/@niente |
      wedge/@dash-length | wedge/@space-length |
      bracket/@dash-length | bracket/@space-length |
      dashes/@dash-length | dashes/@space-length |
      octave-shift/@dash-length | octave-shift/@space-length |
      accordion-registration/@halign | accordion-registration/@valign |
      damp/@halign | damp/@valign |
      damp-all/@halign | damp-all/@valign |
      eyeglasses/@halign | eyeglasses/@valign |
      harp-pedals/@halign | harp-pedals/@valign |
      pedal/@halign | pedal/@valign | pedal/@sign |
      other-direction/@halign | other-direction/@valign |
      measure-numbering/@halign | measure-numbering/@valign |
      metronome/@halign | metronome/@valign |
      metronome/@justify | frame/@unplayed |
      degree-value/@symbol | sound/midi-device"/>

  <!-- 
    For safety, remove entire direction that has a new 
    continue value for the type attribute.
  -->
  <xsl:template 
    match="direction[direction-type[wedge[@type[. = 'continue']]]]"/>

  <xsl:template 
    match="direction[direction-type[dashes[@type[. = 'continue']]]]"/>

  <xsl:template 
    match="direction[direction-type[bracket[@type[. = 'continue']]]]"/>

  <xsl:template 
    match="direction[direction-type[octave-shift[@type[. = 'continue']]]]"/>

  <xsl:template 
    match="direction[direction-type[pedal[@type[. = 'continue']]]]"/>

  <!-- 
    For safety, remove entire direction that has a new
    MusicXML 3.0 direction-type child.
  -->
  <xsl:template 
    match="direction[direction-type[string-mute]]"/>

  <xsl:template 
    match="direction[direction-type[principal-voice]]"/>

  <xsl:template 
    match="direction[direction-type[percussion]]"/>

  <!-- 
    Remove rehearsal enclosure attributes that have values
    of rectangle, oval, bracket, triangle, or diamond.
  -->
  <xsl:template 
    match="rehearsal/@enclosure[. = 'rectangle' or . = 'oval' or
              . = 'bracket' or . = 'triangle' or
              . = 'diamond'] "/>

  <!-- 
    Remove words enclosure attributes that have values
    of square, circle, bracket, triangle, or diamond.
  -->
  <xsl:template 
    match="words/@enclosure[. = 'square' or . = 'circle' or
              . = 'bracket' or . = 'triangle' or
              . = 'diamond'] "/>

  <!-- Remove metronome-beam element with new number values -->
  <xsl:template 
    match="metronome-beam[@number[. = '7' or . = '8']]"/>

  <!-- Additions in layout.mod -->
  <xsl:template 
    match="distance | system-dividers"/>
  
  <!-- Additions in score.mod -->

  <xsl:template 
    match="credit-type | instrument-sound | virtual-instrument"/>
  
  <!-- Remove all but first midi-device element -->
  <xsl:template 
    match="score-part/midi-device[position() > 1]"/>

  <!-- Remove group-symbol element with new square value -->
  <xsl:template 
    match="group-symbol[.='square']"/>

  <!-- 
    Remove credit-words enclosure attributes that have
    values of square, circle, bracket, triangle, or diamond.
  -->
  <xsl:template 
    match="credit-words/@enclosure[. = 'square' or . = 'circle' or
                   . = 'bracket' or . = 'triangle' or
                   . = 'diamond'] "/>

  <!--
    Convert score version attribute to 2.0
  -->
  <xsl:template 
    match="score-partwise/@version">
    <xsl:attribute name="version">2.0</xsl:attribute>
  </xsl:template>

  <!--
    The identity transformation. Used for everything that
    stays the same in 2.0.
  -->

  <xsl:template match="text()">
    <xsl:value-of select="." />
  </xsl:template>
  
  <!--
    Whitespace within an xsl:copy could cause problems with 
    empty elements.
  -->
  <xsl:template match="*|@*|comment()|processing-instruction()">
    <xsl:copy><xsl:apply-templates
        select="*|@*|comment()|processing-instruction()|text()"
    /></xsl:copy>
  </xsl:template>

</xsl:stylesheet>