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

Feature Request: Capo #1245

Closed earboxer closed 4 years ago

earboxer commented 4 years ago

Since we've implemented transposition, another helpful feature would be adding Capo'd versions of chords for guitarists (and other stringed instruments that use a capo).

It should receive a non-zero positive integer (the number of frets this is being capo'd), and for each chord symbol (harm element), it should add a transposed chord symbol (transposed down the number of semitones that the capo is set to).

Example:

Screen Shot 2019-12-20 at 11 58 06
craigsapp commented 4 years ago

Now you are trying to sneak in the transposition by semitone that I was complaining about initially 😛

I will implement Transposer class functionality to do what you want. This will also make it possible to do what you originally wanted, such as verovio --transpose +3 to transpose up 3 semitones. This system should be further enhanced to check for automatic enharmonic transpositions at every key / key signature change throughout the score if you want it to be fully generalized, but just doing assigning a single transposition at the start of the music will work in about 99% of real-world cases.

For automating the generation of capo knowledge inside of verovio, that will probably be dependent on having some formal way of indicating it in MEI. The informal way to indicate that a harm is a capo is to add harm@type="capo" (which is already possible in MEI). A more formal way would be to allow harm@form="capo" (should probably be formally allowed in MEI) or possibly harm@class="capo" (@class is probably free-form, so could be set to capo without any change in the MEI spec.). When using harm@type="capo", the @type attribute will be mapped to <g class="harm capo"> in the SVG rendering, so you could also stylize the capo in the SVG with CSS (change its color or hide it, for example).

A problem that you would have to negotiate with @lpugin is that you seem to want to generate the capo chords automatically from the regular chords. This is a transformation of the data which is not generally done inside of verovio (except that I get special dispensation to do it in the Humdrum importer 😉 ), but this sort of thing would be done with external processing data. MEI sorts of people would suggest using XSLT to do such a thing (in which case you would have to implement the transposition algorithms in XSLT). In the worst case, an independent C++ program that uses pugixml to load MEI data and borrows the Transposer class could be implemented and compiled to javascript with emscripten. The MEI data would then be passed through your preprocessor before being passed to veorvio.

If you are not planning on automatically inserting the capo harmony elements, but rather change existing elements to a new capo transposition, then adding an extra option to verovio such as --capo -3 seems more possible. This option would only transpose element that have a @type="capo" attribute on the transposable element. Or perhaps the options would be --transpose -3 --capo or --transpose -3 --type capo, or use of an XPath option in conjunction with the transpose option. (which can be discussed further with @lpugin).

If the capo is already inserted into the MEI data, then the verovio --transpose option will work on it correctly, assuming you are not changing the capo transposition amount. For example of G minor music is transposed to C minor, then the real chord on the first note will be changed Cm and the capo will be changed to Am.


Below are charts for transposition by semitones. For semitone transposition, there will ideally be enharmonic adjustments to choose between alternate possible notations. For each semitone transposition, there are two possible transpositions that could be done (or at least two reasonable ones). The example initially discussed was a +1 transposition which would convert C major into either C-sharp major or D-flat major. The best choice will be D-flat major since that add 5 flats while the C-sharp major transposition adds 7 sharps. In other words, there will be two options to choose for the transposition, and the option that gives the least number of accidentals will be the optimal solution (which will usually, but not always, be desirable).

The first column is the number of semitones to transpose by. The second column lists the two possible enharmonically equivalent key signature adjustments related to that semitone transposition, and the third column is the chromatic interval related to the circle-of-fifths number in the second column. When doing a transposition by semitone, you would take the key signature in the music as a circle-of-fifths value and add each of the values in the second column to this, and then choose the resulting value for the circle-of-fifths for a new key signature which is closest to zero (so a transposition to D-flat major will always be preferred to C-sharp major).

Once the option of when circle-of-fifths value is decided on, then the chromatic interval in the third column can be used to perform the transposition as usual with the existing code.

Example: Suppose that you are in C major and want to transpose up five semitones. The starting key signature is "0" and the two fifth-transpositions are "-1" and "+11". 0-1=-1 is a better choice than 0+11=11 since -1 is closer to 0 than 11 is. Therefore the chromatic transposition to select is +P4 rather than +A3.

Here is an example that involves enharmonic wrapping: Suppose that you are in C-sharp major and want to transpose up 7 semitones. The starting key signature is "7" and the two fifth-transpositions are "+1" and "-11". 7+1=8 is not as good as 7-11=-4, so the best transposition value will be +d6.

Here is the table for positive semitone transpositions:

Semi 5ths Interval Example
0 0 P1 C => C
+1 -5 +m2 C => D-flat
+1 +7 +A1 C => C-sharp
+2 +2 +M2 C => D
+2 -10 +d3 C => E-flat-flat
+3 -3 +m3 C => E-flat
+3 +9 +A2 C => C-sharp-sharp
+4 +4 +M3 C => E
+4 -8 +d4 C => F-flat
+5 -1 +P4 C => F
+5 +11 +A3 C => E-sharp
+6 +6 +A4 C => F-sharp
+6 -6 +d5 C => G-flat
+7 +1 +P5 C => G
+7 -11 +d6 C => A-flat-flat
+8 -4 +m6 C => A-flat
+8 +8 +A5 C => G-sharp
+9 +3 +M6 C => A
+9 -9 +d7 C => B-flat-flat
+10 -2 +m7 C => B-flat
+10 +10 +A6 C => A-sharp
+11 +5 +M7 C => B
+11 -7 +d8 C => C-flat

And here is the table for negative semitone transpositions (same as above, but the signs are all negated):

Semi 5ths Interval Example
0 0 P1 C => C
-1 +5 -m2 C => B
-1 -7 -A1 C => C-flat
-2 -2 -M2 C => B-flat
-2 +10 -d3 C => A-sharp
-3 +3 -m3 C => A
-3 -9 -A2 C => B-flat-flat
-4 -4 -M3 C => A-flat
-4 +8 -d4 C => G-sharp
-5 +1 -P4 C => G
-5 -11 -A3 C => A-flat-flat
-6 -6 -A4 C => G-flat
-6 +6 -d5 C => F-sharp
-7 -1 -P5 C => F
-7 +11 -d6 C => E-sharp-sharp
-8 +4 -m6 C => E
-8 -8 -A5 C => F-flat
-9 -3 -M6 C => E-flat
-9 +9 -d7 C => D-sharp
-10 +2 -m7 C => D
-10 -10 -A6 C => E-flat-flat
-11 -5 -M7 C => D-flat
-11 +7 -d8 C => C-sharp
craigsapp commented 4 years ago

Semitone transposition is implemented in commit https://github.com/rism-ch/verovio/commit/dee9436ce6ec3cb6e8662fb26d22c03e971b2e1f . This commit does not deal with capo directly, but adds functionality to the Transposer class to allow for capo to be implemented.

As a by-product of the semitone transposition functions, the --transpose option for verovio now allows for semitone transposition.

Here is an example of transposing a sample of music up and down two semitones:

Here is the original notation:

Screen Shot 2019-12-20 at 5 53 45 PM

MEI data:

<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-20T00:42:44" version="2.4.0-dev-2bbba3e-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001424087937">
    <score xml:id="score-0000000940711344">
     <scoreDef xml:id="scoredef-0000000256908801">
      <staffGrp xml:id="staffgrp-0000000789733026">
       <staffDef xml:id="staffdef-0000001011347073" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" pname="d" mode="minor" sig="1f" />
        <meterSig xml:id="metersig-L5F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="0">
       <staff xml:id="staff-0000000197682131" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <beam xml:id="beam-L7F1-L10F1">
          <note xml:id="note-L7F1" dur="16" oct="4" pname="d" accid.ges="n" />
          <note xml:id="note-L8F1" dur="16" oct="4" pname="e" accid.ges="n" />
          <note xml:id="note-L9F1" dur="16" oct="4" pname="f" accid.ges="n" />
          <note xml:id="note-L10F1" dur="16" oct="4" pname="g" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L11F1-L12F1">
          <note xml:id="note-L11F1" dur="8" oct="4" pname="a" accid.ges="n" />
          <note xml:id="note-L12F1" dur="8" oct="5" pname="d" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L13F1-L14F1">
          <note xml:id="note-L13F1" dur="8" oct="5" pname="c" accid="s" />
          <note xml:id="note-L14F1" dur="8" oct="4" pname="a" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L15F1-L16F1">
          <note xml:id="note-L15F1" dur="8" oct="4" pname="e" accid.ges="n" />
          <note xml:id="note-L16F1" dur="8" oct="4" pname="g" accid.ges="n" />
         </beam>
        </layer>
       </staff>
      </measure>
      <measure xml:id="measure-L17" n="2">
       <staff xml:id="staff-L17F1N1" n="1">
        <layer xml:id="layer-L17F1N1" n="1">
         <beam xml:id="beam-L18F1-L19F1">
          <note xml:id="note-L18F1" dur="8" oct="4" pname="f" accid="s" />
          <note xml:id="note-L19F1" dur="8" oct="4" pname="d" accid.ges="n" />
         </beam>
         <note xml:id="note-L20F1" dur="4" oct="5" pname="c">
          <accid xml:id="accid-L20F1" accid="n" func="caution" />
         </note>
         <beam xml:id="beam-L21F1-L23F1">
          <note xml:id="note-L21F1" dur="8" oct="5" pname="c" accid.ges="n" />
          <note xml:id="note-L22F1" dur="16" oct="4" pname="b" accid="n" />
          <note xml:id="note-L23F1" dur="16" oct="4" pname="a" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L24F1-L25F1">
          <note xml:id="note-L24F1" dur="8" oct="4" pname="b" accid.ges="n" />
          <note xml:id="note-L25F1" dur="8" oct="4" pname="g" accid.ges="n" />
         </beam>
        </layer>
       </staff>
       <tie xml:id="tie-L20F1-L21F1" startid="#note-L20F1" endid="#note-L21F1" />
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

Here is transposition up two semitones:

verovio --transpose 2 test.mei
Screen Shot 2019-12-20 at 5 52 05 PM

MEI data for transposition up two semitones:

<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-20T00:42:44" version="2.4.0-dev-2bbba3e-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001424087937">
    <score xml:id="score-0000000940711344">
     <scoreDef xml:id="scoredef-0000000256908801">
      <staffGrp xml:id="staffgrp-0000000789733026">
       <staffDef xml:id="staffdef-0000001011347073" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" accid="n" pname="e" mode="minor" sig="1s" />
        <meterSig xml:id="metersig-L5F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="0">
       <staff xml:id="staff-0000000197682131" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <beam xml:id="beam-L7F1-L10F1">
          <note xml:id="note-L7F1" dur="16" oct="4" pname="e" accid.ges="n" />
          <note xml:id="note-L8F1" dur="16" oct="4" pname="f" accid.ges="s" />
          <note xml:id="note-L9F1" dur="16" oct="4" pname="g" accid.ges="n" />
          <note xml:id="note-L10F1" dur="16" oct="4" pname="a" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L11F1-L12F1">
          <note xml:id="note-L11F1" dur="8" oct="4" pname="b" accid.ges="n" />
          <note xml:id="note-L12F1" dur="8" oct="5" pname="e" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L13F1-L14F1">
          <note xml:id="note-L13F1" dur="8" oct="5" pname="d" accid="s" />
          <note xml:id="note-L14F1" dur="8" oct="4" pname="b" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L15F1-L16F1">
          <note xml:id="note-L15F1" dur="8" oct="4" pname="f" accid.ges="s" />
          <note xml:id="note-L16F1" dur="8" oct="4" pname="a" accid.ges="n" />
         </beam>
        </layer>
       </staff>
      </measure>
      <measure xml:id="measure-L17" n="2">
       <staff xml:id="staff-L17F1N1" n="1">
        <layer xml:id="layer-L17F1N1" n="1">
         <beam xml:id="beam-L18F1-L19F1">
          <note xml:id="note-L18F1" dur="8" oct="4" pname="g" accid="s" />
          <note xml:id="note-L19F1" dur="8" oct="4" pname="e" accid.ges="n" />
         </beam>
         <note xml:id="note-L20F1" dur="4" oct="5" pname="d">
          <accid xml:id="accid-L20F1" accid="n" func="caution" />
         </note>
         <beam xml:id="beam-L21F1-L23F1">
          <note xml:id="note-L21F1" dur="8" oct="5" pname="d" accid.ges="n" />
          <note xml:id="note-L22F1" dur="16" oct="5" pname="c" accid="s" />
          <note xml:id="note-L23F1" dur="16" oct="4" pname="b" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L24F1-L25F1">
          <note xml:id="note-L24F1" dur="8" oct="5" pname="c" accid.ges="s" />
          <note xml:id="note-L25F1" dur="8" oct="4" pname="a" accid.ges="n" />
         </beam>
        </layer>
       </staff>
       <tie xml:id="tie-L20F1-L21F1" startid="#note-L20F1" endid="#note-L21F1" />
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

Here is transposition down two semitones:

verovio --transpose -2 test.mei
Screen Shot 2019-12-20 at 5 56 51 PM

MEI data:

<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-20T00:42:44" version="2.4.0-dev-2bbba3e-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001424087937">
    <score xml:id="score-0000000940711344">
     <scoreDef xml:id="scoredef-0000000256908801">
      <staffGrp xml:id="staffgrp-0000000789733026">
       <staffDef xml:id="staffdef-0000001011347073" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" accid="n" pname="c" mode="minor" sig="3f" />
        <meterSig xml:id="metersig-L5F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" n="0">
       <staff xml:id="staff-0000000197682131" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <beam xml:id="beam-L7F1-L10F1">
          <note xml:id="note-L7F1" dur="16" oct="4" pname="c" accid.ges="n" />
          <note xml:id="note-L8F1" dur="16" oct="4" pname="d" accid.ges="n" />
          <note xml:id="note-L9F1" dur="16" oct="4" pname="e" accid.ges="f" />
          <note xml:id="note-L10F1" dur="16" oct="4" pname="f" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L11F1-L12F1">
          <note xml:id="note-L11F1" dur="8" oct="4" pname="g" accid.ges="n" />
          <note xml:id="note-L12F1" dur="8" oct="5" pname="c" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L13F1-L14F1">
          <note xml:id="note-L13F1" dur="8" oct="4" pname="b" accid="n" />
          <note xml:id="note-L14F1" dur="8" oct="4" pname="g" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L15F1-L16F1">
          <note xml:id="note-L15F1" dur="8" oct="4" pname="d" accid.ges="n" />
          <note xml:id="note-L16F1" dur="8" oct="4" pname="f" accid.ges="n" />
         </beam>
        </layer>
       </staff>
      </measure>
      <measure xml:id="measure-L17" n="2">
       <staff xml:id="staff-L17F1N1" n="1">
        <layer xml:id="layer-L17F1N1" n="1">
         <beam xml:id="beam-L18F1-L19F1">
          <note xml:id="note-L18F1" dur="8" oct="4" pname="e" accid="n" />
          <note xml:id="note-L19F1" dur="8" oct="4" pname="c" accid.ges="n" />
         </beam>
         <note xml:id="note-L20F1" dur="4" oct="4" pname="b">
          <accid xml:id="accid-L20F1" accid="f" func="caution" />
         </note>
         <beam xml:id="beam-L21F1-L23F1">
          <note xml:id="note-L21F1" dur="8" oct="4" pname="b" accid.ges="f" />
          <note xml:id="note-L22F1" dur="16" oct="4" pname="a" accid="n" />
          <note xml:id="note-L23F1" dur="16" oct="4" pname="g" accid.ges="n" />
         </beam>
         <beam xml:id="beam-L24F1-L25F1">
          <note xml:id="note-L24F1" dur="8" oct="4" pname="a" accid.ges="n" />
          <note xml:id="note-L25F1" dur="8" oct="4" pname="f" accid.ges="n" />
         </beam>
        </layer>
       </staff>
       <tie xml:id="tie-L20F1-L21F1" startid="#note-L20F1" endid="#note-L21F1" />
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
earboxer commented 4 years ago

If you are not planning on automatically inserting the capo harmony elements, but rather change existing elements to a new capo transposition, then adding an extra option to verovio such as --capo -3 seems more possible. This option would only transpose element that have a @type="capo" attribute on the transposable element. Or perhaps the options would be --transpose -3 --capo or --transpose -3 --type capo, or use of an XPath option in conjunction with the transpose option. (which can be discussed further with @lpugin).

I think a separate capo option would be good so that transposition and capoing can happen independently client-side.

Something like --transpose +G --transpose-capo -3

Though I'm afraid having a separate --capo or --transpose-capo option would be confusing if it only operated on existing elements (but I see the benefit in simplicity by having the elements created before passing to verovio). An issue with having the elements existing is that even if they could be hidden with CSS, they would still affect the layout (and thus affect how the score is paginated).

craigsapp commented 4 years ago

Another possibility for an interface would be to add an XPath option for transposition. Something like:

transpose --transpose +2 --transpose-x-path-query '//*[@type="capo"]'

This would mean transpose up two semitones only transposable elements that have a type attribute set to the value "capo".

If you need to both transpose the music and transpose the capo at the same time, then two calls to verovio would be needed: (1) to transpose the music (and capo), and (2) another one to adjust the capo to a different semitone amount:

First transpose everything (including capo) up two semitones:

verovio input.mei -atmei --transpose +2 -o intermediate

Then transpose only the capo to a different semitone amount:

verovio intermediate.mei --transpose -3 --transpose-x-path-query '//*[@type="capo"]' -o output

Here is some example data, with the "G" chord label marked with @type="capo":

Screen Shot 2019-12-23 at 12 23 59
<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-23T12:00:11" version="2.4.0-dev-0a49d87-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000689297044">
    <score xml:id="score-0000001488626590">
     <scoreDef xml:id="scoredef-0000002116859099" midi.bpm="400">
      <staffGrp xml:id="staffgrp-0000000819520704">
       <staffDef xml:id="staffdef-0000000042314007" n="1" lines="5">
        <clef xml:id="clef-0000001316899658" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" pname="c" mode="major" sig="0" />
        <meterSig xml:id="metersig-L2F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" right="end" n="0">
       <staff xml:id="staff-0000000225970676" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L6F1" dur="1" oct="4" pname="c" accid.ges="n" />
        </layer>
       </staff>
       <harm xml:id="harm-L6F2" staff="1" tstamp="1.000000" n="2">C</harm>
       <harm xml:id="harm-L6F3" type="capo" staff="1" tstamp="1.000000" n="3">G</harm>
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

Transposing "+M2", "D", or "+2" (currently implemented):

verovio --transpose M2 input.mei -o intermediate
verovio --transpose D input.mei -o intermediate
verovio --transpose 2 input.mei -o intermediate
Screen Shot 2019-12-23 at 12 33 28
<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-23T12:00:11" version="2.4.0-dev-0a49d87-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000689297044">
    <score xml:id="score-0000001488626590">
     <scoreDef xml:id="scoredef-0000002116859099" midi.bpm="400">
      <staffGrp xml:id="staffgrp-0000000819520704">
       <staffDef xml:id="staffdef-0000000042314007" n="1" lines="5">
        <clef xml:id="clef-0000001316899658" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" accid="n" pname="d" mode="major" sig="2s" />
        <meterSig xml:id="metersig-L2F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" right="end" n="0">
       <staff xml:id="staff-0000000225970676" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L6F1" dur="1" oct="4" pname="d" accid.ges="n" />
        </layer>
       </staff>
       <harm xml:id="harm-L6F2" staff="1" tstamp="1.000000" n="2">D</harm>
       <harm xml:id="harm-L6F3" type="capo" staff="1" tstamp="1.000000" n="3">A</harm>
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

Then a second pass through verovio with a transposition XPath to select only @type="capo", transposing down three semitones:

verovio intermediate.mei --transpose -3 --transpose-x-path-query '//*[@type="capo"]' -o output -atmei
verovio intermediate.mei --transpose -m3 --transpose-x-path-query '//*[@type="capo"]' -o output -atmei
verovio intermediate.mei --transpose B --transpose-x-path-query '//*[@type="capo"]' -o output -atmei

This would only transpose the capo down a minor third:

Screen Shot 2019-12-23 at 12 32 38
<?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 xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">
 <meiHead>
  <fileDesc>
   <titleStmt>
    <title />
   </titleStmt>
   <pubStmt />
  </fileDesc>
  <encodingDesc>
   <appInfo>
    <application isodate="2019-12-23T12:00:11" version="2.4.0-dev-0a49d87-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000689297044">
    <score xml:id="score-0000001488626590">
     <scoreDef xml:id="scoredef-0000002116859099" midi.bpm="400">
      <staffGrp xml:id="staffgrp-0000000819520704">
       <staffDef xml:id="staffdef-0000000042314007" n="1" lines="5">
        <clef xml:id="clef-0000001316899658" shape="G" line="2" />
        <keySig xml:id="keysig-L3F1" accid="n" pname="d" mode="major" sig="2s" />
        <meterSig xml:id="metersig-L2F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <measure xml:id="measure-L1" right="end" n="0">
       <staff xml:id="staff-0000000225970676" n="1">
        <layer xml:id="layer-L1F1N1" n="1">
         <note xml:id="note-L6F1" dur="1" oct="4" pname="d" accid.ges="n" />
        </layer>
       </staff>
       <harm xml:id="harm-L6F2" staff="1" tstamp="1.000000" n="2">D</harm>
       <harm xml:id="harm-L6F3" type="capo" staff="1" tstamp="1.000000" n="3">F#</harm>
      </measure>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
craigsapp commented 4 years ago

Added Transposer::IntervalToSemitones() functions in commit https://github.com/rism-ch/verovio/commit/a0ca21710c1a348487b08f65cbf2096361abd07b, which may be useful for this issue (for example, changing "capo 3" text to "capo 5" text when transposing capo down a major second).

lpugin commented 4 years ago

Please move the discussion to the MEI repo