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

merging rests #947

Closed rettinghaus closed 5 years ago

rettinghaus commented 5 years ago

Merging rests with @sameas does not work like expected with rests in different layers.

1) if @sameas is applied to one rest, the glyph is not present in the svg, but should be printed instead over (or under) the other one.

mergerest

2) if @sameas is applied to both rests (referencing vice versa), Verovio crashes. I expected both rests to be printed at their 'normal' position, as if there is only one layer.

<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
  <meiHead>
    <fileDesc>
      <titleStmt>
        <title>merging rests</title>
      </titleStmt>
      <pubStmt />
    </fileDesc>
  </meiHead>
  <music>
    <body>
      <mdiv xml:id="mdiv-0000000930386372">
        <score xml:id="score-0000001679681882">
          <scoreDef xml:id="scoredef-0000000777211839">
            <staffGrp xml:id="staffgrp-0000001198227407">
              <staffDef xml:id="staffdef-0000000289145439" clef.shape="G" clef.line="2" n="1" lines="5" />
            </staffGrp>
          </scoreDef>
          <section xml:id="section-0000000622944406">
            <pb xml:id="pb-0000002029893668" />
            <measure xml:id="measure-0000001708243152" n="1">
              <staff xml:id="staff-0000000313346093" n="1">
                <layer xml:id="layer-0000000912970797" n="1">
                  <rest xml:id="rest-0000000259646555" dur="4" sameas="#rest-0000001381701126"/>
                </layer>
                <layer xml:id="layer-0000001491911089" n="2">
                  <rest xml:id="rest-0000001381701126" dur="4" sameas="#rest-0000000259646555"/>
                </layer>
              </staff>
            </measure>
          </section>
        </score>
      </mdiv>
    </body>
  </music>
</mei>
craigsapp commented 5 years ago

Is there a precise meaning for @sameas? The attribute description is a bit ambiguous:

Points to an element that is the same as the current element but is not a literal copy of the current element.

In other words, what is the definition of "same" and "copy" as used in this definition? There are also @copyof and @correp that are related.

I would interpret "sameas" to mean that there is a single visual object being displayed in the notation, but this needs to be represented as an element at different points in the MEI file. This occurs with clefs, which need to be duplicated in the MEI encoding in each layer, and examples such as this where there is a shared rest common to two different layers.

So there is a problem according to my interpretation and the following statement:

if @sameas is applied to one rest, the glyph is not present in the svg, but should be printed instead over (or under) the other one.

I would say that only one of the rests should have a @sameas attribute which points to the (perhaps arbitrary) primary element. The primary element contains all of the other attributes for the element, and the element with the @sameas has only that attribute (it will borrow the other attributes as needed from the primary element. If there are non-visual aspects that are different between the two elements, then they could be added to the @sameas element (like CSS, where the local parameters would overwrite any that are adopted from the primary element).

I would also say that there should only be one glyph present in the SVG, and there should not be an over/understrike with a second rest. If that is needed, then I would expect @copyof to be used instead of @sameas. Although over/understrikes are already possible in verovio by placing the rests in different layers at the same vertical position (so no important reason to use @sameas in such cases).

if @sameas is applied to both rests (referencing vice versa), Verovio crashes. I expected both rests to be printed at their 'normal' position, as if there is only one layer.

Here is an example that @lpugin gave for clef@sameas in issue https://github.com/rism-ch/verovio/issues/935

      <clef xml:id="clef-L5F1" shape="G" line="2" />
      ...
      <clef sameas="clef-L5F1" />
screen shot 2018-10-26 at 8 40 59 am

I would say that only one of the rests should have a @sameas attribute that points to the other rest; otherewise, there will be a recursion problem (which is probably what is causing the crash in verovio). There should be a primary rests (such as in the first layer), and then secondary rests which are labeled with @sameas that point to the primary rests.

A possible useful application of @sameas would control whether or not to merge rests in separat layers (which I think you are thinking about). Currently verovio merges rests (via overstriking) when the same rest occurs at the same time in different layers on the same staff:

screen shot 2018-10-26 at 9 23 53 am
  <layer n="1">
      <note dur="4" oct="5" pname="f" accid.ges="n" />
      <rest dur="4"/>
  </layer>  
  <layer n="2">
      <note dur="4" oct="4" pname="e" accid.ges="n" />
      <rest dur="4"/>
  </layer>  

Using @sameas could be a way of explicitly saying that the two rests should be merged into a single graphical element:

screen shot 2018-10-26 at 9 19 25 am
  <layer n="1">
      <note dur="4" oct="5" pname="f" accid.ges="n" />
      <rest xml:id="rest1" dur="4"/>
  </layer>  
  <layer n="2">
      <note dur="4" oct="4" pname="e" accid.ges="n" />
      <rest sameas="rest1" dur="4"/>
  </layer>  

The @dur="4" on the second rest would be optional, and mostly for readability and making it easier to switch between a shared and separate graphical display of the rests. However, using @sameas in this way would be computationally expensive, particularly when used in orchestral scores, which is a problems that would have to be thought about before adopting this convention for shared rests between layers.

In the Humdrum-to-MEI converter, I wanted rests in the (first two layers) to merge automatically:

screen shot 2018-10-26 at 9 31 44 am

The MEI conversion does this ploc/oloc to force both rests to the center line:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://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="2018-10-26T09:34:27" version="2.0.0-dev-cd3c695">
                    <name>Verovio</name>
                    <p>Transcoded from Humdrum</p>
                </application>
            </appInfo>
        </encodingDesc>
        <workDesc>
            <work>
                <titleStmt>
                    <title />
                </titleStmt>
            </work>
        </workDesc>
    </meiHead>
    <music>
        <body>
            <mdiv xml:id="mdiv-0000001339110420">
                <score xml:id="score-0000000821378631">
                    <scoreDef xml:id="scoredef-0000001156356833">
                        <staffGrp xml:id="staffgrp-0000001850342199">
                            <staffDef xml:id="staffdef-0000000657059520" clef.shape="G" clef.line="2" n="1" lines="5">
                                <label xml:id="label-0000000907185384" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-L1F1">
                        <measure xml:id="measure-L1" n="0">
                            <staff xml:id="staff-0000000721529213" n="1">
                                <layer xml:id="layer-L1F1N1" n="1">
                                    <note xml:id="note-L3F1" dur="4" oct="5" pname="f" stem.len="5.500000" accid.ges="n" />
                                    <rest xml:id="rest-L4F1" dur="4" ploc="b" oloc="4" />
                                </layer>
                                <layer xml:id="layer-L3F2N2" n="2">
                                    <note xml:id="note-L3F2" dur="4" oct="4" pname="e" stem.len="5.500000" accid.ges="n" />
                                    <rest xml:id="rest-L4F2" dur="4" ploc="b" oloc="4" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>

Otherwise, if I do not want them merged, I currently specifiy the pitch that the rests should be displayed at:

screen shot 2018-10-26 at 9 35 36 am
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://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="2018-10-26T09:36:34" version="2.0.0-dev-cd3c695">
                    <name>Verovio</name>
                    <p>Transcoded from Humdrum</p>
                </application>
            </appInfo>
        </encodingDesc>
        <workDesc>
            <work>
                <titleStmt>
                    <title />
                </titleStmt>
            </work>
        </workDesc>
    </meiHead>
    <music>
        <body>
            <mdiv xml:id="mdiv-0000001217570192">
                <score xml:id="score-0000001716845239">
                    <scoreDef xml:id="scoredef-0000000300074874">
                        <staffGrp xml:id="staffgrp-0000000078584989">
                            <staffDef xml:id="staffdef-0000001121077938" clef.shape="G" clef.line="2" n="1" lines="5">
                                <label xml:id="label-0000000525977998" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-L1F1">
                        <measure xml:id="measure-L1" n="0">
                            <staff xml:id="staff-0000001940209099" n="1">
                                <layer xml:id="layer-L1F1N1" n="1">
                                    <note xml:id="note-L3F1" dur="4" oct="5" pname="f" stem.len="5.500000" accid.ges="n" />
                                    <rest xml:id="rest-L4F1" dur="4" ploc="f" oloc="5" />
                                </layer>
                                <layer xml:id="layer-L3F2N2" n="2">
                                    <note xml:id="note-L3F2" dur="4" oct="4" pname="e" stem.len="5.500000" accid.ges="n" />
                                    <rest xml:id="rest-L4F2" dur="4" ploc="e" oloc="4" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>

Full MEI example for clef excerpt given further above:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="http://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="2018-10-02T07:11:32" version="2.0.0-dev-eebabfa">
                    <name>Verovio</name>
                    <p>Transcoded from Humdrum</p>
                </application>
            </appInfo>
        </encodingDesc>
        <workDesc>
            <work>
                <titleStmt>
                    <title />
                </titleStmt>
            </work>
        </workDesc>
    </meiHead>
    <music>
        <body>
            <mdiv xml:id="mdiv-0000000423522553">
                <score xml:id="score-0000001000653264">
                    <scoreDef xml:id="scoredef-0000001104367153">
                        <staffGrp xml:id="staffgrp-0000001737249930">
                            <staffDef xml:id="staffdef-0000000226698908" clef.shape="F" clef.line="4" n="1" lines="5">
                                <label xml:id="label-0000001046762150" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-L1F1">
                        <measure xml:id="measure-L1" n="0">
                            <staff xml:id="staff-0000001732895402" n="1">
                                <layer xml:id="layer-L1F1N1" n="1">
                                    <beam xml:id="beam-L4F1-L8F1">
                                        <note xml:id="note-L4F1" dur="8" oct="3" pname="b" accid.ges="n" />
                                        <clef xml:id="clef-L5F1" shape="G" line="2" />
                                        <note xml:id="note-L6F1" dur="8" oct="4" pname="b" accid.ges="n" />
                                        <note xml:id="note-L7F1" dur="8" oct="4" pname="a" accid.ges="n" />
                                        <note xml:id="note-L8F1" dur="8" oct="4" pname="g" accid.ges="n" />
                                    </beam>
                                </layer>
                                <layer xml:id="layer-L4F2N2" n="2">
                                    <note xml:id="note-L4F2" dur="4" oct="3" pname="b" stem.len="5.500000" accid.ges="n" />
                                    <clef sameas="clef-L5F1" />
                                    <note xml:id="note-L7F2" dur="4" oct="4" pname="f" stem.len="5.500000" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>
rettinghaus commented 5 years ago

Sorry for not making it clear: with 'over' and 'under' I meant at the same position, with different z-indices. Problem now in Verovio is, that there is an empty <g class="rest"/> in the SVG, so there is no way to highlight it.

I would argue, that from a human-reader perspective it would make more sense to have @sameas applied to both elements. What are your thoughts, @pe-ro ?

And just to be a smart-ass @craigsapp : You are using @sameas wrong. It has to hold a pointer (starting with #). :wink:

craigsapp commented 5 years ago

z-indices

A problem is that SVG 1.1 does not have z-indexes (added to SVG 2). If you don't care about compatibility with SVG 1.1 that would not be a problem. This random webpage from two years ago: https://css-tricks.com/svg-2-conundrum

says:

Unfortunately, with the browsers now backing away from SVG 2 features like mesh gradients and z-index (which we’d thought had wide support), it’s uncertain when if ever the new graphics features will be pursued.

I would argue, that from a human-reader perspective it would make more sense to have @sameas applied to both elements.

A problem is the clef example in issue https://github.com/rism-ch/verovio/issues/935, where there are two clefs that are same-as with each other, but only one of them should be shown. So either there needs to be a way of indicating the primal element, or the case in issue #195 should be implemented in a different way (such as making the unaligned clef invisible).

And just to be a smart-ass @craigsapp : You are using @sameas wrong. It has to hold a pointer (starting with #). 😉

That is more pedantic (a.k.a. Germanic) than smart-ass, while this comment is smart-ass 😸

kepper commented 5 years ago

For what it's worth, my interpretation of @sameas always was that both elements provide information about the very same thing on the page, just from different perspectives. In this case, the first layer's rest provides almost everything. The second <rest> element just adds the information that this very same rest also belongs to the second layer in that staff. Maybe that's a very 'graphical' approach, as there is only one blob of ink on the page, but of course two instruments are pausing. @copyof, in contrast, is more like a markup abbreviation. It says that one should copy over the markup pointed at into the current place (excluding .//@xml:id, plus local attributes may override the copied stuff). In a way, this also serves analytical purposes. Finally, @corresp has no specific meaning at all. Every project may decide what the type of this correspondence is in it's own data. It is also important to consider the directions of these pointers. @copyof has a clear direction – element A is identified as a copy of element B. @sameas has no direction: If element A is the same as element B, then B is also the same as A. While I understand that this is a processing nightmare, logically it's not necessary to have pointers in both ways – either one direction is sufficient. Maybe it's helpful to pre-process your data in such cases. All that said, I'm aware that this doesn't help with your problem. The only solution I see (besides changes to Verovio, which I'm not the right one to suggest) would be to go back to your MEI file when you want to highlight the 'unrendered' rest. There, you can find all you need to know which SVG element needs to be touched…

lpugin commented 5 years ago

Closing for now. We can have another issue for the placement if necessary but the handling of @sameas is appropriate.