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

PAE does not interpret accids correctly #980

Closed peterkorgaard closed 4 years ago

peterkorgaard commented 5 years ago

This issue is constructed by using version 2.0.0-dev-61da81a of the javascript toolkit.

The musical notation rule is that an acccid is due to end of the measure - if not changed before. This means that the notation

@clef:G-2
@keysig:0
@timesig:4/4
@data:'4xGGGG// 

that generates this score image in playback using the generated midi should produce the midi notes 68, 68, 68, 68. It does not. It produces the notes 68,67,67,67

Examining the generated MEI it shows, that the notes after the accid does not get accid.ges applied as they should. So I believe this is happening when PAE is translated into MEI

image

craigsapp commented 5 years ago

This is a problem that is much discussed and never seems to go away. Related to an issue from 2.5 years ago https://github.com/rism-ch/verovio/issues/190 and from 1.5 weeks ago https://github.com/rism-ch/verovio/issues/974.

The solution, as you point out, is that notes with an implicit sharp must also include the attribute accid.ges="s".

craigsapp commented 5 years ago

Here is a generalized test case:

screen shot 2019-01-13 at 8 48 22 am
@clef:G-2
@keysig:0
@timesig:4/4
@data:'4xGGGG+/GxGGG+/1G+/1G+/4GxG''2G//

All notes except for the last one are G-sharps. The last note is a G nautral.

rettinghaus commented 5 years ago

Here are some things mixed up.

  1. That is not really an encoding problem in MEI (so not really connected to #190 and #974). Instead it has something to do with Plaine & Easie code, that was never made for encoding sound, but what is written.

  2. Plaine & Easie code is for "entering music incipits in modern or mensural notation" (https://www.iaml.info/plaine-easie-code). So the "generalized test case" isn't really one.

  3. The first case could be solved relatively easy. But I'm not sure about a general solution.

  4. There are still other problems in the PAE importer with preliminary (or ugly) fixes.

peterkorgaard commented 5 years ago

I know that PAE was never made in relation to generating MIDI, but I do not think this is the problem.

Within the PAE code is every information needed to convert it to correct MEI with accid.ges. The coversion just need to follow the rules for musical notation. At the moment the generated MEI is incorrect - not in visual apperance, but in terms of the attributes assigned.

Generally I think one of Verovios' strongest features is the possibility to use PAE or MEI - whatever fits the purpose best. If PAE is unable to convert correctly to MEI, and thereby creating propper MIDI, the value downgrades significant.

craigsapp commented 5 years ago

Here are some things mixed up.

OK, then that is not a bug, but should be classified as a feature request.

The primary use of Plaine & Easie is encoding for RISM A/II, which is music manuscripts after 1600. So if there are ambiguities, then the majority interpretation should be for CMN rather than mensuration. And since P&E is for opening measures of a piece of music, it is not common to find written accidentals at the start of mensural music.

Ideally the P&E importer should include an option for how to interpret accidentals.

It is critically related to those two issues: The MEI data in this case must therefore indicate that the accid.ges state is undefined (which is not currently possible to do in an explicit manner). In #974, you say that no @accid.ges means @accid.ges="n", but now you are saying that no @accid.ges means @accid.ges="undefined". There is only 10 days between these two interpretations 😛 Having this ambiguous interpretation of a missing @accid.ges in MEI is bad. This is also why I add @accid.ges if there is no @accid on every single note in the humdrum-to-mei importer.


One possibility is that I could add the Humdrum PAE importer to verovio, similar to the one for MusicXML which is already there.

https://github.com/craigsapp/humextra/blob/master/src-programs/pae2kern.cpp http://extras.humdrum.org/man/pae2kern

The converter does include interpretation of implicit accidentals:

!!!clef:G-2
!!!key:
!!!keysig:0
!!!timesig:4/4
!!!alttimesig:
!!!incipit:'4xGGGG// 
**kern
*MM120
*clefG2
*k[]
*M4/4
4g#
4g#
4g#
4g#
=||
*-

Which then transforms into MEI with the Humdrum importer in verovio as:

<?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="2019-01-13T10:05:06" version="2.0.0-dev-5832f47-dirty">
                    <name>Verovio</name>
                    <p>Transcoded from Humdrum</p>
                </application>
            </appInfo>
        </encodingDesc>
        <workList>
            <work>
                <title />
            </work>
        </workList>
        <extMeta>
            <frames xmlns="http://www.humdrum.org/ns/humxml">
                <metaFrame n="0" token="!!!clef:G-2" xml:id="L1">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>clef</referenceKey>
                        <referenceValue>G-2</referenceValue>
                    </frameInfo>
                </metaFrame>
                <metaFrame n="1" token="!!!key:" xml:id="L2">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>key</referenceKey>
                        <referenceValue />
                    </frameInfo>
                </metaFrame>
                <metaFrame n="2" token="!!!keysig:0" xml:id="L3">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>keysig</referenceKey>
                        <referenceValue>0</referenceValue>
                    </frameInfo>
                </metaFrame>
                <metaFrame n="3" token="!!!timesig:4/4" xml:id="L4">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>timesig</referenceKey>
                        <referenceValue>4/4</referenceValue>
                    </frameInfo>
                </metaFrame>
                <metaFrame n="4" token="!!!alttimesig:" xml:id="L5">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>alttimesig</referenceKey>
                        <referenceValue />
                    </frameInfo>
                </metaFrame>
                <metaFrame n="5" token="!!!incipit:'4xGGGG// " xml:id="L6">
                    <frameInfo>
                        <startTime float="0" />
                        <frameType>reference</frameType>
                        <referenceKey>incipit</referenceKey>
                        <referenceValue>'4xGGGG//</referenceValue>
                    </frameInfo>
                </metaFrame>
            </frames>
        </extMeta>
    </meiHead>
    <music>
        <body>
            <mdiv xml:id="mdiv-0000002047778210">
                <score xml:id="score-0000001435448648">
                    <scoreDef xml:id="scoredef-0000001769908001" midi.bpm="120">
                        <staffGrp xml:id="staffgrp-0000000750382875">
                            <staffDef xml:id="staffdef-0000000324309572" clef.shape="G" clef.line="2" key.sig="0" meter.count="4" meter.unit="4" n="1" lines="5">
                                <label xml:id="label-0000001590776402" />
                            </staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section xml:id="section-L7F1">
                        <measure xml:id="measure-L7" right="dbl" n="0">
                            <staff xml:id="staff-0000001069777701" n="1">
                                <layer xml:id="layer-L7F1N1" n="1">
                                    <note xml:id="note-L12F1" dur="4" oct="4" pname="g" accid="s" />
                                    <note xml:id="note-L13F1" dur="4" oct="4" pname="g" accid.ges="s" />
                                    <note xml:id="note-L14F1" dur="4" oct="4" pname="g" accid.ges="s" />
                                    <note xml:id="note-L15F1" dur="4" oct="4" pname="g" accid.ges="s" />
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>

Notice that it includes the desired accid.ges="s". And renders notation correctly:

screen shot 2019-01-13 at 10 06 01 am

And MIDI export from verovio now sounds correct.

The importer should probably include information about the explicit visual accidentals, which is currently being thrown away (all notes in the Humdrum data are g#, but the first one could be encoded as g#X to indicate that the sharp is written). But my Humdrum-to-MEI converter reconstructs this visual information.

The most precise conversion would be:

!!!clef:G-2
!!!key:
!!!keysig:0
!!!timesig:4/4
!!!alttimesig:
!!!incipit:'4xGGGG// 
**kern
*MM120
*clefG2
*k[]
*M4/4
4g#X
4g#y
4g#y
4g#y
=||
*-

Where X means the accidental is written, and y means that it is interpreted (visually hidden, but sonically audible).

But pae2kern does not yet handle accidentals for tied notes across barlines and needs to treat octave accidentals separately, so its conversion would need adjustment. The current output for the generalized case:

screen shot 2019-01-13 at 10 17 35 am
!!!clef:G-2
!!!key:
!!!keysig:0
!!!timesig:4/4
!!!alttimesig:
!!!incipit:'4xGGGG+/GxGGG+/1G+/1G+/4GxG''2G//
**kern
*MM120
*clefG2
*k[]
*M4/4
4g#
4g#
4g#
[4g#
=
4g]
4g#
4g#
[4g#
=
[1g
=
[1g
=
4g]
4g#
2gg#
=||
*-

The ties are converted properly, but the humdrum-to-MEI converter is refusing to convert them since they are not tieing the same pitches together (g-sharp to g-natural).


If you import ABC notation, what would you do? The encoding of accidentals in ABC notation is also implicit like P&E.

http://abcplus.sourceforge.net/#choralmusic http://abcplus.sourceforge.net/Choral/Ave_Maria_Arcadelt.pdf http://abcplus.sourceforge.net/Choral/Ave_Maria_Arcadelt.abc

screen shot 2019-01-13 at 9 46 41 am

The fourth measure ABC encoding:

[V: S] (BA) !p!G2 |z AGA|(FG) A2|
w: ple -na, Do-mi-nus te -cum,

The B-flat is encoded as B in the data (the first note in the example data).

The MusicXML conversion: http://abcplus.sourceforge.net/Choral/Ave_Maria_Arcadelt.xml

identifies that note as flat:

<measure number="4">
   <note>
      <pitch>
         <step>B</step>
         <alter>-1</alter>
         <octave>4</octave>
      </pitch>
      <duration>120</duration>
      <voice>1</voice>
      <type>quarter</type>
      <notations>
         <slur number="1" type="start"/>
      </notations>
      <lyric number="1">
         <syllabic>single</syllabic>
         <text>ple</text>
      </lyric>
   </note>

And likewise the flat from the signature is added to the MIDI conversion: http://abcplus.sourceforge.net/Choral/Ave_Maria_Arcadelt.mid

rettinghaus commented 5 years ago

@craigsapp I never said a missing @accid.ges would/could/should mean @accid.ges="undefined"! Verovio basically just reacts to explicit accidentals in PAE. (However the actual key written at the beginning is affecting @accid.ges, which I added in #652).

And as I said, the first case, i.e. bar lines reset accidentals, would be really easy to add. Tied notes are a bit tricky.

craigsapp commented 5 years ago

You are saying it here:

Plaine & Easie code is for "entering music incipits in modern or mensural notation" (https://www.iaml.info/plaine-easie-code). So the "generalized test case" isn't really one.

Or you can elaborate on what that means.

rettinghaus commented 5 years ago

That means, that how many notes are affected by an accidental, changed over time. E.g. in the 17th century a bar line not always cancels an accidental.

craigsapp commented 5 years ago

Yes, so this means that in the general case, 100% certainty in @accid.ges for the converter is not possible. To be the most careful, the converter should be able to encode a state meaning "I do not know". You could then apply a particular ruleset for assigning @accid.ges.

craigsapp commented 5 years ago

For amusement, here is a random Tasso musical setting published in 1602:

https://books.google.be/books?id=8cpNAAAAcAAJ&printsec=frontcover&hl=nl&source=gbs_ge_summary_r&cad=0#v=onepage&q&f=false

screen shot 2019-01-13 at 6 36 52 pm

This one is interesting because on the third line of music there are three F-sharps in a row, and every note has a sharp in front of it. I think the general rule around that time was that the sharp would apply to all F's immediately following the first sharped F, but if there were any other pitch, that would cancel the sharp sign even if an F immediately followed that note. When they started putting barlines into the music, the still kept that basic rule and cancelling due to the barline was a later convention.

Modern transcription:

http://www.tassomusic.org/work/?id=Trm0025c

screen shot 2019-01-13 at 6 40 19 pm

[ligature bracket starting in m7 is not added -- but that is now possible in verovio since last month, so I will add it soon]

Here is the Tenor part (no scan available online I think), where there is only one natural to cancel out the flat for a string of B's:

screen shot 2019-01-13 at 6 48 01 pm

But there are two repeated C-sharps in a row that happen to cross a barline in the modern edition by coincidence.

The Alto part has the feature I was describing above:

screen shot 2019-01-13 at 6 57 47 pm

In measure 26 there is a natural written in front of the B, then a C, then a B without a sign, which is interpreted to be a B-flat due to the key signature, and not B-natural due to the intervening C.

Then in the next measure the editor (Emiliano Ricciardi) seems to over interpret by saying that the first F in that measure should be sharped, eventhough only the second one has a written sharp (to generate a leading tone for the cadence).

rettinghaus commented 5 years ago

fixed with 6083eb338e861f373ce4337ff262b88ebfe45ca5