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
678 stars 185 forks source link

stacking order of control events #1296

Open craigsapp opened 4 years ago

craigsapp commented 4 years ago

Here is an example of a fermata and a dynamic that are stacked in the wrong order:

Screen Shot 2020-02-05 at 00 54 54

The "sf" should go above the fermata rather than the other way around. If the <dynam> is converted into a <dir>, the stacking order is correct:

Screen Shot 2020-02-05 at 00 59 39

MEI test data:

<?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-02-05T00:54:48" version="2.5.0-dev-bdbee94">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001195811737">
    <score xml:id="score-0000000363403612">
     <scoreDef xml:id="scoredef-0000001272376039" midi.bpm="189">
      <staffGrp xml:id="staffgrp-0000000681810386">
       <staffDef xml:id="staffdef-0000000751877302" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" pname="c" mode="major" sig="0" />
        <meterSig xml:id="metersig-L5F1" count="3" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="0">
       <staff xml:id="staff-0000002007495486" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L8F1" dur="8" oct="4" pname="g" accid.ges="n">
          <verse xml:id="verse-L8F2" n="1">
           <syl xml:id="syl-L8F2">wie</syl>
          </verse>
         </note>
         <note xml:id="note-L9F1" dur="8" oct="4" pname="a" accid.ges="n">
          <verse xml:id="verse-L9F2" n="1">
           <syl xml:id="syl-L9F2">die</syl>
          </verse>
         </note>
         <note xml:id="note-L10F1" dur="4" oct="4" pname="g" accid.ges="n">
          <verse xml:id="verse-L10F2" n="1">
           <syl xml:id="syl-L10F2" con="d" wordpos="i">Au</syl>
          </verse>
         </note>
         <note xml:id="note-L11F1" dur="4" oct="5" pname="c" accid.ges="n">
          <verse xml:id="verse-L11F2" n="1">
           <syl xml:id="syl-L11F2" wordpos="t">gen</syl>
          </verse>
         </note>
        </layer>
       </staff>
      </measure>
      <measure xml:id="measure-L12" n="32">
       <staff xml:id="staff-L12F1N1" n="1">
        <layer xml:id="layer-L12F1N1" n="1">
         <note xml:id="note-L14F1" dots="1" dur="2" oct="5" pname="d" accid.ges="n">
          <verse xml:id="verse-L14F2" n="1">
           <syl xml:id="syl-L14F2">sprüh'n!</syl>
          </verse>
         </note>
        </layer>
       </staff>
       <dynam xml:id="dynam-L14F3" place="above" staff="1" tstamp="1.000000" vgrp="100">
        <rend xml:id="rend-0000001184946542" halign="right" fontsize="large" fontweight="bold">sf </rend>
       </dynam>
       <fermata xml:id="fermata-L14F1" staff="1" startid="#note-L14F1" place="above" />
      </measure>
      <measure xml:id="measure-L15" n="33">
       <staff xml:id="staff-L15F1N1" n="1">
        <layer xml:id="layer-L15F1N1" n="1">
         <note xml:id="note-L17F1" dur="8" oct="4" pname="f" accid.ges="n">
          <verse xml:id="verse-L17F2" n="1">
           <syl xml:id="syl-L17F2" con="d" wordpos="i">Lip</syl>
          </verse>
         </note>
         <note xml:id="note-L18F1" dur="8" oct="4" pname="a" accid.ges="n">
          <verse xml:id="verse-L18F2" n="1">
           <syl xml:id="syl-L18F2" wordpos="t">pen</syl>
          </verse>
         </note>
         <note xml:id="note-L19F1" dur="4" oct="4" pname="f" accid.ges="n">
          <verse xml:id="verse-L19F2" n="1">
           <syl xml:id="syl-L19F2" con="d" wordpos="i">müs</syl>
          </verse>
         </note>
         <beam xml:id="beam-L20F1-L21F1">
          <note xml:id="note-L20F1" dur="16" oct="4" pname="f" grace="acc" stem.dir="up" accid.ges="n" />
          <note xml:id="note-L21F1" dur="16" oct="4" pname="e" grace="acc" stem.dir="up" accid.ges="n" />
         </beam>
         <note xml:id="note-L22F1" dur="4" oct="4" pname="d" accid.ges="n">
          <verse xml:id="verse-L22F2" n="1">
           <syl xml:id="syl-L22F2" wordpos="t">sen</syl>
          </verse>
          <artic xml:id="artic-L22F1" artic="acc" place="above" />
         </note>
        </layer>
       </staff>
       <dynam xml:id="dynam-L17F3" place="above" staff="1" tstamp="1.000000" vgrp="100">
        <rend xml:id="rend-0000001414664763" halign="right" fontsize="large" fontweight="bold">p </rend>
       </dynam>
       <slur xml:id="slur-L20F1-L21F1" staff="1" startid="#note-L20F1" endid="#note-L21F1" curvedir="below" />
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
lpugin commented 3 years ago

See this https://github.com/rism-ch/verovio/issues/445#issuecomment-274008419 and this https://github.com/rism-ch/verovio/issues/1151#issuecomment-539965672

Maybe we can change the default priority order to:

Any objections?

(There was a while ago a discussion about being able to provide the order explicitly in the encoding when a particular sequence is desired, but I cannot find anything about it.)

craigsapp commented 3 years ago

That looks good, but the octave line should have lower priority than in the above list since usually only dir, tempo and endings are further (but it is not common to have dynamics above the staff, so an actual music example containing both would be good to find).

articulation (staccato / accent / tenuto / stacattissimo)       (closest)
ornament (mordent / turn / trill)
brackets                          (not sure about this one: might depend on its purpose and may be just above octave in the list)
breath
fermata
dynam / hairpin
octave
dir
tempo
pedal
[verse]                            (especially when above the staff)
harm
(ending)                           (furthest)
lpugin commented 3 years ago

For now I would prefer to keep dynam / hairpin and dir next to each other. That would (I think) solve the issue when some dynams and dirs are grouped together. Now it is a problem when there is something in-between, which makes no sense - basically saying that a group of elements needs to be aligned together, but some placed above other ones, and others below.

More fundamentally, the problem is related to the fact the dir covers a very broad range of cases. Once we have a way to qualify them in a more refined way (see https://github.com/music-encoding/music-encoding/issues/712) we can do something like:

We can have other dir@func in the list, e.g. dir@func="lv", which should probably be closer to the note than generic dir elements and be at the same level as lv.

As it stands, artic and verse are treated differently since they are not control events. I don't think we support verse to be above the staff, do we? In any case, they would appear after (further away) than harm. I think this is fine.

craigsapp commented 3 years ago

OK

We can have other dir@func in the list, e.g. dir@func="lv", which should probably be closer to the note than generic dir elements and be at the same level as lv.

Yes, l.v. should have higher stacking priority than regular dirs.

A thought: tempo-like dirs should be given lower priority than regular durs (such as "ritard"), since they should be equivalent to <tempo> in their stacking order (or should these just be called <tempo>?).

dir@func="dynam" (if we agree for this value to be added to the list in @func)

That is good. I have been calling these <dynam>.

I don't think we support verse to be above the staff, do we?

Not yet, but eventually: https://github.com/humdrum-tools/verovio-humdrum-viewer/issues/292

[lyrics] would appear after (further away) than harm.

I have never seen such a case where verse and harm are on the same side of the staff, but I would think that verse has higher priority than harm since it is more important musically. But that can be dealt with if a real musical example is found demonstrating the priority of the two.

ndubo commented 3 years ago

Have you thought about implementing the aboveorder / beloworder attributes on scoreDef? Maybe the list of values should be expanded, but these attributes may serve the purpose we are looking for. (It would be an addition, of course, to a default stacking order.)

lpugin commented 3 years ago

I am still not sure how to properly stack some control events, in particular dir. For example, I assume something like this would be encoded with two dir elements, one for con sordino and one for Thema, and a tempo indication. image However, I would expect to be able to encode that one of the dir is below the tempo the other above. I don't think above / beloworder allows us to do that, does it? Any though how to handle this?

craigsapp commented 3 years ago

In this particular case, the <dir>/<tempo> elements each have a well-defined function, so maybe @type can be used to refine the stacking order: Regular <dir> should always be below a <tempo>. And maybe Thema could be encoded using dir@type="title" which would stack above <tempo> (and above anything else, including endings). Or there could otherwise be a certain type, such as top or furthest, that is always placed above <tempo> while other <dir> would be placed below <tempo>.

lpugin commented 3 years ago

Yes, that would work. However, it would break the rule of thumb that Verovio should not rely on @type attribute values - these are not pre-defined in MEI and are meant to be application specific. It would be much better if we can rely on something that is part of MEI in order to maximise interoperability. Hence the proposal to have @func on dir (see here) but that did not seem to lead to a consensus.

lpugin commented 3 years ago

Instead of using dir@type I would prefer to have a Verovio customization with dir@func, but that would be a very last resort solution I would prefer not to go for. @kepper, do you see any other options we have with MEI?

craigsapp commented 3 years ago

One possibility is to promote the <div> container for Thema to its own element, as is done with <tempo>. This would simplify stacking, since that new element would always be given the highest stacking order.

This sort of case is common in particular genres (especially Theme and Variations), so some sort of solution would be very useful. Here is an example where it is common in Minuet and Trios:

Screen Shot 2021-10-07 at 11 19 12 PM

Notice that there is no tempo under the Trio label in this case:

Screen Shot 2021-10-07 at 11 19 25 PM

Of course, here is an exception where the tempo is above the section title:

Screen Shot 2021-10-07 at 11 23 18 PM

But more common is section title first and then tempo below:

Screen Shot 2021-10-07 at 11 24 30 PM
lpugin commented 3 years ago

Yes, that would work too. Something like <title>?

Of course, here is an exception where the tempo is above the section title:

I think this type of exception could then be handled with @aboveorder.

rettinghaus commented 2 years ago

I think that title is an element for semantic tagging, not for display. The current structure of MEI doesn't seem to allow multiple pieces with different titles on on page at all with pgHead. It would be good to have something more versatile here.