cemfi / meico

A converter framework with support for MEI, MSM, MPM, MIDI, WAV, MP3, chroma, and XSLT
GNU General Public License v3.0
69 stars 14 forks source link

Support for <supplied> #17

Closed ndubo closed 4 years ago

ndubo commented 6 years ago

Meico seems to skip elements wrapped by a supplied element. This becomes particularly evident in cases of editorial additions of accidentals. In the following example, all three notes should sound alike, but Meico plays the third one as c natural. In the first measure, the sharp is coded as an attribute, in the second measure as an element which is an equivalent to the coding in the first measure; in the third measure, the sharp is wrapped by a supplied element, marking it as an editorial addition. The sounding pitch should remain the same, i.e. c sharp. As a fix, Meico should just ignore the supplied element, leaving the accid intact however.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://dme.mozarteum.at/DME/music/schemas/mei-CMN_3.0.0.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<mei xmlns="http://www.music-encoding.org/ns/mei"
 meiversion="3.0.0">
    <meiHead>
        <fileDesc>
            <titleStmt>
                <title>an example</title>
            </titleStmt>
            <pubStmt/>
        </fileDesc>
    </meiHead>
    <music>
        <body>
            <mdiv>
                <score>
                    <scoreDef meter.count="4" meter.unit="4" meter.sym="common">
                        <staffGrp>
                            <staffDef n="1" lines="5" clef.shape="G" clef.line="2"></staffDef>
                        </staffGrp>
                    </scoreDef>
                    <section>
                        <measure n="1">
                            <staff n="1">
                                <layer n="1">
                                    <note pname="c" oct="4" dur="1" accid="s"/>
                                </layer>
                            </staff>
                        </measure>
                        <measure n="2">
                            <staff n="1">
                                <layer n="1">
                                    <note pname="c" oct="4" dur="1"><accid accid="s"/></note>
                                </layer>
                            </staff>
                        </measure>
                        <measure n="3">
                            <staff n="1">
                                <layer n="1">
                                    <note pname="c" oct="4" dur="1"><supplied><accid accid="s"/></supplied></note>
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>
axelberndt commented 6 years ago

The supplied environment was not the problem. Your accid elements do not provide attributes oloc and ploc. So they are taken from the parent note and here something went wrong. It should be fixed with meico version 0.3.4. Could you test it on your data? Thanks.

TillReininghaus commented 4 years ago

Sorry for reopening this issue again, but in the recent MEICO version I have found the following bug. In encodings with the child elements <supplied><accid/></supplied> which reference to with @oct.ges, the element is obviously not recognized correctly by Meico and therefore played incorrectly (in the example Mozart, String Quartet K. 589, mvt 1, m. 30: "e flat" instead of "e"). grafik

<?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 meiversion="4.0.0" xmlns="http://www.music-encoding.org/ns/mei">
    <meiHead>
        <fileDesc>
            <titleStmt>
                <title>example K. 589/1, m. 30, Vc - note with @oct.ges and supplied accid</title>
            </titleStmt>
            <pubStmt/>
        </fileDesc>
    </meiHead>
    <music>
        <body>
            <mdiv n="1">
                <score>
                    <scoreDef key.accid="f" key.mode="major" key.pname="b" key.sig="2f" meter.count="3" meter.unit="4" optimize="false">
                        <staffGrp bar.thru="true" symbol="bracket">
                            <staffDef clef.line="4" clef.shape="F" lines="5" n="1"/>
                        </staffGrp>
                    </scoreDef>
                    <section>
                        <measure n="28">
                            <staff n="1">
                                <layer n="1">
                                    <mRest tstamp="1" xml:id="mRest_9906"/>
                                    <clef line="2" shape="G" tstamp="3.5" xml:id="clef_9912"/>
                                </layer>
                            </staff>
                        </measure>
                        <measure n="29">
                            <staff n="1">
                                <layer n="1" xml:id="layer_10062">
                                    <note dur="4" oct="5" oct.ges="4" pname="c" tstamp="1" xml:id="note_10068"/>
                                    <note accid="n" dur="4" oct="5" oct.ges="4" pname="e" tstamp="2" xml:id="note_10074"/>
                                    <note dur="4" oct="5" oct.ges="4" pname="g" tstamp="3" xml:id="note_10080"/>
                                </layer>
                            </staff>
                        </measure>
                        <measure n="30">
                            <staff n="1">
                                <layer n="1" xml:id="layer_10242">
                                    <note accid.ges="f" dur="2" oct="5" oct.ges="4" pname="b" tstamp="1" xml:id="note_10248"/>
                                    <beam xml:id="beam_10254">
                                        <note dur="8" oct="5" oct.ges="4" pname="g" tstamp="3" xml:id="note_10260"/>
                                        <note color="red" dur="8" oct="5" oct.ges="4" pname="e" tstamp="3.5" xml:id="note_10266">
                                            <supplied xml:id="supplied_10266">
                                                <accid accid="n" xml:id="accid_10266"/>
                                            </supplied>
                                        </note>
                                    </beam>
                                </layer>
                            </staff>
                        </measure>
                        <measure n="31">
                            <staff n="1">
                                <layer n="1" xml:id="layer_10488">
                                    <note dur="4" oct="5" oct.ges="4" pname="f" tstamp="1" xml:id="note_10494"/>
                                    <rest dur="4" tstamp="2" xml:id="rest_10500"/>
                                    <rest dur="4" tstamp="3" xml:id="rest_10506"/>
                                </layer>
                            </staff>
                        </measure>
                    </section>
                </score>
            </mdiv>
        </body>
    </music>
</mei>
axelberndt commented 4 years ago

Thanks for reporting the bug. I will look into it.

axelberndt commented 4 years ago

Found the problem. This is a typical MEI ambiguity situation, basically a mishmash of information from the graphical and gestural domain where meico has to make the most reasonable choice, even though counter-intuitive at first glance.

When processing the <accid>, meico looks at the parent to determine the pitch and octave which the <accid> is aplied to. In this case, there is an oct="5" and an oct.ges="4". Since the <accid> is (in MEI) a graphical domain element, meico chooses the @oct to assign the accidental to an octave, thus staying consistent with this domain. It would take the @oct.ges only in the absence of @oct as a fallback.

In other words, as <accid> comes from the graphical domain meico first tries to stay consistent within this domain when processing it. Within this domain, the <accid> belongs to oct="5". However, when computing the performed pitch of the note, meico prioritizes the gestural domain which means that the <note> is at oct.ges="4". But there is nothing telling meico, that there is an accidental at this octave level or that the graphical <accid> should also be applied to this gestural octave level.

MEI-wise the semantically correct encoding for this case would be to add accid.ges="n" to the <note>.

axelberndt commented 4 years ago

Hm, ... I keep thinking about it. What I might do is, that such an accidental can propagate up to the <note> and put in an @accid. Then, meico would play this <note> with the accidental but subsequent notes will ignore it for the reason explained above. Is this a desired behavior? This needs discussion.

ndubo commented 4 years ago

@axelberndt, I think, you outlined the solution already. MEICO could simply take the information in all <accid> and process them internally as @accid (and ignore the <supplied> anyway). Yes, this is the desired behavior. In my opinion, however, the reasoning behind it is a different one. The coding of the alteration with the element <accid> (as a child element to <note>) is equivalent to the coding with the attribute @accid in <note> (second note in m. 29). In order to be able to address the alteration (in this case it is necessary because it is added by means of the element <supplied>), the coding with <accid> was the only choice for the red note. From a semantic or logical viewpoint, there is no difference; and the coding with <accid> has nothing to do with the graphical domain. Most importantly, <accid> is a child element to <note>, it descibes a property of the note. As such it does not need to have an @oct assigned. BTW, as a background information to the example given by @TillReininghaus . This is what some people call a “false treble clef” which is often found in cello parts. The section in the treble clef is supposed to sound an octave lower than notated. In MEI, this can easily be expressed through @oct=”x” plus @oct.ges=”x-1”. Thus for the acoustic output, MEICO should always allow to overwrite @oct by @oct.ges.

axelberndt commented 4 years ago

I have implemented this and tested it. ... Basically, it works in your case BUT the following will sooner or later happen. Therefor, you have to keep in mind that <note> will propagate the accidental throughout the whole measure.

  1. Subsequent notes on the same gestural pitch and in the same measure will not take notice of the accidental because it is specified for a different pitch.
  2. Subsequent notes on that other pitch will take notice of the accidental an be altered.

What I did so far was keeping the ambiguous <accid> out of the game. But now, once I propagate it to the parent note's @accid, it is in, with all the consequences.

By the way, the same happens with the regular <note> elements' @accid attributes when they are masked by @accid.ges. You have an accidental in play that alters subsequent notes even though the note where it comes in plays on another pitch than it is printed.

I am surprised that no one stumbled over this, yet. With this new change, however, it becomes more likely to happen. Before I make a new release I would like to hear some opinions.

kepper commented 4 years ago

Having been asked on separate channels to comment on this, I don't really see the problem of propagation. If someone supplies @*.ges, I believe it's safe to assume that this has been done consistently – if a subsequent note is supposed to be affected by an accidental, octave shift, whatever, it will carry the required @*.ges attributes as well. I believe it would be an extremely bad habit to use those attributes only occasionally, and someone doing that should not expect software to correctly guess the meaning of her or his encodings. From what I read, meico currently tries to carry over logic required for processing a purely graphical encoding over to one which also has gestural information – which I think is already a step too far. I doubt that any solution in this territory can really satisfy everyone, while strictly sticking to values provided by @*.ges is a very easy position to explain and defend. Just a first thought, though… 

axelberndt commented 4 years ago

Alright, thanks Till for reporting the issue and thanks Norbert and Johannes for your opinions, as always very plausible and convincing. The new release version is online.