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
673 stars 184 forks source link

Appoggiatura grace-note durations #2579

Open craigsapp opened 2 years ago

craigsapp commented 2 years ago

The new MIDI grace-note feature is great!

Here is an enhancement I think of while testing it:

Screen Shot 2022-01-17 at 5 16 42 AM

The current implementation performs all of these like the last beat in the measure, with the accented grace note stealing the duration of an eighth note from the following quarter note. Preferably the stolen duration is equal to the visual duration of the grace note (so 32nd note on the the first beat, and 16th note on the second beat). Probably check that the stolen duration is no more than 1/2 of the duration of the following note (and use 1/2 duration if trying to take more than 1/2 duration).

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="https://music-encoding.org/schema/dev/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="https://music-encoding.org/schema/dev/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.0.0-dev">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2022-01-17T05:16:44" version="3.9.0-dev-a25f001">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="m3wsodx">
    <score xml:id="snwacpc">
     <scoreDef xml:id="s41hron" midi.bpm="60.000000">
      <staffGrp xml:id="sbdxr2c">
       <staffDef xml:id="sank4xi" n="1" lines="5">
        <clef xml:id="curmko5" shape="G" line="2" />
        <meterSig xml:id="metersig-L2F1" count="3" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="1">
       <staff xml:id="sjam9wz" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L5F1" dur="32" oct="4" pname="e" grace="acc" accid.ges="n" />
         <note xml:id="note-L6F1" dur="4" oct="4" pname="f" accid.ges="n" />
         <note xml:id="note-L7F1" dur="16" oct="4" pname="e" grace="acc" accid.ges="n" />
         <note xml:id="note-L8F1" dur="4" oct="4" pname="f" accid.ges="n" />
         <note xml:id="note-L9F1" dur="8" oct="4" pname="e" grace="acc" accid.ges="n" />
         <note xml:id="note-L10F1" dur="4" oct="4" pname="f" accid.ges="n" />
        </layer>
       </staff>
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
brdvd commented 2 years ago

Yes, for now we basically ignore the duration of the grace notes. Fixing this shouldn't be too difficult. The most general case that must be handled is to rescale beams (with grace notes of different durations) to 1/2 of the principal note, if the beam is trying to steal more than 1/2 of the duration: GraceBeam Wikipedia says that if the principal note is dotted, then 1/2 should be replaced by 2/3. Is that a rule that we should use as well?

rettinghaus commented 2 years ago

For dotted notes the rule is not as rigid as Wikipedia suggests. But if we all agree on it, I think it's fine to do so.

craigsapp commented 2 years ago

Grace notes are not conducive to computational performance since there is leeway by the performer on how to perform them (and may be played differently at different tempos), and also a lot of convention for various repertories that is not encoded into the graphical score. So good luck solving the general case of grace-note (and ornament) MIDI playback 😉

This example:

GraceBeam

Is not grace notes in the musical sense even if it is grace notes in a notational sense (where grace notes mean no time is allotted to them in the score). These grace notes are a form of cadenza, evidenced in particular by the use of variable rhythms within the grace note group. To perform such music, the tempo would be suspended for the quarter note that precedes them:

Accented

Since this is a single-staff example the music could be encoded either as grace notes without score duration or as smaller sized notes with score duration (the latter method would be better for MIDI playback). For a performer, these grace notes would indicate that the music is not to be played in time, and there is a fair amount of improvization in the timing that would be allowed.

Full example from https://github.com/rism-digital/verovio/pull/2577

Here is a related example from real music (Hummel prelude in B major (H dur) op. 67, no. 11):

Screen Shot 2022-01-18 at 9 13 51 AM

In this case the last three grace notes beamed groups will be given extra time for performance that is not present in the score. There is another grace note group above the espress. which is a written-out turn ornament, and this grace note group will not modify the onset times of the notes around it (but will have an expected starting time that matches to a turn).

craigsapp commented 2 years ago

Here is an example where the primary note is dotted (half and quarter):

Screen Shot 2022-01-23 at 12 52 42 PM

I would expect that the grace note takes one eighth note of time from the main note (so 1/3 of the main notes duration goes to the grace note when it is a dotted quarter, and 1/6 of the main note's duration when the grace note is an eighth note and the main note is a dotted half note). In other words, for accented grace notes without slashes, the duration that should be taken away is the visual duration of the grace note (so take away an eighth-note duration for this this example for both dotted quarter notes and dotted half notes.

Click to view MEI data for above example. ```xml </titleStmt> <pubStmt /> </fileDesc> <encodingDesc> <appInfo> <application isodate="2022-01-23T12:52:49" version="3.9.0-dev-a25f001"> <name>Verovio</name> <p>Transcoded from Humdrum</p> </application> </appInfo> </encodingDesc> <workList> <work> <title /> </work> </workList> </meiHead> <music> <body> <mdiv xml:id="m1ug9ht"> <score xml:id="skoeo09"> <scoreDef xml:id="suuxo7k"> <staffGrp xml:id="sw60rnp" bar.thru="true" symbol="brace"> <staffDef xml:id="s2s1mmx" n="1" lines="5"> <clef xml:id="clef-L3F2" shape="G" line="2" /> <keySig xml:id="keysig-L4F2" pname="f" mode="major" sig="1f" /> <meterSig xml:id="metersig-L6F2" count="6" unit="8" /> </staffDef> <staffDef xml:id="scu6for" n="2" lines="5"> <clef xml:id="clef-L3F1" shape="F" line="4" /> <keySig xml:id="keysig-L4F1" pname="f" mode="major" sig="1f" /> <meterSig xml:id="metersig-L6F1" count="6" unit="8" /> </staffDef> </staffGrp> </scoreDef> <section xml:id="section-L1F1"> <pb xml:id="pg9eogm" /> <measure xml:id="measure-L1" n="12"> <staff xml:id="s48flob" n="1"> <layer xml:id="layer-L1F2N1" n="1"> <beam xml:id="beam-L9F2-L14F2"> <note xml:id="note-L9F2" dur="16" oct="4" pname="b" accid.ges="f" /> <note xml:id="note-L10F2" dur="16" oct="4" pname="g" accid.ges="n" /> <note xml:id="note-L11F2" dur="16" oct="4" pname="e" accid.ges="n" /> <note xml:id="note-L12F2" dur="16" oct="4" pname="c" accid="s" /> <note xml:id="note-L13F2" dur="16" oct="4" pname="g" accid="s" /> <note xml:id="note-L14F2" dur="16" oct="4" pname="d" accid.ges="n" /> </beam> <chord xml:id="chord-L15F2" dur="16"> <note xml:id="note-L15F2S1" oct="4" pname="c"> <accid xml:id="accid-L15F2S1" accid="s" func="caution" /> </note> <note xml:id="note-L15F2S2" oct="4" pname="a" accid.ges="n" /> </chord> <beam xml:id="beam-L17F2-L21F2"> <note xml:id="note-L17F2" dur="16" oct="4" pname="a" accid.ges="n" /> <note xml:id="note-L18F2" dur="16" oct="5" pname="c" accid="s" /> <note xml:id="note-L19F2" dur="16" oct="5" pname="e" accid.ges="n" /> <note xml:id="note-L20F2" dur="16" oct="5" pname="a" accid.ges="n" /> <note xml:id="note-L21F2" dur="16" oct="6" pname="c" accid="s" /> </beam> </layer> </staff> <staff xml:id="stigial" n="2"> <layer xml:id="layer-L1F1N1" n="1"> <note xml:id="note-L8F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L9F1" dots="1" dur="2" oct="3" pname="a" accid.ges="n" /> </layer> </staff> </measure> <measure xml:id="measure-L22" n="13"> <staff xml:id="staff-L22F2N1" n="1"> <layer xml:id="layer-L22F2N1" n="1"> <beam xml:id="beam-L25F2-L30F2"> <note xml:id="note-L25F2" dur="16" oct.ges="6" oct="5" pname="b" accid.ges="f" /> <note xml:id="note-L26F2" dur="16" oct.ges="6" oct="5" pname="g" accid.ges="n" /> <note xml:id="note-L27F2" dur="16" oct.ges="6" oct="5" pname="e" accid.ges="n" /> <note xml:id="note-L28F2" dur="16" oct.ges="6" oct="5" pname="c" accid="s" /> <note xml:id="note-L29F2" dur="16" oct.ges="6" oct="5" pname="a" accid.ges="n" /> <note xml:id="note-L30F2" dur="16" oct.ges="6" oct="5" pname="f" accid.ges="n" /> </beam> <beam xml:id="beam-L32F2-L37F2"> <note xml:id="note-L32F2" dur="16" oct.ges="6" oct="5" pname="g" accid.ges="n" /> <note xml:id="note-L33F2" dur="16" oct.ges="6" oct="5" pname="e" accid.ges="n" /> <note xml:id="note-L34F2" dur="16" oct.ges="6" oct="5" pname="c"> <accid xml:id="accid-L34F2" accid="s" func="caution" /> </note> <note xml:id="note-L35F2" dur="16" oct.ges="5" oct="4" pname="a" accid.ges="n" /> <note xml:id="note-L36F2" dur="16" oct.ges="6" oct="5" pname="f" accid.ges="n" /> <note xml:id="note-L37F2" dur="16" oct.ges="6" oct="5" pname="d" accid.ges="n" /> </beam> </layer> </staff> <staff xml:id="staff-L22F1N1" n="2"> <layer xml:id="layer-L22F1N1" n="1"> <note xml:id="note-L23F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L25F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> <note xml:id="note-L31F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L32F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> </layer> </staff> <octave xml:id="of56h2i" staff="1" startid="#note-L25F2" endid="#note-L37F2" dis="8" dis.place="above" /> </measure> <sb xml:id="sb-L40F1" type="original" /> <measure xml:id="measure-L40" n="14"> <staff xml:id="staff-L40F2N1" n="1"> <layer xml:id="layer-L40F2N1" n="1"> <beam xml:id="beam-L43F2-L48F2"> <note xml:id="note-L43F2" dur="16" oct="6" pname="e" accid.ges="n" /> <note xml:id="note-L44F2" dur="16" oct="6" pname="c" accid="s" /> <note xml:id="note-L45F2" dur="16" oct="5" pname="b" accid.ges="f" /> <note xml:id="note-L46F2" dur="16" oct="5" pname="g" accid.ges="n" /> <note xml:id="note-L47F2" dur="16" oct="6" pname="d" accid.ges="n" /> <note xml:id="note-L48F2" dur="16" oct="5" pname="a" accid.ges="n" /> </beam> <beam xml:id="beam-L50F2-L55F2"> <note xml:id="note-L50F2" dur="16" oct="5" pname="b" accid.ges="f" /> <note xml:id="note-L51F2" dur="16" oct="5" pname="g" accid.ges="n" /> <note xml:id="note-L52F2" dur="16" oct="5" pname="e" accid.ges="n" /> <note xml:id="note-L53F2" dur="16" oct="5" pname="c" accid="s" /> <note xml:id="note-L54F2" dur="16" oct="5" pname="a" accid.ges="n" /> <note xml:id="note-L55F2" dur="16" oct="5" pname="f" accid.ges="n" /> </beam> </layer> </staff> <staff xml:id="staff-L40F1N1" n="2"> <layer xml:id="layer-L40F1N1" n="1"> <note xml:id="note-L42F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L43F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> <note xml:id="note-L49F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L50F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> </layer> </staff> <dir xml:id="dir-L41F2" place="above" staff="1" tstamp="1.000000"> <rend xml:id="rwlghn4" fontstyle="normal">loco</rend> </dir> </measure> <measure xml:id="measure-L56" n="15"> <staff xml:id="staff-L56F2N1" n="1"> <layer xml:id="layer-L56F2N1" n="1"> <beam xml:id="beam-L58F2-L63F2"> <note xml:id="note-L58F2" dur="16" oct="5" pname="g" accid.ges="n" /> <note xml:id="note-L59F2" dur="16" oct="5" pname="e" accid.ges="n" /> <note xml:id="note-L60F2" dur="16" oct="5" pname="c" accid="s" /> <note xml:id="note-L61F2" dur="16" oct="4" pname="a" accid.ges="n" /> <note xml:id="note-L62F2" dur="16" oct="5" pname="f" accid.ges="n" /> <note xml:id="note-L63F2" dur="16" oct="5" pname="d" accid.ges="n" /> </beam> <beam xml:id="beam-L65F2-L70F2" type="placed"> <note xml:id="note-L65F2" dur="16" oct="5" pname="e" stem.dir="up" accid.ges="n" /> <note xml:id="note-L66F2" dur="16" oct="5" pname="c" stem.dir="up"> <accid xml:id="accid-L66F2" accid="s" func="caution" /> </note> <note xml:id="note-L67F2" dur="16" oct="4" pname="b" stem.dir="up" accid.ges="f" /> <note xml:id="note-L68F2" dur="16" oct="4" pname="g" stem.dir="up" accid.ges="n" /> <note xml:id="note-L69F2" dur="16" oct="5" pname="d" stem.dir="up" accid.ges="n" /> <note xml:id="note-L70F2" dur="16" oct="4" pname="a" stem.dir="up" accid.ges="n" /> </beam> </layer> </staff> <staff xml:id="staff-L56F1N1" n="2"> <layer xml:id="layer-L56F1N1" n="1"> <note xml:id="note-L57F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L58F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> <note xml:id="note-L64F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L65F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> </layer> </staff> </measure> <measure xml:id="measure-L71" n="16"> <staff xml:id="staff-L71F2N1" n="1"> <layer xml:id="layer-L71F2N1" n="1"> <beam xml:id="beam-L73F2-L78F2"> <note xml:id="note-L73F2" dur="16" oct="4" pname="b" accid.ges="f" /> <note xml:id="note-L74F2" dur="16" oct="4" pname="g" accid.ges="n" /> <note xml:id="note-L75F2" dur="16" oct="4" pname="e" accid.ges="n" /> <note xml:id="note-L76F2" dur="16" oct="4" pname="c" accid="s" /> <note xml:id="note-L77F2" dur="16" oct="4" pname="g" accid="s" /> <note xml:id="note-L78F2" dur="16" oct="4" pname="d" accid.ges="n" /> </beam> <chord xml:id="chord-L79F2" dur="4"> <note xml:id="note-L79F2S1" oct="4" pname="c"> <accid xml:id="accid-L79F2S1" accid="s" func="caution" /> </note> <note xml:id="note-L79F2S2" oct="4" pname="a" accid.ges="n" /> </chord> <rest xml:id="rest-L81F2" dur="8" /> </layer> </staff> <staff xml:id="staff-L71F1N1" n="2"> <layer xml:id="layer-L71F1N1" n="1"> <note xml:id="note-L72F1" dur="8" oct="2" pname="a" grace="acc" accid.ges="n" /> <note xml:id="note-L73F1" dots="1" dur="4" oct="3" pname="a" accid.ges="n" /> <beam xml:id="beam-L79F1-L81F1"> <note xml:id="note-L79F1" dur="8" oct="3" pname="a" accid.ges="n"> <artic xml:id="artic-L79F1" artic="stacciss" /> </note> <note xml:id="note-L80F1" dur="8" oct="3" pname="e" accid.ges="n"> <artic xml:id="artic-L80F1" artic="stacciss" /> </note> <note xml:id="note-L81F1" dur="8" oct="3" pname="c" accid="s"> <artic xml:id="artic-L81F1" artic="stacciss" /> </note> </beam> </layer> </staff> </measure> </section> </score> </mdiv> </body> </music> </mei> ``` </details> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/rettinghaus"><img src="https://avatars.githubusercontent.com/u/7693447?v=4" />rettinghaus</a> commented <strong> 2 years ago</strong> </div> <div class="markdown-body"> <p>The Appoggiatura is normally played on the beat. Its value depends on context but in the baroque era, usually takes half or even two-thirds of the value of the written note:</p> <p><img src="https://artscimedia.case.edu/wp-content/uploads/sites/135/2016/09/30201355/Orn9a.Appogg-300x69.jpg" alt="" /></p> <p>One special form of the appoggiatura is known as the Coulé, namely one that fills in between descending thirds, either before the beat or on the beat (Ex.9b):</p> <p><img src="https://artscimedia.case.edu/wp-content/uploads/sites/135/2016/09/30201353/Orn9b.Coule_-300x57.jpg" alt="" /></p> <p>See: <a href="https://casfaculty.case.edu/ross-duffin/muhi-ornamentation-i-agrements/muhi-3441-baroque-ornamentation-i-description-introduction/">https://casfaculty.case.edu/ross-duffin/muhi-ornamentation-i-agrements/muhi-3441-baroque-ornamentation-i-description-introduction/</a></p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/craigsapp"><img src="https://avatars.githubusercontent.com/u/3487289?v=4" />craigsapp</a> commented <strong> 2 years ago</strong> </div> <div class="markdown-body"> <p>The problem with implementing grace notes in the MIDI conversion will be that people will want to hear them played correctly 😉 </p> <blockquote> <p>One special form of the appoggiatura is known as the Coulé, namely one that fills in between descending thirds, either before the beat or on the beat (Ex.9b):</p> </blockquote> <p><img src="https://artscimedia.case.edu/wp-content/uploads/sites/135/2016/09/30201353/Orn9b.Coule_-300x57.jpg" alt="" /></p> <p>Is this the same as Nachschlag grace notes? (Example given on the page <a href="https://github.com/rism-digital/verovio/pull/2577">https://github.com/rism-digital/verovio/pull/2577</a>). Based on the "nach" part of the name, I am suspecting that it can only be the unaccented interpretation in measure 2.</p> <p>A problem is that the Coulé can be both accented or unaccented, and there is currently no way of representing such a grace note in MEI (grace ntoes have to be either one or the other). Perhaps using something like <code>form="coule"</code> would be a way of clarifying grace note gestural renderings?</p> <p>For the unaccented version, based on the Nachschlag example referenced above:</p> <pre><code class="language-xml"><note dur="4" oct="5" pname="e" accid.ges="n" /> <graceGrp grace="unacc" attach="pre"> <note dur="8" oct="5" pname="d" accid.ges="n" /> </graceGrp> <note dur="4" oct="5" pname="c" accid.ges="n" /> <graceGrp grace="unacc" attach="pre"> <note dur="8" oct="4" pname="b" accid.ges="n" /> </graceGrp> <note dur="2" oct="4" pname="a" accid.ges="n" /></code></pre> <p>Perhaps <code>@dur.ges</code> can be used to specify the performance duration of grace notes (or some other duration parameter, particularly if the duration is not a proportional duration)? By default <code>@dur</code> will be taken from the adjacent regular note, and <code>@dur.ges</code> can be used when it is another value. For the above example where 16th note duration should be stolen:</p> <pre><code class="language-xml"><note dur="4" oct="5" pname="e" accid.ges="n" /> <graceGrp grace="unacc" attach="pre"> <note dur="8" dur.ges="16" oct="5" pname="d" accid.ges="n" /> </graceGrp> <note dur="4" oct="5" pname="c" accid.ges="n" /> <graceGrp grace="unacc" attach="pre"> <note dur="8" dur.ges="16" oct="4" pname="b" accid.ges="n" /> </graceGrp> <note dur="2" oct="4" pname="a" accid.ges="n" /></code></pre> <p>This will minimize the need for interpretation that verovio or other performance rendering software would need in order to play MEI encodings.</p> <p>Also note that verovio uses <code>@dur.ges</code> for time alignment between notes in different layers/staves, Using grace notes to use <code>@dur.ges</code> for performance rendering should be OK if verovio ignores that parameter for cross-layer/staff alignments.</p> <p>The definition of <code>unacc</code> on this page is interesting: <a href="https://music-encoding.org/guidelines/v4/data-types/data.grace.html">https://music-encoding.org/guidelines/v4/data-types/data.grace.html</a></p> <p>Acciaccaturas are currently encoded as <code>@grace="unacc"</code>, but this state would also fit the definition of Nachschläge? The main difference is that a Nachschlag is played in time (such as as a 16th note in the above example), while an acciaccatura is typically "as short as possible" (and would not change duration much if the tempo changes).</p> </div> </div> <div class="page-bar-simple"> </div> <div class="footer"> <ul class="body"> <li>© <script> document.write(new Date().getFullYear()) </script> Githubissues.</li> <li>Githubissues is a development platform for aggregating issues.</li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script> <script src="/githubissues/assets/js.js"></script> <script src="/githubissues/assets/markdown.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.4.0/build/languages/go.min.js"></script> <script> hljs.highlightAll(); </script> </body> </html>