rism-digital / verovio

🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)
https://www.verovio.org
GNU Lesser General Public License v3.0
676 stars 184 forks source link

MusicXML: Slur matching does not take orientation into account #1507

Closed yeonoson closed 4 years ago

yeonoson commented 4 years ago

The slurs in the attached MusicXML extract are not correctly rendered. The actual rendering looks like this:

actual-rendering

The expected rendering should be:

expected-rendering

The MusicXML is exported by Sibelius. If I import it to MuseScore the rendering is also broken (only 2 / 3 slurs are shown and one slur is also incorrect).

The problem is that the first lower slur (orientation="under") and the upper slur (orientation="over") do not have a number attribute, thus 1 is implied. This way the start of the upper slur is matched with the stop of the lower slur. It would be possible to fix this by also using the orientation when matching start and stop of slurs in the functions MusicXmlInput::OpenSlur and MusicXmlInput::CloseSlur. Would that make sense to you?

<?xml version="1.0" encoding="UTF-8"?>
<score-partwise version="3.0">
 <part-list>
  <score-part id="P1">
   <identification>
    <miscellaneous>
     <miscellaneous-field name="show-rhythms">false</miscellaneous-field>
    </miscellaneous>
   </identification>   
   <part-abbreviation-display>
    <display-text>Dis.</display-text>
   </part-abbreviation-display>
   <score-instrument id="P1-I1">
    <instrument-name>Soprano</instrument-name>
    <instrument-sound>keyboard.piano.grand</instrument-sound>
    <solo/>
    <virtual-instrument>
     <virtual-library>General MIDI</virtual-library>
     <virtual-name>Acoustic Piano</virtual-name>
    </virtual-instrument>
   </score-instrument>
  </score-part>  
 </part-list>
 <part id="P1">  
  <!--============== Part: P1, Measure: 3 ==============-->
  <measure number="1" width="248">
   <print new-page="yes">
    <system-layout>
     <system-margins>
      <left-margin>22</left-margin>
      <right-margin>0</right-margin>
     </system-margins>
     <top-system-distance>301</top-system-distance>
    </system-layout>
   </print>
   <attributes>
    <divisions>256</divisions>
    <key color="#000000">
     <fifths>3</fifths>
     <mode>major</mode>
    </key>
    <time symbol="common" color="#000000">
     <beats>4</beats>
     <beat-type>4</beat-type>
    </time>
    <staves>1</staves>
    <clef number="1" color="#000000">
     <sign>G</sign>
     <line>2</line>
    </clef>
    <staff-details number="1" print-object="yes"/>
   </attributes>
   <note color="#000000" default-x="15" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="74" default-y="10">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="132" default-y="10">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>384</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <dot/>
    <stem>up</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="start" orientation="over"/>
    </notations>
   </note>
   <note color="#000000" default-x="219" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <backup>
    <duration>1024</duration>
   </backup>
   <note color="#000000" default-x="15" default-y="-72">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
    <beam number="1">begin</beam>
    <notations>
     <slur color="#000000" type="start" orientation="under"/>
    </notations>
   </note>
   <note color="#000000" default-x="45" default-y="-72">
    <pitch>
     <step>D</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
    <beam number="1">end</beam>
    <notations>
     <slur color="#000000" type="stop" orientation="under"/>
    </notations>
   </note>
   <note color="#000000" default-x="74" default-y="-77">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="132" default-y="-62">
    <pitch>
     <step>F</step>
     <alter>1</alter>
     <octave>4</octave>
    </pitch>
    <duration>512</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>half</type>
    <stem>down</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="start" orientation="under" number="2"/>
    </notations>
   </note>
  </measure>
  <!--============== Part: P1, Measure: 4 ==============-->
  <measure number="2" width="252">
   <note color="#000000" default-x="15" default-y="17">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>5</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="stop" orientation="over"/>
    </notations>
   </note>
   <note color="#000000" default-x="73" default-y="17">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>5</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="131" default-y="12">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
    <beam number="1">begin</beam>
    <notations>
     <slur color="#000000" type="start" orientation="over"/>
    </notations>
   </note>
   <note color="#000000" default-x="165" default-y="12">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
    <beam number="1">end</beam>
    <notations>
     <slur color="#000000" type="stop" orientation="over"/>
    </notations>
   </note>
   <note color="#000000" default-x="194" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <backup>
    <duration>1024</duration>
   </backup>
   <note color="#000000" default-x="15" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="73" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="stop" orientation="under" number="2"/>
    </notations>
   </note>
   <note color="#000000" default-x="131" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>384</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <dot/>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="223" default-y="-72">
    <pitch>
     <step>D</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
  </measure>
 </part>
</score-partwise>
craigsapp commented 4 years ago

The problem is that the slur@number parameter is being ignored:

<slur color="#000000" type="start" orientation="under" number="2"/><slur color="#000000" type="stop" orientation="under" number="2"/>

If there is a number parameter on a slur start/end, then it must be matched to the first next/previous slur endpoint that has the same number value.

The MusicXML->Humdrum->MEI importer does this correctly:

 verovio -f musicxml-hum -at mei file.xml

file.mei will contain:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="https://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2020-06-18T14:20:13" version="3.0.0-dev-e82b916-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001662650179">
    <score xml:id="score-0000000011634937">
     <scoreDef xml:id="scoredef-0000000639419673">
      <staffGrp xml:id="staffgrp-0000001077304452">
       <staffDef xml:id="staffdef-0000000826187873" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <keySig xml:id="keysig-L4F1" pname="a" mode="major" sig="3s" />
        <meterSig xml:id="metersig-L6F1" count="4" sym="common" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="1">
       <staff xml:id="staff-0000000227766677" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L10F1" dur="4" oct="4" pname="b" accid.ges="n" />
         <note xml:id="note-L12F1" dur="4" oct="4" pname="a" accid.ges="n" />
         <note xml:id="note-L13F1" dots="1" dur="4" oct="4" pname="a" accid.ges="n" />
         <note xml:id="note-L14F1" dur="8" oct="4" pname="b" accid.ges="n" />
        </layer>
        <layer xml:id="layer-L10F2N2" n="2">
         <beam xml:id="beam-L10F2-L11F2">
          <note xml:id="note-L10F2" dur="8" oct="4" pname="e" accid.ges="n" />
          <note xml:id="note-L11F2" dur="8" oct="4" pname="d" accid.ges="n" />
         </beam>
         <note xml:id="note-L12F2" dur="4" oct="4" pname="c" stem.len="5.500000" accid.ges="s" />
         <note xml:id="note-L13F2" dur="2" oct="4" pname="f" stem.len="5.500000" accid.ges="s" />
        </layer>
       </staff>
       <slur xml:id="slur-L10F2-L11F2" staff="1" startid="#note-L10F2" endid="#note-L11F2" />
       <slur xml:id="slur-L13F1-L16F1" staff="1" startid="#note-L13F1" endid="#note-L16F1" />
       <slur xml:id="slur-L13F2-L17F2" staff="1" startid="#note-L13F2" endid="#note-L17F2" />
      </measure>
      <measure xml:id="measure-L15" n="2">
       <staff xml:id="staff-L15F1N1" n="1">
        <layer xml:id="layer-L15F1N1" n="1">
         <note xml:id="note-L16F1" dur="4" oct="5" pname="c" stem.len="6.500000" accid.ges="s" />
         <note xml:id="note-L17F1" dur="4" oct="5" pname="c" stem.len="6.500000" accid.ges="s" />
         <beam xml:id="beam-L18F1-L19F1">
          <note xml:id="note-L18F1" dur="8" oct="4" pname="b" accid.ges="n" />
          <note xml:id="note-L19F1" dur="8" oct="4" pname="a" accid.ges="n" />
         </beam>
         <note xml:id="note-L20F1" dur="4" oct="4" pname="b" accid.ges="n" />
        </layer>
        <layer xml:id="layer-L15F2N2" n="2">
         <note xml:id="note-L16F2" dur="4" oct="4" pname="e" stem.len="5.500000" accid.ges="n" />
         <note xml:id="note-L17F2" dur="4" oct="4" pname="e" stem.len="5.500000" accid.ges="n" />
         <note xml:id="note-L18F2" dots="1" dur="4" oct="4" pname="e" stem.len="5.500000" accid.ges="n" />
         <note xml:id="note-L21F2" dur="8" oct="4" pname="d" accid.ges="n" />
        </layer>
       </staff>
       <slur xml:id="slur-L18F1-L19F1" staff="1" startid="#note-L18F1" endid="#note-L19F1" />
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
yeonoson commented 4 years ago

The MusicXML import already considers the slur@number attribute. The rendering is correct if the input file has number="3" for both slur-elements with orientation="over".

Let me try to explain the problem again: The start-slur with orientation="over" has no number-attribute (which implies number="1" in the MusicXML-Import). This start-slur is matched to the next end-slur with number="1" (or without a number), which is the first lower end-slur with orientation="under" and without a number-attribute. This happens because the upper voice is notated first, followed by a <backup> which allows to notate the second voice afterwards. The real end-slur with orientation="over" follows in the next measure. This will then be matched to the first start-slur in the lower voice of the first measure.

Thus, a solution would be to not only rely on the number-attribute, but to also consider the orientation-attribute. The same should also be done for the placement-attribute, as already done in the function InferCurvedir.

craigsapp commented 4 years ago

The MusicXML import already considers the slur@number attribute. The rendering is correct if the input file has number="3" for both slur-elements with orientation="over".

OK. The problem is then related to the precedence of linking the endpoints. Linking of slur endpoints by @number should be done first. This should be implemented with a two-dimensional array. The first dimension of this array should be a map, and the second dimension should be a vector:

      map<int, vector<xml_node>> slurStartBuffer;

The index into the map is the @number value, using 1 as the default when there is no @number parameter. Then for each map entry, there is a vector that stores the slur starts for that @number.

When a slur end is encountered with the same @number as a slur start, the last stored slur start for @number is matched to the current slur end. This matching process probably has to be done temporally rather than within the data serialization of the notes in different layers/voices (I am fairly sure of this requirement, but it would need to be verified).

There is also the consideration of hanging slurs, where not all slur endpoints match up. For example if there is a slur start that is still unmatched after processing the score, then this means that the slur endpoint should be attached to the last measure in the MEI data. Likewise, if there is a slur endpoint that does not have a matching start, then the start of the slur should be @tstamp="0" in the first measure of the MEI data. I do not know of any notation editor that outputs hanging slur: if they are present in the data, it could mean some sort of data error instead of a hanging slur.

The @orientation parameter should not be used to link the slur endpoints. This is an educated guess that will be correct most of the time, but not for complicated cases where there are multiple slurs with the same orientation. There are also cases for cross-staff slurs where the orientation of a slur may be different at the start and end points.

Here is a schematic of how the data is serialized in the example data, following along the red line superimposed on the notation:

Screen Shot 2020-06-18 at 3 38 55 PM

Notice that Slur C is marked as @number="2". This is because it starts at the same time as Slur A, so the endpoints need to be matched properly by giving them different @numbers.

Here is the order of the slur elements in the example data (see also below):

<slur type="start" orientation="over"/> <!-- slur A start @M1B3L1 -->
<slur type="start" orientation="under"/> <!-- slur B start @M1B1L2 -->
<slur type="stop" orientation="under"/> <!-- slur B stop @M1B1.5L2 -->
<slur type="start" orientation="under" number="2"/> <!-- slur C start @M1B3L2 -->
<slur type="stop" orientation="over"/> <!-- slur A stop @M2B1L1 -->
<slur type="start" orientation="over"/> <!-- slur D start @M2B3L1 -->
<slur type="stop" orientation="over"/> <!-- slur D stop @M2B3.5L1 -->
<slur type="stop" orientation="under" number="2"/> <!-- slur C stop @M2B2L2 -->

M2B3L1 means measure 2, beat 3, layer 1.

Slur A and Slur B endpoints are being mixed up in the current MusicXML conversion. This seems to indicate that the converter is storing the slur starts in a FIFO buffer (first in, first out) serialized by the data order, but a LIFO (last in, first out) serialized by time order is needed. It needs to be LIFO in any case regardless of whether the linking should be done in data or time serialization.

Here is the MusicXML data, where I manually matched the slur endpoints:


<?xml version="1.0" encoding="UTF-8"?>
<score-partwise version="3.0">
 <part-list>
  <score-part id="P1">
   <identification>
    <miscellaneous>
     <miscellaneous-field name="show-rhythms">false</miscellaneous-field>
    </miscellaneous>
   </identification>   
   <part-abbreviation-display>
    <display-text>Dis.</display-text>
   </part-abbreviation-display>
   <score-instrument id="P1-I1">
    <instrument-name>Soprano</instrument-name>
    <instrument-sound>keyboard.piano.grand</instrument-sound>
    <solo/>
    <virtual-instrument>
     <virtual-library>General MIDI</virtual-library>
     <virtual-name>Acoustic Piano</virtual-name>
    </virtual-instrument>
   </score-instrument>
  </score-part>  
 </part-list>
 <part id="P1">  
  <!--============== Part: P1, Measure: 3 ==============-->
  <measure number="1" width="248">
   <print new-page="yes">
    <system-layout>
     <system-margins>
      <left-margin>22</left-margin>
      <right-margin>0</right-margin>
     </system-margins>
     <top-system-distance>301</top-system-distance>
    </system-layout>
   </print>
   <attributes>
    <divisions>256</divisions>
    <key color="#000000">
     <fifths>3</fifths>
     <mode>major</mode>
    </key>
    <time symbol="common" color="#000000">
     <beats>4</beats>
     <beat-type>4</beat-type>
    </time>
    <staves>1</staves>
    <clef number="1" color="#000000">
     <sign>G</sign>
     <line>2</line>
    </clef>
    <staff-details number="1" print-object="yes"/>
   </attributes>
   <note color="#000000" default-x="15" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="74" default-y="10">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="132" default-y="10">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>384</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <dot/>
    <stem>up</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="start" orientation="over"/> <!-- slur A start @M1B3 -->
    </notations>
   </note>
   <note color="#000000" default-x="219" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <backup>
    <duration>1024</duration>
   </backup>
   <note color="#000000" default-x="15" default-y="-72">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
    <beam number="1">begin</beam>
    <notations>
     <slur color="#000000" type="start" orientation="under"/> <!-- slur B start @M1B1 -->
    </notations>
   </note>
   <note color="#000000" default-x="45" default-y="-72">
    <pitch>
     <step>D</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
    <beam number="1">end</beam>
    <notations>
     <slur color="#000000" type="stop" orientation="under"/> <!-- slur B stop @M1B1.5 -->
    </notations>
   </note>
   <note color="#000000" default-x="74" default-y="-77">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="132" default-y="-62">
    <pitch>
     <step>F</step>
     <alter>1</alter>
     <octave>4</octave>
    </pitch>
    <duration>512</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>half</type>
    <stem>down</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="start" orientation="under" number="2"/> <!-- slur C start @M1B3 -->
    </notations>
   </note>
  </measure>
  <!--============== Part: P1, Measure: 4 ==============-->
  <measure number="2" width="252">
   <note color="#000000" default-x="15" default-y="17">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>5</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="stop" orientation="over"/> <!-- slur A stop @M2B1 -->
    </notations>
   </note>
   <note color="#000000" default-x="73" default-y="17">
    <pitch>
     <step>C</step>
     <alter>1</alter>
     <octave>5</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="131" default-y="12">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
    <beam number="1">begin</beam>
    <notations>
     <slur color="#000000" type="start" orientation="over"/> <!-- slur D start @M2B3 -->
    </notations>
   </note>
   <note color="#000000" default-x="165" default-y="12">
    <pitch>
     <step>A</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>eighth</type>
    <stem>up</stem>
    <staff>1</staff>
    <beam number="1">end</beam>
    <notations>
     <slur color="#000000" type="stop" orientation="over"/> <!-- slur D stop @M2B3.5 -->
    </notations>
   </note>
   <note color="#000000" default-x="194" default-y="15">
    <pitch>
     <step>B</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>1</voice>
    <type>quarter</type>
    <stem>up</stem>
    <staff>1</staff>
   </note>
   <backup>
    <duration>1024</duration>
   </backup>
   <note color="#000000" default-x="15" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="73" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>256</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <stem>down</stem>
    <staff>1</staff>
    <notations>
     <slur color="#000000" type="stop" orientation="under" number="2"/> <!-- slur C stop @M2B2 -->
    </notations>
   </note>
   <note color="#000000" default-x="131" default-y="-67">
    <pitch>
     <step>E</step>
     <octave>4</octave>
    </pitch>
    <duration>384</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>quarter</type>
    <dot/>
    <stem>down</stem>
    <staff>1</staff>
   </note>
   <note color="#000000" default-x="223" default-y="-72">
    <pitch>
     <step>D</step>
     <octave>4</octave>
    </pitch>
    <duration>128</duration>
    <instrument id="P1-I1"/>
    <voice>2</voice>
    <type>eighth</type>
    <stem>down</stem>
    <staff>1</staff>
   </note>
  </measure>
 </part>
</score-partwise>

The MusicXML import already considers the slur@number attribute. The rendering is correct if the input file has number="3" for both slur-elements with orientation="over".

Yes, the @number has precedence, since slur endpoints with different @numbers cannot be linked to each other.

yeonoson commented 4 years ago

I like your idea of using a FIFO buffer for slurs, as it improves the matching of slur-start and slur-end within a single layer if no number element is given. Using a FIFO buffer also works for the original example I reported in this issue.

But with multiple layers there are other situations in which this does not work. This is because of the <backup> mechanism (used by MusicXML to notate different layers) that leads to time-jumps within a measure. This is also shown very well by the red line you used to annotate the rendering.

To demonstrate the problem I slightly changed the slurs in the original example:

rendering-adjusted-slurs

I marked the order of the slur-start and slur-end elements in the MusicXML. So now slur elements 1 and 3 belong together, but by using a FIFO 1 and 4 would be matched. The same problem exists for elements 2 and 4 which actually belong together, but instead elements 2 and 3 would be matched.

The conversion to MEI would be correct if the orientation (or placement) is taken into account, even if the number attribute is only unique per layer. Using the orientation allows to differentiate between two layers. And it also makes sense in a logical way because a slur-start with orientation="over" should never be matched with a slur-end with orientation="under"

craigsapp commented 4 years ago

You need to supply MusicXML data for the new example, generated by a notation editor that exports MusicXML data (rather than a hand-edited example).

craigsapp commented 4 years ago

The MusicXML documentation for slur@type says that the linking of start and stop endpoints of slurs must be assigned temporally, irrespective of their order in the data serialization:

http://usermanuals.musicxml.com/MusicXML/MusicXML.htm#ST-MusicXML-start-stop-continue.htm

The values of start, stop, and continue refer to how an element appears in musical score order, not in MusicXML document order. An element with a stop attribute may precede the corresponding element with a start attribute within a MusicXML document. This is particularly common in multi-staff music. For example, the stopping point for a slur may appear in staff 1 before the starting point for the slur appears in staff 2 later in the document.

craigsapp commented 4 years ago

None of Finale/Sibelius/MuseScore can handle cases where slurs are nested or crossing unless slur@number is used. In other words, there can be only one slur of a given number active at any point in the score (presumably by time order as opposed to data order). Here is an example that I created in Finale, and @number is given for all slurs:

Screen Shot 2020-06-19 at 6 26 35 PM

Sibelius:

Screen Shot 2020-06-19 at 6 43 55 PM

N.B.: the orientation and/or placement of the last slur is lost.

MuseScore:

Screen Shot 2020-06-19 at 6 28 39 PM

Here is the MusicXML data exported from Finale:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
  <identification>
    <encoding>
      <software>Finale v26.3 for Mac</software>
      <encoding-date>2020-06-19</encoding-date>
      <supports attribute="new-system" element="print" type="yes" value="yes"/>
      <supports attribute="new-page" element="print" type="yes" value="yes"/>
      <supports element="accidental" type="yes"/>
      <supports element="beam" type="yes"/>
      <supports element="stem" type="yes"/>
    </encoding>
  </identification>
  <defaults>
    <scaling>
      <millimeters>7.2319</millimeters>
      <tenths>40</tenths>
    </scaling>
    <page-layout>
      <page-height>1545</page-height>
      <page-width>1194</page-width>
      <page-margins type="both">
        <left-margin>70</left-margin>
        <right-margin>70</right-margin>
        <top-margin>88</top-margin>
        <bottom-margin>88</bottom-margin>
      </page-margins>
    </page-layout>
    <system-layout>
      <system-margins>
        <left-margin>0</left-margin>
        <right-margin>0</right-margin>
      </system-margins>
      <system-distance>121</system-distance>
      <top-system-distance>70</top-system-distance>
    </system-layout>
    <appearance>
      <line-width type="stem">0.918</line-width>
      <line-width type="beam">5</line-width>
      <line-width type="staff">0.918</line-width>
      <line-width type="light barline">0.918</line-width>
      <line-width type="heavy barline">5</line-width>
      <line-width type="leger">1.0807</line-width>
      <line-width type="ending">0.957</line-width>
      <line-width type="wedge">0.918</line-width>
      <line-width type="enclosure">0.918</line-width>
      <line-width type="tuplet bracket">0.918</line-width>
      <note-size type="grace">60</note-size>
      <note-size type="cue">60</note-size>
      <distance type="hyphen">120</distance>
      <distance type="beam">7.5</distance>
    </appearance>
    <music-font font-family="Maestro,engraved" font-size="20.5"/>
    <word-font font-family="Times New Roman" font-size="10.25"/>
  </defaults>
  <credit page="1">
    <credit-type>part name</credit-type>
    <credit-words default-x="70" default-y="1453" font-size="12" valign="top">Score</credit-words>
  </credit>
  <part-list>
    <score-part id="P1">
      <part-name print-object="no">MusicXML Part</part-name>
      <score-instrument id="P1-I1">
        <instrument-name>ARIA Player</instrument-name>
      </score-instrument>
      <midi-instrument id="P1-I1">
        <midi-channel>1</midi-channel>
        <midi-bank>15489</midi-bank>
        <midi-program>1</midi-program>
        <volume>80</volume>
        <pan>0</pan>
      </midi-instrument>
    </score-part>
  </part-list>
  <!--=========================================================-->
  <part id="P1">
    <measure number="1" width="298">
      <print>
        <system-layout>
          <system-margins>
            <left-margin>70</left-margin>
            <right-margin>0</right-margin>
          </system-margins>
          <top-system-distance>211</top-system-distance>
        </system-layout>
        <measure-numbering>system</measure-numbering>
      </print>
      <attributes>
        <divisions>2</divisions>
        <key>
          <fifths>0</fifths>
          <mode>major</mode>
        </key>
        <time>
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
        <clef>
          <sign>G</sign>
          <line>2</line>
        </clef>
      </attributes>
      <sound tempo="120"/>
      <note default-x="83">
        <pitch>
          <step>C</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="99" bezier-y="42" default-x="8" default-y="-5" number="1" placement="above" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="2" width="228">
      <note default-x="13">
        <pitch>
          <step>D</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="53" bezier-y="-26" default-x="8" default-y="-55" number="2" placement="below" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="3" width="228">
      <note default-x="13">
        <pitch>
          <step>E</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-52" bezier-y="-28" default-x="8" default-y="-50" number="2" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="4" width="228">
      <note default-x="13">
        <pitch>
          <step>G</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-101" bezier-y="36" default-x="8" default-y="15" number="1" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="5" width="292">
      <print new-system="yes">
        <system-layout>
          <system-distance>114</system-distance>
        </system-layout>
      </print>
      <note default-x="52">
        <pitch>
          <step>C</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="90" bezier-y="30" default-x="8" default-y="-5" number="1" placement="above" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="6" width="254">
      <note default-x="13">
        <pitch>
          <step>D</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="94" bezier-y="-42" default-x="9" default-y="-60" number="2" placement="below" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="7" width="254">
      <note default-x="13">
        <pitch>
          <step>E</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-87" bezier-y="38" default-x="8" default-y="-27" number="1" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="8" width="254">
      <note default-x="13">
        <pitch>
          <step>G</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-84" bezier-y="-60" default-x="8" default-y="-10" number="2" type="stop"/>
        </notations>
      </note>
      <barline location="right">
        <bar-style>light-heavy</bar-style>
      </barline>
    </measure>
  </part>
  <!--=========================================================-->
</score-partwise>

When I remove the @number attributes from the slurs, all programs cannot parse the slur endpoints correctly. In theory the first four measures or the second four measures should be correct, but both sets of measures are both incorrect in all editors:

Screen Shot 2020-06-19 at 6 28 15 PM

Finale also gives this error window:

Screen Shot 2020-06-19 at 6 27 52 PM

Sibelius:

Screen Shot 2020-06-19 at 6 44 16 PM

Loss of orientation and/or placement on one slur, however.

Musescore:

Screen Shot 2020-06-19 at 6 28 54 PM

Here is the MusicXML data for this test (the original data with the slur@number attributes removed):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
  <identification>
    <encoding>
      <software>Finale v26.3 for Mac</software>
      <encoding-date>2020-06-19</encoding-date>
      <supports attribute="new-system" element="print" type="yes" value="yes"/>
      <supports attribute="new-page" element="print" type="yes" value="yes"/>
      <supports element="accidental" type="yes"/>
      <supports element="beam" type="yes"/>
      <supports element="stem" type="yes"/>
    </encoding>
  </identification>
  <defaults>
    <scaling>
      <millimeters>7.2319</millimeters>
      <tenths>40</tenths>
    </scaling>
    <page-layout>
      <page-height>1545</page-height>
      <page-width>1194</page-width>
      <page-margins type="both">
        <left-margin>70</left-margin>
        <right-margin>70</right-margin>
        <top-margin>88</top-margin>
        <bottom-margin>88</bottom-margin>
      </page-margins>
    </page-layout>
    <system-layout>
      <system-margins>
        <left-margin>0</left-margin>
        <right-margin>0</right-margin>
      </system-margins>
      <system-distance>121</system-distance>
      <top-system-distance>70</top-system-distance>
    </system-layout>
    <appearance>
      <line-width type="stem">0.918</line-width>
      <line-width type="beam">5</line-width>
      <line-width type="staff">0.918</line-width>
      <line-width type="light barline">0.918</line-width>
      <line-width type="heavy barline">5</line-width>
      <line-width type="leger">1.0807</line-width>
      <line-width type="ending">0.957</line-width>
      <line-width type="wedge">0.918</line-width>
      <line-width type="enclosure">0.918</line-width>
      <line-width type="tuplet bracket">0.918</line-width>
      <note-size type="grace">60</note-size>
      <note-size type="cue">60</note-size>
      <distance type="hyphen">120</distance>
      <distance type="beam">7.5</distance>
    </appearance>
    <music-font font-family="Maestro,engraved" font-size="20.5"/>
    <word-font font-family="Times New Roman" font-size="10.25"/>
  </defaults>
  <credit page="1">
    <credit-type>part name</credit-type>
    <credit-words default-x="70" default-y="1453" font-size="12" valign="top">Score</credit-words>
  </credit>
  <part-list>
    <score-part id="P1">
      <part-name print-object="no">MusicXML Part</part-name>
      <score-instrument id="P1-I1">
        <instrument-name>ARIA Player</instrument-name>
      </score-instrument>
      <midi-instrument id="P1-I1">
        <midi-channel>1</midi-channel>
        <midi-bank>15489</midi-bank>
        <midi-program>1</midi-program>
        <volume>80</volume>
        <pan>0</pan>
      </midi-instrument>
    </score-part>
  </part-list>
  <!--=========================================================-->
  <part id="P1">
    <measure number="1" width="298">
      <print>
        <system-layout>
          <system-margins>
            <left-margin>70</left-margin>
            <right-margin>0</right-margin>
          </system-margins>
          <top-system-distance>211</top-system-distance>
        </system-layout>
        <measure-numbering>system</measure-numbering>
      </print>
      <attributes>
        <divisions>2</divisions>
        <key>
          <fifths>0</fifths>
          <mode>major</mode>
        </key>
        <time>
          <beats>4</beats>
          <beat-type>4</beat-type>
        </time>
        <clef>
          <sign>G</sign>
          <line>2</line>
        </clef>
      </attributes>
      <sound tempo="120"/>
      <note default-x="83">
        <pitch>
          <step>C</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="99" bezier-y="42" default-x="8" default-y="-5" placement="above" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="2" width="228">
      <note default-x="13">
        <pitch>
          <step>D</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="53" bezier-y="-26" default-x="8" default-y="-55" placement="below" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="3" width="228">
      <note default-x="13">
        <pitch>
          <step>E</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-52" bezier-y="-28" default-x="8" default-y="-50" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="4" width="228">
      <note default-x="13">
        <pitch>
          <step>G</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-101" bezier-y="36" default-x="8" default-y="15" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="5" width="292">
      <print new-system="yes">
        <system-layout>
          <system-distance>114</system-distance>
        </system-layout>
      </print>
      <note default-x="52">
        <pitch>
          <step>C</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="90" bezier-y="30" default-x="8" default-y="-5" placement="above" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="6" width="254">
      <note default-x="13">
        <pitch>
          <step>D</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="94" bezier-y="-42" default-x="9" default-y="-60" placement="below" type="start"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="7" width="254">
      <note default-x="13">
        <pitch>
          <step>E</step>
          <octave>4</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-87" bezier-y="38" default-x="8" default-y="-27" type="stop"/>
        </notations>
      </note>
    </measure>
    <!--=======================================================-->
    <measure number="8" width="254">
      <note default-x="13">
        <pitch>
          <step>G</step>
          <octave>5</octave>
        </pitch>
        <duration>8</duration>
        <voice>1</voice>
        <type>whole</type>
        <notations>
          <slur bezier-x="-84" bezier-y="-60" default-x="8" default-y="-10" type="stop"/>
        </notations>
      </note>
      <barline location="right">
        <bar-style>light-heavy</bar-style>
      </barline>
    </measure>
  </part>
  <!--=========================================================-->
</score-partwise>

So none of these editors can handle more than one active slur with a given @number at a time. How did you create the original example?

When I load the original example into Finale, I get two error windows, one for a validation problem and another for the slur@number problem:

Screen Shot 2020-06-19 at 7 00 15 PM Screen Shot 2020-06-19 at 7 00 33 PM

And loading into Finale, the result is:

Screen Shot 2020-06-19 at 7 00 46 PM

Compared to the desired:

Screen Shot 2020-06-19 at 7 02 25 PM

Sibelius notes the validation error and refuses to import the MusicXML file:

Screen Shot 2020-06-19 at 7 07 00 PM

Fixing the validation error and loading into Sibelius, then the MusicXML data loads into the editor as desired:

Screen Shot 2020-06-19 at 7 11 45 PM

Loading into MuseScore, there is a validation error warning:

Screen Shot 2020-06-19 at 7 12 34 PM

And the MusicXML is loaded with messed-up slurs:

Screen Shot 2020-06-19 at 7 12 43 PM
yeonoson commented 4 years ago

The original MusicXML extract was stripped down from the MusicXML export of the following file SIB file: https://www.liederindex.de/songs/15/file/4009

One part of Sibelius logic for using a number in slur-elements seems to be: simple slurs that end in the same measure and do not overlap with other slurs do not get a number. That is why Slur B in the original example is not considered for the slur counting, only Slur A and C are considered. Eventually this ends in Slur A and B having the same number, which is only a problem if the slurs are read with a FIFO.

The hand-edited example of my previous post, however, is exported correctly by Sibelius, i.e. both slurs have different numbers. If this logic always behaves like that in Sibelius and other notation editors, then the slur stack solution as provided by @rettinghaus in #1508 should be sucfficient. Using the orientation/placement would then only required as an additional fallback and can be omitted if you don't think it's necessary.

craigsapp commented 4 years ago

Here is the MusicXML export of the full work from Sibelius:

ach-text.zip

The purpose of MusicXML is to transfer musical data between different music notation editors. The Sibelius export does not do that, so by this definition, the MusicXML export from Sibelius for this example is incorrect.

Here is the view from Sibelius of the original .SIB file in Sibelius:

Screen Shot 2020-06-21 at 1 46 44 PM

When loading the MusicXML back into Sibelius, the slur endpoints are matched correctly (although there are other unrelated problems/limitations):

Screen Shot 2020-06-21 at 2 37 28 PM

When loading the MusicXML into Finale, the slurs are messed up and there is a warning message:

Screen Shot 2020-06-21 at 2 19 53 PM Screen Shot 2020-06-21 at 2 21 24 PM

Since MusicXML is owned by MakeMusic, also the owners of Finale, if there is a problem loading a MusicXML file into Finale, then it is by definition a problem with the MusicXML file. The Sibelius developers should buy a copy of Finale (or use the 30-day free demo) and make sure that the slurs export from Sibelius can be read into Finale.

When loading the MusicXML into MuseScore, there are also slur problems (as in the original excerpt):

Screen Shot 2020-06-21 at 2 29 53 PM

MuseScore is free, so there should not be a problem for the Sibelius developers to download a copy of it to evaluate if their slur exports will import into MuseScore.

Loading the MusicXML into verovio via the direct path (copy-and-paste MusicXML text into VHV:

Screen Shot 2020-06-21 at 2 57 34 PM

This is before @rettinghaus's pull request for the slur fix. There is a strange problem with the vertical placement of the verses. Verses 4 and 5 at the bottom of the original example are inserted as text expressions. Perhaps these can be improved in the importer by detecting text when has a very low placement on the staff as is done in the MusicXML export.

The 1, 2, 3 verse numbers are exported as text directions. It would be useful to infer that these are verse labels which are now available in MEI/verovio. There are strange horizontal lines in the squashed text, probably word extenders that are not turned off at the correct time.

Here is the import via MusicXML-to-Humdrum in VHV (by drag-and-drop of the MusicXML file):

Screen Shot 2020-06-21 at 3 04 22 PM

The verses are not collapsing for some reason, (maybe the run-away word extender has to do with the verse collapsing, since I do not keep the word extenders). Also needs to infer the verse labels.

craigsapp commented 4 years ago

The purpose of MusicXML is to transfer musical data between different music notation editors. The Sibelius export does not do that, so by this definition, the MusicXML export from Sibelius for this example is incorrect.

That being said, I do not see a problem with how Sibelius exports the slurs in this example given the previously cited instruction in the MusicXML documentation:

http://usermanuals.musicxml.com/MusicXML/MusicXML.htm#ST-MusicXML-start-stop-continue.htm

The values of start, stop, and continue refer to how an element appears in musical score order, not in MusicXML document order. An element with a stop attribute may precede the corresponding element with a start attribute within a MusicXML document. This is particularly common in multi-staff music. For example, the stopping point for a slur may appear in staff 1 before the starting point for the slur appears in staff 2 later in the document.

Here is a markup of the slur numbers in the MusicXML data:

Screen Shot 2020-06-21 at 3 19 27 PM

(The two staves are separate parts; otherwise if they were in the same part, the slur parsing would become more complex.)

The slurs are numbered correctly in "musical score order" (time serialization), and the MusicXML documentation says that they should not be numbered according to "MusicXML documentation order" (data serialization). I other words, when there is only one active slur in the score, it is given a number of 1 (or an empty number attribute). When there is another slur that starts at the same time in the score as another slur, or it starts after another slur start that has not yet stopped, then it should be assigned @number="2", and so on (there is a limit of 6 active slurs in MusicXML at any time).

So LIFO/FIFO is a red herring for this issue, since there should not be more than one slur assigned to any @number at a given time (but when serialized in the data it is possible for more than one slur with a given number). Therefore, the correct solution to import MusicXML slurs would be to store them in a separate time-sorted list, and then read through this list to match the endpoints to each other. In other words, assigning the endpoints from the possibly unsorted data serialization should not be done (as I still think is being done in the MusicXML importer after @rettinghaus's pull request).

But if this is done, then perhaps MusicXML slurs exported from Finale/MuseScore will not import properly since they do not seem to be following the MusicXML documentation. So there may need to be a different parsing model for slurs depending on the software that created the MusicXML file...

craigsapp commented 4 years ago

Here is the same music exported as MusicXML from Finale:

ach-finale.zip

(I had to delete all of the slurs in the Sibelius MusicXML and then add them manually in Finale; otherwise, there were junk slurs in the Finale MusicXML export related to the bad slur import).

The data is serialized in the same order as in the Sibelius export: The top voice (black notes) is given first in a measure, then the second voice (red notes) is given.

Here are the slur@number assignments for the slurs from the full example:

Screen Shot 2020-06-21 at 5 59 52 PM

slur@numbers are assigned according to the MusicXML data serialization of the layers. This is in contrast to the Sibelius method which is a time serialization:

Screen Shot 2020-06-21 at 3 19 27 PM

(only @number="2" are given, and @number="1" is implied for the slurs without @number).

So for the MusicXML importer, there should be two models for converting slurs: one which is data-order based for MusicXML from Finale, and another which is time-ordered for Sibelius MusicXML exports. An automated detection system could be implemented to determine which serialization method is being used: If there is a slur start that is not closed before another slur start with the same @number, then that means that the other system should be used, since it seems that active slurs in the data/time serialization systems cannot share the same @number.

craigsapp commented 4 years ago

Here is a useful note for the MusicXML importer: For automatic identification of post-music text such as additional verse text in this case (which could be inferred and added to the page footer in the MEI conversion):

Finale MusicXML export places the text into a single <words> element:

      <direction placement="below">
        <direction-type>
          <words default-y="-629" font-size="10.4" xml:space="preserve">5. Hilf leben uns in Deinem Wort
und darauf mutig fahren fort
Von hinnen aus dem Jammertal
zu Dir in Deinen Himmelssaal!</words>
        </direction-type>

While Sibelius MusicXML places each line in a separate <words> element, with only the first one assigned a vertical position:

    <direction-type>
     <words default-y="-629" justify="left" valign="middle" font-family="Times New Roman" font-style="normal" font-size="10.3243" font-weight="normal">5. Hilf leben uns in Deinem Wort</words>
     <words justify="left" valign="middle" font-family="Times New Roman" font-style="normal" font-size="10.3243" font-weight="normal">und darauf mutig fahren fort</words>
     <words justify="left" valign="middle" font-family="Times New Roman" font-style="normal" font-size="10.3243" font-weight="normal">Von hinnen aus dem Jammertal</words>
     <words justify="left" valign="middle" font-family="Times New Roman" font-style="normal" font-size="10.3243" font-weight="normal">zu Dir in Deinen Himmelssaal!</words>
    </direction-type>
craigsapp commented 4 years ago

Here is the MusicXML export from MuseScore:

ach-musescore.zip

(I deleted the slurs in the MusicXML file from Sibelius, then added them manually in MuseScore before exporting as MusicXML from MuseScore).

The slur numbering uses the data serialization model:

Screen Shot 2020-06-21 at 9 12 22 PM

This matches the Finale method:

Screen Shot 2020-06-21 at 5 59 52 PM

Sibelius can link the slurs correctly in this example MusicXML file.

Screen Shot 2020-06-21 at 9 18 36 PM

There is only one ambiguity in the third-to-last measure where there are slurs in both layers at the same time, so it could either be a coincidence that it was parsed correctly in the time serialization model, or they detect and use a data serialization model, or they guess based on the orientation and/or layer attachment of the slur.