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

@stem.dir on semibreve only with mensural.black #839

Closed craigsapp closed 3 years ago

craigsapp commented 6 years ago

@stem.dir values on semibreve notes in mensural notation should be ignored. Currently a stem is being added if there is a stem direction given for the note.

Example:

The first measure contains three semi-breves, with the first one having no @stem.dir, and then the other two having an up and down stem indication. The same encoding is done for the second measure, consisting of three breves.

screen shot 2018-05-05 at 1 37 27 am

Note that breves in the second measure are not affected.

MEI test data:

<?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-05-05T01:35:30" version="2.0.0-dev-1c60b86">
                    <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-0000000838783923">
                <score xml:id="score-0000001358734953">
                    <scoreDef xml:id="scoredef-0000000977003555" midi.bpm="400">
                        <staffGrp xml:id="staffgrp-0000000123433429">
                            <staffDef xml:id="staffdef-0000000838439767" clef.shape="C" clef.line="3" n="1" notationtype="mensural" lines="5">
                                <label xml:id="label-0000000861340324" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-0000002064736520">
                        <measure xml:id="measure-L1" n="0">
                            <staff xml:id="staff-0000000446497358" n="1">
                                <layer xml:id="layer-L1F1N1" n="1">
                                    <note xml:id="note-L3F1" dur="semibrevis" oct="4" pname="c" accid.ges="n" />
                                    <note xml:id="note-L4F1" dur="semibrevis" oct="4" pname="c" stem.dir="up" accid.ges="n" />
                                    <note xml:id="note-L5F1" dur="semibrevis" oct="4" pname="c" stem.dir="down" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                        <measure xml:id="measure-L6">
                            <staff xml:id="staff-L6F1N1" n="1">
                                <layer xml:id="layer-L6F1N1" n="1">
                                    <note xml:id="note-L7F1" dur="brevis" oct="4" pname="c" accid.ges="n" />
                                    <note xml:id="note-L8F1" dur="brevis" oct="4" pname="c" stem.dir="up" accid.ges="n" />
                                    <note xml:id="note-L9F1" dur="brevis" oct="4" pname="c" stem.dir="down" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>

As a comparison, here is the same conditions for modern notation where the whole-notes do not have added stems, as expected:

screen shot 2018-05-05 at 1 43 28 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-05-05T01:42:57" version="2.0.0-dev-1c60b86">
                    <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-0000001154367647">
                <score xml:id="score-0000001089776131">
                    <scoreDef xml:id="scoredef-0000001012671046" midi.bpm="400">
                        <staffGrp xml:id="staffgrp-0000002050479737">
                            <staffDef xml:id="staffdef-0000001810237192" clef.shape="C" clef.line="3" n="1" lines="5">
                                <label xml:id="label-0000001809475211" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-0000002126892101">
                        <measure xml:id="measure-L1" n="0">
                            <staff xml:id="staff-0000000954592112" n="1">
                                <layer xml:id="layer-L1F1N1" n="1">
                                    <note xml:id="note-L3F1" dur="1" oct="4" pname="c" accid.ges="n" />
                                    <note xml:id="note-L4F1" dur="1" oct="4" pname="c" stem.dir="up" accid.ges="n" />
                                    <note xml:id="note-L5F1" dur="1" oct="4" pname="c" stem.dir="down" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                        <measure xml:id="measure-L6">
                            <staff xml:id="staff-L6F1N1" n="1">
                                <layer xml:id="layer-L6F1N1" n="1">
                                    <note xml:id="note-L7F1" dur="breve" oct="4" pname="c" accid.ges="n" />
                                    <note xml:id="note-L8F1" dur="breve" oct="4" pname="c" stem.dir="up" accid.ges="n" />
                                    <note xml:id="note-L9F1" dur="breve" oct="4" pname="c" stem.dir="down" accid.ges="n" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>
rettinghaus commented 6 years ago

This is easy to fix. But again the question: schould Verovio try to fix nonsense encodings?

craigsapp commented 6 years ago

Reason 1: Verovio should behave the same for semibreves as it does for whole notes. So otherwise, a bug should be added to draw stems on whole notes.

Reason 2: There are automatic layout reasons to add a virtual stem direction to a note without a stem.

Reason 3: Since it is easy to fix in Verovio, it should be done in verovio rather than relying on external validation. Stem directions are allowed on notes regardless of their type: http://music-encoding.org/guidelines/v3/elements/note.html so this cannot be validated with a basic schema.

Reason 4: Adding a stem to a semibreve converts it visually into a minim, which is quite a serious error to let slip by a notation renderer.

rettinghaus commented 6 years ago

I didn't say it's not a bug and shouldn't be fixed. Still the question in general. Verovio can't foresee all possible encodings and try to make sense of them. So reason 2 isn't valid, if this only applies to one program.

craigsapp commented 6 years ago

I didn't say it's not a bug and shouldn't be fixed.

Well, you asked for it 😛 .

Reason number 2 is useful to infer the placement of a slur above/below notes. It is not a program feature but a an automatic layout feature. If a slur does not have an @place it must be calculated, and it should be calculated according to standard music layout syntaxes, with stem directions being part of that syntax.

Here is an example in verovio:

screen shot 2018-05-05 at 11 26 55 am

In the first two measures the slurs are placed automatically based on the stem directions of the notes without explicitly specifying the placement of the slur. The stem directions are implied and calculated by verovio.

For the third measure the stems for all notes are set to down. For the half notes, the slur is then placed above the notes since this places it properly opposite to the stems of the notes. However, for the whole notes, the placement of the stems are being ignored incorrectly by verovio and the slur remains below the notes.

This sort of case would not occur too often, mostly related to the stem direction of notes on the middle line for music with lyrics, perhaps. And most other cases would be overridden by automatic placement via layer rules.

<?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-05-05T11:27:00" version="2.0.0-dev-686bba6-dirty">
                    <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-0000000333298569">
                <score xml:id="score-0000002146007558">
                    <scoreDef xml:id="scoredef-0000001245157041" midi.bpm="400">
                        <staffGrp xml:id="staffgrp-0000000166214897">
                            <staffDef xml:id="staffdef-0000000954379741" clef.shape="G" clef.line="2" n="1" lines="5">
                                <label xml:id="label-0000001855419698" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-0000001244072718">
                        <measure xml:id="measure-L2" n="1">
                            <staff xml:id="staff-L2F1N1" n="1">
                                <layer xml:id="layer-L2F1N1" n="1">
                                    <note xml:id="note-L3F1" dur="1" oct="5" pname="d" accid.ges="n" />
                                    <note xml:id="note-L4F1" dur="1" oct="5" pname="c" accid.ges="n" />
                                    <note xml:id="note-L5F1" dur="2" oct="5" pname="d" accid.ges="n" />
                                    <note xml:id="note-L6F1" dur="2" oct="5" pname="c" accid.ges="n" />
                                </layer>
                            </staff>
                            <slur xml:id="slur-L3F1-L4F1N1" staff="1" startid="#note-L3F1" endid="#note-L4F1" />
                            <slur xml:id="slur-L5F1-L6F1N1" staff="1" startid="#note-L5F1" endid="#note-L6F1" />
                        </measure>
                        <measure xml:id="measure-L7">
                            <staff xml:id="staff-L7F1N1" n="1">
                                <layer xml:id="layer-L7F1N1" n="1">
                                    <note xml:id="note-L8F1" dur="1" oct="4" pname="a" accid.ges="n" />
                                    <note xml:id="note-L9F1" dur="1" oct="4" pname="g" accid.ges="n" />
                                    <note xml:id="note-L10F1" dur="2" oct="4" pname="a" accid.ges="n" />
                                    <note xml:id="note-L11F1" dur="2" oct="4" pname="g" accid.ges="n" />
                                </layer>
                            </staff>
                            <slur xml:id="slur-L8F1-L9F1N1" staff="1" startid="#note-L8F1" endid="#note-L9F1" />
                            <slur xml:id="slur-L10F1-L11F1N1" staff="1" startid="#note-L10F1" endid="#note-L11F1" />
                        </measure>
                        <measure xml:id="measure-L12" n="2">
                            <staff xml:id="staff-L12F1N1" n="1">
                                <layer xml:id="layer-L12F1N1" n="1">
                                    <note xml:id="note-L13F1" dur="1" oct="4" pname="a" stem.dir="down" accid.ges="n" />
                                    <note xml:id="note-L14F1" dur="1" oct="4" pname="g" stem.dir="down" accid.ges="n" />
                                    <note xml:id="note-L15F1" dur="2" oct="4" pname="a" stem.dir="down" accid.ges="n" />
                                    <note xml:id="note-L16F1" dur="2" oct="4" pname="g" stem.dir="down" accid.ges="n" />
                                </layer>
                            </staff>
                            <slur xml:id="slur-L13F1-L14F1N1" staff="1" startid="#note-L13F1" endid="#note-L14F1" />
                            <slur xml:id="slur-L15F1-L16F1N1" staff="1" startid="#note-L15F1" endid="#note-L16F1" />
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>
lpugin commented 6 years ago

Support for @stem.dir on semibrevis was a feature request https://github.com/rism-ch/verovio/issues/813#issuecomment-379013687 for cases in mensural notation where semibreve do have a stem. If you really want this to be change, please raise the question to the mensural IG group on or MEI before and we can discuss there if there is a better way to encode this.

craigsapp commented 6 years ago

@karend27 could comment here on that comment, particularly on the reason that a semibreve has a stem.

Some semibreves in this notation have descending stems, which we encode with the attribute stem.dir="down". Is is possible to have these render in Verovio as semibreves but with descending stems?

It seems that note@dur.ges should be "semibrevis" and note@dur should be "minim" for such cases.

karend27 commented 6 years ago

There's a transitional period of notation in the early 14th century, where all notes shorter than a breve are called semibreves, but some manuscripts and theorists distinguish a certain kind of semibreve that is held longer than the other semibreves, and it is marked by a descending stem. It isn't a minim. When they finally figure out that they will distinguish instead the shorter notes, and add an ascending stem to the shorter notes, this is then what is called a minim in ars nova notation.

In practice, particularly in the Roman de Fauvel manuscript, the semibreve with a descending stem can be used to tag the first of a group of two or three semibreves. In other notational dialects such as Italian and English fourteenth-century notations, the descending stem on a semibreve also marks that semibreve to be held for a longer duration than normal.

craigsapp commented 6 years ago

Stem directions in white mensural music have the same meaning as in modern notation, i.e., there is no significance and it is only for layout concerns. For black mensural music, the stem direction defines the duration of the note. If this is so, then the note@stem.dir should not need to be specified since it can be inferred from the duration of the note.

Here is the introduction to black mensural notation in Willi Apel's The Notation of Polyphonic Music. 900-1600 (1949) which alludes to this discussion:

screen shot 2018-05-06 at 7 04 57 pm

Note the sentence:

For, whereas white notation is a consolidated system which, during its period of existence, underwent only slight modification, black notation must be comprehended as an historical process.

There are mensural.white and mensural.black categories in staffDef@notatintype:

http://music-encoding.org/guidelines/v3/data-types/data.notationtype.html

I would say that if staffDef@notationtype="mensural.white", then stems should not be allowed on semibreves. If staffDef@notationtype="mensural.black" or staffDef@notationtype="mensural", then stems should be allowed on semibreves.

I notice a staffDef@notationsubtype attribute which is free-form subcategorization of the notation type.

lpugin commented 6 years ago

I renamed the issue accordingly

karend27 commented 6 years ago

Yes, that makes sense to me. The presence and absence of a stem and its direction have specific durational or even pitch meanings (like with the plica) in black mensural notation, but these aspects are no longer present in white mensural notation.