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
680 stars 185 forks source link

Support for MIDI export of repetitions #235

Open craigsapp opened 8 years ago

craigsapp commented 8 years ago

Is there a way of encoding repeats in MEI which verovio, and more particularly the verovio MIDI export understands?

Example work with one simple repeat (no separate second ending): http://verovio.humdrum.org/?file=chorales/chor001.krn

screen shot 2016-07-11 at 5 40 56 pm

The next complication would be first/second endings. And then cases such as da capo repeats (such as in minuet and trios).

Click to view example MEI data. ``` xml Aus meines Herzens Grunde From the Depths of My Heart Bach, Johann Sebastian Craig Stuart Sapp 2016-07-11 17:44:19

Transcoded from Humdrum with Verovio version 0.9.12-dev-48e949d

reference COM Bach, Johann Sebastian reference CDT 1685/02/21/-1750/07/28/ reference OTL Aus meines Herzens Grunde reference OTL From the Depths of My Heart reference SCT BWV 269 reference PC# 1 reference AGN chorale reference hum2abc -Q '' reference title @{PC#}. @{OTL@@DE} reference YOR1 371 vierstimmige Choralgesänge von Johann Sebastian Bach, reference YOR2 4th ed. by Alfred Dörffel (Leipzig: Breitkopf und Härtel, reference YOR3 c.1875). 178 pp. Plate "V.A.10". reprint: J.S. Bach, 371 Four-Part reference YOR4 Chorales (New York: Associated Music Publishers, Inc., c.1940). reference SMS B&H, 4th ed, Alfred Dörffel, c.1875, plate V.A.10 reference EED Craig Stuart Sapp reference EEV 2009/05/22
```
lpugin commented 8 years ago

Repeats should not be too difficult. Endings will indeed be more complicated but OK, and Da Capo and so on even more. For these I think there is a need for discussion in MEI.

craigsapp commented 3 years ago

Technically this features has now been implemented, since verovio can now expand a score and output a MIDI file (using the --expand option).

But a second stage of implementation would be to render an expanded MIDI file while keeping the score in compressed format. This would allow live playback of the MIDI data with WildWebMidi with the same score in compressed mode, where the note highlighting would jump around in the score according to the repeats (currently the score can be expanded and the MIDI file is a straight pass through the expanded score).

Here is a test case:

Screen Shot 2021-02-02 at 11 22 32 PM

This score would be displayed, but the MIDI file would play the sequence C D C E F F, and the notes would highlight in that sequence. (also there could be another expansion for only second endings, which would produce C E F in the MIDI).

The output timemap for the compressed score is currently (this should probably remain the default if no MIDI expansion is requested):

[
    {
        "tstamp":   0.000000,
        "qstamp":   0.000000,
        "tempo":    60,
        "on":   ["note-L7F1"]
    },
    {
        "tstamp":   4000.000000,
        "qstamp":   4.000000,
        "on":   ["note-L10F1"],
        "off":  ["note-L7F1"]
    },
    {
        "tstamp":   8000.000000,
        "qstamp":   8.000000,
        "on":   ["note-L13F1"],
        "off":  ["note-L10F1"]
    },
    {
        "tstamp":   12000.000000,
        "qstamp":   12.000000,
        "on":   ["note-L16F1"],
        "off":  ["note-L13F1"]
    },
    {
        "tstamp":   16000.000000,
        "qstamp":   16.000000,
        "off":  ["note-L16F1"]
    }
]

Here is what the timemap (and matching MIDI file) should be produced if an expanded MIDI file is requested:

[

    {
        "tstamp":   0.000000,   //  C (note-L7F1)
        "qstamp":   0.000000,
        "tempo":    60,
        "on":   ["note-L7F1"]
    },
    {
        "tstamp":   4000.000000,    //  D (note-L10F1)
        "qstamp":   4.000000,
        "on":   ["note-L10F1"],
        "off":  ["note-L7F1"]
    },
    {
        "tstamp":   8000.000000,    //  C (note-L7F1)
        "qstamp":   0.000000,
        "tempo":    60,
        "on":   ["note-L7F1"],
        "off":  ["note-L10F1"]
    },
    {
        "tstamp":   12000.000000,   //  E (note-L13F1)
        "qstamp":   8.000000,
        "on":   ["note-L13F1"],
        "off":  ["note-L7F1"]
    },
    {
        "tstamp":   16000.000000,   //  F (note-L16F1)
        "qstamp":   12.000000,
        "on":   ["note-L16F1"],
        "off":  ["note-L13F1"]
    },
    {
        "tstamp":   20000.000000,   //  F (note-L16F1)
        "qstamp":   12.000000,
        "on":   ["note-L16F1"],
        "off":  ["note-L16F1"]
    },
    {
        "tstamp":   24000.000000,   //  end
        "qstamp":   16.000000,
        "off":  ["note-L16F1"]
    }
]

The tstamp variable always increased in the timemap (millisecond time in the MIDI file), but the qstamp will decrease whenever there is a repeat happening.

Here is the distilled timemap:

real    score
0   0
4000    4
8000    0
12000   8
16000   12
20000   12
24000   16

In the MEI score, the time data would be reversed, with potentially multiple realTime values stored for each note:

score   real
0   [0, 8000]
4   [4000]
8   [12000]
12  [16000, 20000]

So for example, the first note would have two realTime values: 0 for the first repeat, and 8000 for the second repeat. Likewise, the note in the last measure at scoreTime 12 would have an array containing the two realTime values 16000 and 20000.

If there were an expansion list for the second endings only, then the score's note would have the values related to the timings of the notes in the shortened MIDI file:

score   real
0   [0]
4   []
8   [4000]
12  [8000]

Notice that the realTime values for the first ending note (D) is never played, so it has an empty realTime.

Test 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="2021-02-02T23:23:59" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001923652048">
    <score xml:id="score-0000001831078766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000001108141941">
       <staffDef xml:id="staffdef-0000000076573858" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000685011685" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L7F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L8" right="rptend" n="2">
        <staff xml:id="staff-L8F1N1" n="1">
         <layer xml:id="layer-L8F1N1" n="1">
          <note xml:id="note-L10F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L11" n="3">
        <staff xml:id="staff-L11F1N1" n="1">
         <layer xml:id="layer-L11F1N1" n="1">
          <note xml:id="note-L13F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L14" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L14F1N1" n="1">
         <layer xml:id="layer-L14F1N1" n="1">
          <note xml:id="note-L16F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

And a test containing a second-ending expansion:

<?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="2021-02-02T23:23:19" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000048699960">
    <score xml:id="score-0000001258871766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000002139912416">
       <staffDef xml:id="staffdef-0000000014370509" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <choice xml:id="choice-0000001179785592">
       <orig xml:id="orig-0000001713763553">
        <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
       </orig>
       <reg xml:id="reg-0000000163708006" type="norep">
        <expansion plist="#label-A #label-A2 #label-B" xml:id="expansion-L5F1" type="norep" />
       </reg>
      </choice>
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000725373844" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L8F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L9" right="rptend" n="2">
        <staff xml:id="staff-L9F1N1" n="1">
         <layer xml:id="layer-L9F1N1" n="1">
          <note xml:id="note-L11F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L12" n="3">
        <staff xml:id="staff-L12F1N1" n="1">
         <layer xml:id="layer-L12F1N1" n="1">
          <note xml:id="note-L14F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L15" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L15F1N1" n="1">
         <layer xml:id="layer-L15F1N1" n="1">
          <note xml:id="note-L17F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>
faelwar commented 1 year ago

I'm trying to use the expand option on the RenderToTimemap on the JS lib. However, it always returns the same result as using it without it.

timemap = tk.renderToTimemap({
        expand: "expansion-L4F1"
    });

However on the command line I can see the expect result while using the --expand option.

There is something that I could be using wrong here?

lpugin commented 1 year ago

You need to set the option before loading the data.

faelwar commented 1 year ago

Like this?

console.log("Verovio has loaded!");
    options = {
        pageWidth: pageWidth,
        scale: zoom,
        expand: "expansion-L4F1",
        // Add an option to pass note@pname and note@oct as svg @data-*
        svgAdditionalAttribute: ["note@pname", "note@oct"]
    };
    tk.setOptions(options);
    let svg = tk.renderData(meiXML, {
    });
faelwar commented 1 year ago

That code will generate the following error on my Browser:

Screenshot 2023-03-03 at 14 32 05

wergo commented 1 year ago

I am getting the same error.

(I have posted this on the Slack Verovio channel earlier.)

I have a problem with getting expansions work in the toolkit. In this example, the toolkit crashes when having to expand “expansion-repeat” or “expansion-default”. It works correctly when expansion set to “expansion-minimal”.

It seems when the toolkit has to add elements it crashes, when it removes elements, it works. (All these functions work correctly from the command line.)

wergo commented 1 year ago

Ah, I just see that it was raised again in #3314 and fixed through a90b43bb54b62331c2018dbe3699617c0fd62581.

Muromi-Rikka commented 4 months ago

Technically this features has now been implemented, since verovio can now expand a score and output a MIDI file (using the --expand option).

But a second stage of implementation would be to render an expanded MIDI file while keeping the score in compressed format. This would allow live playback of the MIDI data with WildWebMidi with the same score in compressed mode, where the note highlighting would jump around in the score according to the repeats (currently the score can be expanded and the MIDI file is a straight pass through the expanded score).

Here is a test case:

Screen Shot 2021-02-02 at 11 22 32 PM

This score would be displayed, but the MIDI file would play the sequence C D C E F F, and the notes would highlight in that sequence. (also there could be another expansion for only second endings, which would produce C E F in the MIDI).

The output timemap for the compressed score is currently (this should probably remain the default if no MIDI expansion is requested):

[
  {
      "tstamp":   0.000000,
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"]
  },
  {
      "tstamp":   4000.000000,
      "qstamp":   4.000000,
      "on":   ["note-L10F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   8000.000000,
      "qstamp":   8.000000,
      "on":   ["note-L13F1"],
      "off":  ["note-L10F1"]
  },
  {
      "tstamp":   12000.000000,
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L13F1"]
  },
  {
      "tstamp":   16000.000000,
      "qstamp":   16.000000,
      "off":  ["note-L16F1"]
  }
]

Here is what the timemap (and matching MIDI file) should be produced if an expanded MIDI file is requested:

[

  {
      "tstamp":   0.000000,   //  C (note-L7F1)
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"]
  },
  {
      "tstamp":   4000.000000,    //  D (note-L10F1)
      "qstamp":   4.000000,
      "on":   ["note-L10F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   8000.000000,    //  C (note-L7F1)
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"],
      "off":  ["note-L10F1"]
  },
  {
      "tstamp":   12000.000000,   //  E (note-L13F1)
      "qstamp":   8.000000,
      "on":   ["note-L13F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   16000.000000,   //  F (note-L16F1)
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L13F1"]
  },
  {
      "tstamp":   20000.000000,   //  F (note-L16F1)
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L16F1"]
  },
  {
      "tstamp":   24000.000000,   //  end
      "qstamp":   16.000000,
      "off":  ["note-L16F1"]
  }
]

The tstamp variable always increased in the timemap (millisecond time in the MIDI file), but the qstamp will decrease whenever there is a repeat happening.

Here is the distilled timemap:

real  score
0 0
4000  4
8000  0
12000 8
16000 12
20000 12
24000 16

In the MEI score, the time data would be reversed, with potentially multiple realTime values stored for each note:

score real
0 [0, 8000]
4 [4000]
8 [12000]
12    [16000, 20000]

So for example, the first note would have two realTime values: 0 for the first repeat, and 8000 for the second repeat. Likewise, the note in the last measure at scoreTime 12 would have an array containing the two realTime values 16000 and 20000.

If there were an expansion list for the second endings only, then the score's note would have the values related to the timings of the notes in the shortened MIDI file:

score real
0 [0]
4 []
8 [4000]
12    [8000]

Notice that the realTime values for the first ending note (D) is never played, so it has an empty realTime.

Test 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="2021-02-02T23:23:59" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001923652048">
    <score xml:id="score-0000001831078766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000001108141941">
       <staffDef xml:id="staffdef-0000000076573858" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000685011685" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L7F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L8" right="rptend" n="2">
        <staff xml:id="staff-L8F1N1" n="1">
         <layer xml:id="layer-L8F1N1" n="1">
          <note xml:id="note-L10F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L11" n="3">
        <staff xml:id="staff-L11F1N1" n="1">
         <layer xml:id="layer-L11F1N1" n="1">
          <note xml:id="note-L13F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L14" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L14F1N1" n="1">
         <layer xml:id="layer-L14F1N1" n="1">
          <note xml:id="note-L16F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

And a test containing a second-ending expansion:

<?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="2021-02-02T23:23:19" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000048699960">
    <score xml:id="score-0000001258871766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000002139912416">
       <staffDef xml:id="staffdef-0000000014370509" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <choice xml:id="choice-0000001179785592">
       <orig xml:id="orig-0000001713763553">
        <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
       </orig>
       <reg xml:id="reg-0000000163708006" type="norep">
        <expansion plist="#label-A #label-A2 #label-B" xml:id="expansion-L5F1" type="norep" />
       </reg>
      </choice>
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000725373844" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L8F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L9" right="rptend" n="2">
        <staff xml:id="staff-L9F1N1" n="1">
         <layer xml:id="layer-L9F1N1" n="1">
          <note xml:id="note-L11F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L12" n="3">
        <staff xml:id="staff-L12F1N1" n="1">
         <layer xml:id="layer-L12F1N1" n="1">
          <note xml:id="note-L14F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L15" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L15F1N1" n="1">
         <layer xml:id="layer-L15F1N1" n="1">
          <note xml:id="note-L17F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

Has there been any new progress on this issue?

dajun-TT commented 2 months ago

Technically this features has now been implemented, since verovio can now expand a score and output a MIDI file (using the --expand option).从技术上讲,此功能现已实现,因为 verovio 现在可以扩展乐谱并输出 MIDI 文件(使用 --expand 选项)。

But a second stage of implementation would be to render an expanded MIDI file while keeping the score in compressed format. This would allow live playback of the MIDI data with WildWebMidi with the same score in compressed mode, where the note highlighting would jump around in the score according to the repeats (currently the score can be expanded and the MIDI file is a straight pass through the expanded score).但实现的第二个阶段是渲染扩展的 MIDI 文件,同时保持乐谱为压缩格式。 这将允许在压缩模式下使用 WildWebMidi 以相同的乐谱实时播放 MIDI 数据,其中音符高亮显示将根据重复在乐谱中跳动(目前乐谱可以扩展,MIDI 文件是直接通过扩展的乐谱)。

Here is a test case:下面是一个测试用例:

Screen Shot 2021-02-02 at 11 22 32 PM

This score would be displayed, but the MIDI file would play the sequence C D C E F F, and the notes would highlight in that sequence. (also there could be another expansion for only second endings, which would produce C E F in the MIDI).这个乐谱会被显示出来,但 MIDI 文件会播放音序 C D C E F F,并且音符会在该音序中高亮显示。(也可以有另一个仅针对第二个结尾的扩展,这将在 MIDI 中产生 C E F)。

The output timemap for the compressed score is currently (this should probably remain the default if no MIDI expansion is requested):压缩乐谱的输出时间映射为当前(如果未请求 MIDI 扩展,则可能应保持默认值):

[
  {
      "tstamp":   0.000000,
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"]
  },
  {
      "tstamp":   4000.000000,
      "qstamp":   4.000000,
      "on":   ["note-L10F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   8000.000000,
      "qstamp":   8.000000,
      "on":   ["note-L13F1"],
      "off":  ["note-L10F1"]
  },
  {
      "tstamp":   12000.000000,
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L13F1"]
  },
  {
      "tstamp":   16000.000000,
      "qstamp":   16.000000,
      "off":  ["note-L16F1"]
  }
]

Here is what the timemap (and matching MIDI file) should be produced if an expanded MIDI file is requested:如果请求扩展的 MIDI 文件,则应生成时间映射(和匹配的 MIDI 文件)如下:

[

  {
      "tstamp":   0.000000,   //  C (note-L7F1)
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"]
  },
  {
      "tstamp":   4000.000000,    //  D (note-L10F1)
      "qstamp":   4.000000,
      "on":   ["note-L10F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   8000.000000,    //  C (note-L7F1)
      "qstamp":   0.000000,
      "tempo":    60,
      "on":   ["note-L7F1"],
      "off":  ["note-L10F1"]
  },
  {
      "tstamp":   12000.000000,   //  E (note-L13F1)
      "qstamp":   8.000000,
      "on":   ["note-L13F1"],
      "off":  ["note-L7F1"]
  },
  {
      "tstamp":   16000.000000,   //  F (note-L16F1)
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L13F1"]
  },
  {
      "tstamp":   20000.000000,   //  F (note-L16F1)
      "qstamp":   12.000000,
      "on":   ["note-L16F1"],
      "off":  ["note-L16F1"]
  },
  {
      "tstamp":   24000.000000,   //  end
      "qstamp":   16.000000,
      "off":  ["note-L16F1"]
  }
]

The tstamp variable always increased in the timemap (millisecond time in the MIDI file), but the qstamp will decrease whenever there is a repeat happening.tstamp 变量在时间映射中总是增加(MIDI 文件中的毫秒时间),但每当有重复发生时,qstamp 就会减少。

Here is the distilled timemap:以下是提炼的时间表:

real  score
0 0
4000  4
8000  0
12000 8
16000 12
20000 12
24000 16

In the MEI score, the time data would be reversed, with potentially multiple realTime values stored for each note:在 MEI 分数中,时间数据将被反转,每个音符可能存储多个 realTime 值:

score real
0 [0, 8000]
4 [4000]
8 [12000]
12    [16000, 20000]

So for example, the first note would have two realTime values: 0 for the first repeat, and 8000 for the second repeat. Likewise, the note in the last measure at scoreTime 12 would have an array containing the two realTime values 16000 and 20000.例如,第一个音符将有两个 realTime 值:0 表示第一次重复,8000 表示第二次重复。 同样,scoreTime 12 处最后一个小节中的注释将具有一个包含两个 realTime 值 16000 和 20000 的数组。

If there were an expansion list for the second endings only, then the score's note would have the values related to the timings of the notes in the shortened MIDI file:如果只有第二个结尾的扩展列表,那么乐谱的音符将具有与缩短的 MIDI 文件中音符的时间相关的值:

score real
0 [0]
4 []
8 [4000]
12    [8000]

Notice that the realTime values for the first ending note (D) is never played, so it has an empty realTime.请注意,第一个结束音符 (D) 的 realTime 值从未播放过,因此它的 realTime 为空。

Test MEI data: 测试 FROM 数据:

<?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="2021-02-02T23:23:59" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000001923652048">
    <score xml:id="score-0000001831078766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000001108141941">
       <staffDef xml:id="staffdef-0000000076573858" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000685011685" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L7F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L8" right="rptend" n="2">
        <staff xml:id="staff-L8F1N1" n="1">
         <layer xml:id="layer-L8F1N1" n="1">
          <note xml:id="note-L10F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L11" n="3">
        <staff xml:id="staff-L11F1N1" n="1">
         <layer xml:id="layer-L11F1N1" n="1">
          <note xml:id="note-L13F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L14" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L14F1N1" n="1">
         <layer xml:id="layer-L14F1N1" n="1">
          <note xml:id="note-L16F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

And a test containing a second-ending expansion:以及一个包含第二个结尾扩展的测试:

<?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="2021-02-02T23:23:19" version="3.2.0-dev-8f1ece3-dirty">
     <name>Verovio</name>
     <p>Transcoded from Humdrum</p>
    </application>
   </appInfo>
  </encodingDesc>
  <workList>
   <work>
    <title />
   </work>
  </workList>
 </meiHead>
 <music>
  <body>
   <mdiv xml:id="mdiv-0000000048699960">
    <score xml:id="score-0000001258871766">
     <scoreDef xml:id="scoredef-0000001634015516" midi.bpm="60">
      <staffGrp xml:id="staffgrp-0000002139912416">
       <staffDef xml:id="staffdef-0000000014370509" n="1" lines="5">
        <clef xml:id="clef-L2F1" shape="G" line="2" />
        <meterSig xml:id="metersig-L3F1" count="4" unit="4" />
       </staffDef>
      </staffGrp>
     </scoreDef>
     <section xml:id="section-L1F1">
      <choice xml:id="choice-0000001179785592">
       <orig xml:id="orig-0000001713763553">
        <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" />
       </orig>
       <reg xml:id="reg-0000000163708006" type="norep">
        <expansion plist="#label-A #label-A2 #label-B" xml:id="expansion-L5F1" type="norep" />
       </reg>
      </choice>
      <section xml:id="label-A">
       <measure xml:id="measure-L1" n="1">
        <staff xml:id="staff-0000000725373844" n="1">
         <layer xml:id="layer-L1F1N1" n="1">
          <note xml:id="note-L8F1" dur="1" oct="4" pname="c" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
      <ending xml:id="label-A1" n="1">
       <measure xml:id="measure-L9" right="rptend" n="2">
        <staff xml:id="staff-L9F1N1" n="1">
         <layer xml:id="layer-L9F1N1" n="1">
          <note xml:id="note-L11F1" dur="1" oct="4" pname="d" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <ending xml:id="label-A2" n="2">
       <measure xml:id="measure-L12" n="3">
        <staff xml:id="staff-L12F1N1" n="1">
         <layer xml:id="layer-L12F1N1" n="1">
          <note xml:id="note-L14F1" dur="1" oct="4" pname="e" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </ending>
      <section xml:id="label-B">
       <measure xml:id="measure-L15" left="rptstart" right="rptend" n="4">
        <staff xml:id="staff-L15F1N1" n="1">
         <layer xml:id="layer-L15F1N1" n="1">
          <note xml:id="note-L17F1" dur="1" oct="4" pname="f" accid.ges="n" />
         </layer>
        </staff>
       </measure>
      </section>
     </section>
    </score>
   </mdiv>
  </body>
 </music>
</mei>

how could i add code like this <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B #label-B" xml:id="expansion-L4F1" /> in MEI file to use expand: "expansion-L4F1" in option. i generate a mei file from a xml file

craigsapp commented 2 months ago

Here is a basic example with one possiblility for adding an expansion list. Ideally the MusicXML-to-MEI converter would automatically generate <expansion> if it detects <ending> in the output MEI data (particularly since more complicated cases are possible, such as nested <section> elements.

When there are multiple measures before an <ending>, then the converter would ideally place the preceding measures in a <section> to make generation of <expansion> easier (i.e., use the ID for the section in the expansion list instead of haing to give every since measure ID in the @plist).

Musical notation for example:

Screenshot 2024-09-06 at 6 14 53 AM
View Initial MusicXML data for example embedded in a demo HTML file ```xml Untitled score Composer / arranger MuseScore 4.4.0 2024-09-06 6.99911 40 1596.77 1233.87 85.7252 85.7252 85.7252 85.7252 85.7252 85.7252 85.7252 85.7252 1.8 5.5 5 4.5 1 1 1.1 1 1.6 1.1 1.1 2.1 0.5 1.1 1 2.1 0.5 1 1.2 70 70 49 title Untitled score subtitle Subtitle composer Composer / arranger Flute Fl. Flute wind.flutes.flute 1 74 78.7402 0 50 -0 170 1 0 G 2 C 5 4 1 whole 1. D 5 4 1 whole light-heavy 2. E 5 4 1 whole light-heavy ```

Conversion of the MusicXML to MEI using verovio:

Click to view MEI conversion. ```xml Untitled score Composer / arranger 2024-09-06 Verovio

Transcoded from MusicXML

Untitled score Subtitle Composer / arranger Fl.
```

Now here is an example HTML file with javascript code to add the expansion list:

Click to view HTML code with JavaScript to add expansion element. ```html TITLE

Before

Added <expansion> element

After

```

The script adds the element:

<expansion plist="#m1pww2dk #m8m3bh4 #m1pww2dk #myzr5yh" xml:id="expansionList1"/>

to the final MEI data:

Click to view MEI data with expansion element ```xml Untitled score Composer / arranger 2024-09-06 Verovio

Transcoded from MusicXML

Untitled score Subtitle Composer / arranger Fl.
```
Dahurica commented 1 month ago

Hello,

Regarding this repeated issue, I found that using the test based on the above method works in most cases. The test case score works fine, but the issue arises when I switch to another score with repetitions. For more complex scores, I encounter the error "@plist could not match the target, MRpt produces empty MIDI output". Could you please advise on how to solve this problem? From my understanding, you extend the repetition by labeling it with time. What elements' IDs should be included in the plist to correctly format the timing?

Thank you!

craigsapp commented 1 month ago

Here is a longer example (which @rettinghaus and/or @lpugin can comment further on):

Screenshot 2024-09-12 at 2 33 22 AM
Click to view MEI data for above example ```xml </titleStmt> <pubStmt> </pubStmt> </fileDesc> <encodingDesc> <appInfo> <application isodate="2024-09-12T02:33:27" version="4.3.0-dev-bf6b697-dirty"> <name>Verovio</name> <p>Transcoded from Humdrum</p> </application> </appInfo> </encodingDesc> </meiHead> <music decls="#work1_encoded"> <body> <mdiv xml:id="m1aes8yr"> <score xml:id="smpropn"> <scoreDef xml:id="snqwmrv" midi.bpm="400.000000" tempo.dist="3.0000vu"> <staffGrp xml:id="sufahq2"> <staffDef xml:id="staffdef-L1F1" n="1" lines="5"> <clef xml:id="cg75w9m" shape="G" line="2" /> <meterSig xml:id="metersig-L3F1" count="4" unit="4" /> </staffDef> </staffGrp> </scoreDef> <section xml:id="section-L1F1"> <expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B" xml:id="expansion-L2F1" /> <section xml:id="label-A"> <measure xml:id="measure-L1" right="dbl" n="1"> <staff xml:id="staff-L1F1" 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> </measure> <scoreDef xml:id="s1o94igl"> <keySig xml:id="keysig-L9F1" sig="4f" /> <staffGrp xml:id="sx4ircv"> <staffDef xml:id="s19wxq4u" n="1"> <meterSig xml:id="m1dqb21j" count="3" unit="4" /> </staffDef> </staffGrp> </scoreDef> <measure xml:id="measure-L7" n="2"> <staff xml:id="staff-L7F1N1" n="1"> <layer xml:id="layer-L7F1N1" n="1"> <note xml:id="note-L10F1" dots="1" dur="2" oct="4" pname="d" accid.ges="f" /> </layer> </staff> </measure> <measure xml:id="measure-L11" n="3"> <staff xml:id="staff-L11F1N1" n="1"> <layer xml:id="layer-L11F1N1" n="1"> <note xml:id="note-L12F1" dots="1" dur="2" oct="4" pname="e" accid.ges="f" /> </layer> </staff> </measure> </section> <ending xml:id="label-A1" n="1"> <measure xml:id="measure-L13" n="4"> <staff xml:id="staff-L13F1N1" n="1"> <layer xml:id="layer-L13F1N1" n="1"> <note xml:id="note-L15F1" dots="1" dur="2" oct="4" pname="g" accid.ges="n" /> </layer> </staff> </measure> <measure xml:id="measure-L16" right="rptend" n="5"> <staff xml:id="staff-L16F1N1" n="1"> <layer xml:id="layer-L16F1N1" n="1"> <note xml:id="note-L17F1" dots="1" dur="2" oct="4" pname="a" accid.ges="f" /> </layer> </staff> </measure> </ending> <ending xml:id="label-A2" n="2"> <measure xml:id="measure-L18" n="6"> <staff xml:id="staff-L18F1N1" n="1"> <layer xml:id="layer-L18F1N1" n="1"> <note xml:id="note-L20F1" dots="1" dur="2" oct="4" pname="b" accid.ges="f" /> </layer> </staff> </measure> <measure xml:id="measure-L21" right="dbl" n="7"> <staff xml:id="staff-L21F1N1" n="1"> <layer xml:id="layer-L21F1N1" n="1"> <note xml:id="note-L22F1" dots="1" dur="2" oct="5" pname="c" accid.ges="n" /> </layer> </staff> </measure> </ending> <section xml:id="label-B"> <measure xml:id="measure-L23" n="8"> <staff xml:id="staff-L23F1N1" n="1"> <layer xml:id="layer-L23F1N1" n="1"> <note xml:id="note-L25F1" dots="1" dur="2" oct="5" pname="d" accid.ges="f" /> </layer> </staff> </measure> <measure xml:id="measure-L26" right="end" n="9"> <staff xml:id="staff-L26F1N1" n="1"> <layer xml:id="layer-L26F1N1" n="1"> <note xml:id="note-L27F1" dots="1" dur="2" oct="5" pname="e" accid.ges="f" /> </layer> </staff> </measure> </section> </section> </score> </mdiv> </body> </music> </mei> ``` </details> <p>The expansion list is:</p> <pre><code class="language-xml"><expansion plist="#label-A #label-A1 #label-A #label-A2 #label-B" xml:id="expansion-L2F1" /></code></pre> <table> <thead> <tr> <th>ID</th> <th>element</th> <th>measures</th> </tr> </thead> <tbody> <tr> <td><code>label-A</code></td> <td><code><section></code></td> <td>1–3</td> </tr> <tr> <td><code>label-A1</code></td> <td><code><ending></code></td> <td>4–5</td> </tr> <tr> <td><code>label-A2</code></td> <td><code><ending></code></td> <td>6–7</td> </tr> <tr> <td><code>label-B</code></td> <td><code><section></code></td> <td>8–9</td> </tr> </tbody> </table> <p>The section with the ID <code>label-A</code> contains four children elements:</p> <pre><code class="language-xml"><measure xml:id="measure-L1" right="dbl" n="1"> <scoreDef xml:id="s1o94igl"> <measure xml:id="measure-L7" n="2"> <measure xml:id="measure-L11" n="3"> </code></pre> <p>The section element with ID <code>label-A</code> is optional and is used to group these four elements. Instead of</p> <pre><code>plist="#label-A #label-A1 #label-A #label-A2 #label-B"</code></pre> <p>you could replace <code>#label-A</code> with the IDREFs of the four children of <code>#label-A</code> (or if they were not contained in a section element):</p> <pre><code>plist="#measure-L1 #s1o94igl #measure-L7 #measure-L11 #label-A1 #label-A #label-A2 #label-B"</code></pre> <blockquote> <p>What elements' IDs should be included in the plist to correctly format the timing?</p> </blockquote> <p>So it is the IDs (IDREFs technically, which are the IDs with <code>#</code> in front of the ID) in the order that you want to play the music.</p> <blockquote> <p>From my understanding, you extend the repetition by labeling it with time.</p> </blockquote> <p>I don't understand this sentence, so maybe a source of the problem. The expansion list defines a sequence of elements to follow when generating the MIDI. It should be the responsibility of verovio to keep track of the timing information for the generated MIDI. The expansion list only gives the sequence of elements that are to be placed in then given sequence in the MIDI export.</p> <hr /> <blockquote> <p>I encounter the error <code>@Plist</code> could not match the target, <code>MRpt</code> produces empty MIDI output".</p> </blockquote> <p>This could be a separate problem. You have an <code><mRpt></code> in the score? And is there a <code>@plist</code> in the <code><mRpt></code> or rather in <code><expansion></code>? The error seems to say that an IDREF in <code>@plist</code> cannot match to an ID in the score. </p> <p>A possible problem is that in <code>@plist</code> the IDREFs have to start with <code>#</code> and for <code>@xml:id</code> the IDs cannot start with <code>#</code>.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/Dahurica"><img src="https://avatars.githubusercontent.com/u/153709867?v=4" />Dahurica</a> commented <strong> 1 month ago</strong> </div> <div class="markdown-body"> <p>Based on my current understanding, in the expansion plist, I can place the measure IDs to customize the arrangement timing. So what I need to do is to add my own logic for arranging the timing points and then order the measure IDs accordingly, right?</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/Dahurica"><img src="https://avatars.githubusercontent.com/u/153709867?v=4" />Dahurica</a> commented <strong> 1 month ago</strong> </div> <div class="markdown-body"> <p>My current understanding is that the first #id in the plist represents the starting measure, and it will insert the subsequent measures for playback. Each time, I just need to include the id of the starting measure of the repetition as well as the subsequent logic. This is my current understanding. Below is my manually edited mei file。</p> <pre><code class="language-javascript"><?xml version="1.0" encoding="UTF-8"?><?xml-model href="https://music-encoding.org/schema/5.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?><?xml-model href="https://music-encoding.org/schema/5.0/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"> <meiHead xml:id="m76pxkx"> <fileDesc xml:id="fo8kuxb"> <titleStmt xml:id="tttie9n"> <title>未命名乐谱</title> <respStmt> <persName role="composer">作曲 / 编排</persName> </respStmt> </titleStmt> <pubStmt xml:id="p1s5f9vl"> <date isodate="2024-09-12" type="encoding-date">2024-09-12</date> </pubStmt> </fileDesc> <encodingDesc xml:id="e1v0tw7w"> <appInfo xml:id="algonnu"> <application xml:id="a16fokqu" isodate="2024-09-12T17:42:17" version="4.2.0-8a772f5"> <name xml:id="n1sow6rt">Verovio</name> <p xml:id="py5p0zb">Transcoded from MusicXML</p> </application> </appInfo> </encodingDesc> </meiHead> <music> <body> <mdiv xml:id="mk00qk6"> <score xml:id="s1z8311"> <scoreDef xml:id="s15wkvd7"> <staffGrp xml:id="skd1am9"> <staffGrp xml:id="P1" bar.thru="true"> <grpSym xml:id="gepw1h5" symbol="brace" /> <label xml:id="l1qdmni4">Piano</label> <instrDef xml:id="itlwcav" midi.channel="0" midi.instrnum="0" midi.volume="78.00%" /> <staffDef xml:id="s99n7kx" n="1" lines="5" ppq="1"> <clef xml:id="cdwgbjf" shape="G" line="2" /> <keySig xml:id="k1uwof91" sig="0" /> <meterSig xml:id="mkkpgq1" count="4" unit="4" /> </staffDef> <staffDef xml:id="s1ijhfb4" n="2" lines="5" ppq="1"> <clef xml:id="c6n40zb" shape="F" line="4" /> <keySig xml:id="k1tie4si" sig="0" /> <meterSig xml:id="m1a9zja7" count="4" unit="4" /> </staffDef> </staffGrp> </staffGrp> </scoreDef> <section xml:id="silgj4m"> <expansion plist="#m11eyy6j #m11eyy6j #m11eyy6j #m11eyy6j #m11eyy6j #m11eyy6j #mg074no #mg074no #mg074no" xml:id="expansionList1" /> <pb xml:id="p96mkud" /> <measure xml:id="m11eyy6j" n="1"> <staff xml:id="s9r46e7" n="1"> <layer xml:id="l7etlqn" n="1"> <note xml:id="nr5vl6k" dur.ppq="2" dur="2" oct="4" pname="b" stem.dir="down" /> <note xml:id="n1a32dcj" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> </layer> </staff> <staff xml:id="sizh49p" n="2"> <layer xml:id="l1ornha7" n="5"> <mRest xml:id="mbq0k4k" /> </layer> </staff> </measure> <measure xml:id="my5tisq" n="2"> <staff xml:id="s1q8cnvm" n="1"> <layer xml:id="lfxtx7p" n="1"> <note xml:id="nn7woys" dur.ppq="2" dur="2" oct="4" pname="g" stem.dir="up" /> <note xml:id="n10gvfbm" dur.ppq="2" dur="2" oct="4" pname="d" stem.dir="up" /> </layer> </staff> <staff xml:id="s2vvewp" n="2"> <layer xml:id="l1jffgsu" n="5"> <mRest xml:id="mncp183" /> </layer> </staff> </measure> <measure xml:id="m2u4e6v" n="3"> <staff xml:id="s1k54crm" n="1"> <layer xml:id="lgt5nfk" n="1"> <note xml:id="n17kgq2j" dur.ppq="2" dur="2" oct="4" pname="e" stem.dir="up" /> <rest xml:id="rddvosx" dur.ppq="2" dur="2" /> </layer> </staff> <staff xml:id="sccgpde" n="2"> <layer xml:id="l14mczpi" n="5"> <mRest xml:id="ma9mlq6" /> </layer> </staff> </measure> <measure xml:id="mr6yjs" n="4"> <staff xml:id="s1epucul" n="1"> <layer xml:id="lejg45g" n="1"> <note xml:id="nb5ko7w" dur.ppq="2" dur="2" oct="4" pname="b" stem.dir="down" /> <note xml:id="n151gavc" dur.ppq="2" dur="2" oct="4" pname="e" stem.dir="up" /> </layer> </staff> <staff xml:id="s1aj2wqa" n="2"> <layer xml:id="lq3xzpz" n="5"> <mRest xml:id="mw09kh2" /> </layer> </staff> </measure> <measure xml:id="m1yvircq" right="rptend" n="5"> <staff xml:id="s18xzaii" n="1"> <layer xml:id="ldi66z" n="1"> <note xml:id="n1se27cg" dur.ppq="4" dur="1" oct="4" pname="c" /> </layer> </staff> <staff xml:id="s2v3jgc" n="2"> <layer xml:id="l18ixaaf" n="5"> <mRest xml:id="m151phc4" /> </layer> </staff> </measure> <measure xml:id="m64505m" n="6"> <staff xml:id="slom3xj" n="1"> <layer xml:id="lft74cn" n="1"> <note xml:id="n1v6gb4x" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <note xml:id="n1c2eaz3" dur.ppq="1" dur="4" oct="4" pname="a" stem.dir="up" /> <note xml:id="n1iwmqhu" dur.ppq="1" dur="4" oct="4" pname="b" stem.dir="down" /> <note xml:id="n1gro224" dur.ppq="1" dur="4" oct="5" pname="c" stem.dir="down" /> </layer> </staff> <staff xml:id="sdywnrf" n="2"> <layer xml:id="l3dbfpg" n="5"> <mRest xml:id="m18t0nj1" /> </layer> </staff> </measure> <measure xml:id="me3gdtf" n="7"> <staff xml:id="s1lz5k3d" n="1"> <layer xml:id="l1kug4rp" n="1"> <note xml:id="nv15y1x" dur.ppq="1" dur="4" oct="4" pname="e" stem.dir="up" /> <rest xml:id="r1vl1hu4" dur.ppq="1" dur="4" /> <note xml:id="nlww67x" dur.ppq="1" dur="4" oct="5" pname="c" stem.dir="down" /> <rest xml:id="rylxnhv" dur.ppq="1" dur="4" /> </layer> </staff> <staff xml:id="s21x2or" n="2"> <layer xml:id="l1v92ngh" n="5"> <mRest xml:id="m1novkf2" /> </layer> </staff> </measure> <measure xml:id="m1zw7b6" n="8"> <staff xml:id="s1kv5iz2" n="1"> <layer xml:id="l1f2w0bi" n="1"> <note xml:id="nyc04hk" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> <note xml:id="nos5uab" dur.ppq="2" dur="2" oct="4" pname="d" stem.dir="up" /> </layer> </staff> <staff xml:id="shbksax" n="2"> <layer xml:id="l186ynek" n="5"> <mRest xml:id="m1n5ii4s" /> </layer> </staff> </measure> <sb xml:id="s1050vw3" /> <measure xml:id="moq0bdz" n="9"> <staff xml:id="shzkn5m" n="1"> <layer xml:id="l1hvzxtf" n="1"> <note xml:id="n1v6h4ey" dur.ppq="2" dur="2" oct="5" pname="c" stem.dir="down" /> <note xml:id="ngdnyd0" dur.ppq="2" dur="2" oct="4" pname="g" stem.dir="up" /> </layer> </staff> <staff xml:id="ssyw926" n="2"> <layer xml:id="ls1e0di" n="5"> <mRest xml:id="m1os0s5c" /> </layer> </staff> </measure> <measure xml:id="mg074no" left="rptstart" n="10"> <staff xml:id="s1og9psn" n="1"> <layer xml:id="l17cbp1p" n="1"> <note xml:id="n2rxcda" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> <note xml:id="nm7u2o4" dur.ppq="2" dur="2" oct="4" pname="a" stem.dir="up" /> </layer> </staff> <staff xml:id="sql2yei" n="2"> <layer xml:id="l7cwzbg" n="5"> <mRest xml:id="mnka4ly" /> </layer> </staff> </measure> <measure xml:id="m1qwofec" n="11"> <staff xml:id="s1k23pp8" n="1"> <layer xml:id="lni10xv" n="1"> <note xml:id="n1daize9" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> <note xml:id="n1aejsfk" dur.ppq="2" dur="2" oct="4" pname="e" stem.dir="up" /> </layer> </staff> <staff xml:id="s8ra62q" n="2"> <layer xml:id="l13h7wg4" n="5"> <mRest xml:id="mc1dcwg" /> </layer> </staff> </measure> <measure xml:id="m48llki" n="12"> <staff xml:id="ssbb9rg" n="1"> <layer xml:id="l8s7p33" n="1"> <note xml:id="n4tm46x" dur.ppq="2" dur="2" oct="4" pname="d" stem.dir="up" /> <note xml:id="n1xw6n6f" dur.ppq="2" dur="2" oct="3" pname="g" stem.dir="up" /> </layer> </staff> <staff xml:id="s1vzy0gq" n="2"> <layer xml:id="l1bczxoe" n="5"> <mRest xml:id="m1cc7c9j" /> </layer> </staff> </measure> <measure xml:id="m1dgmvg5" n="13"> <staff xml:id="s14jq1mi" n="1"> <layer xml:id="lrnltkp" n="1"> <note xml:id="nk7qjyh" dur.ppq="2" dur="2" oct="4" pname="c" stem.dir="up" /> <rest xml:id="rbtbn5q" dur.ppq="2" dur="2" /> </layer> </staff> <staff xml:id="swg4nlz" n="2"> <layer xml:id="lahdhwq" n="5"> <mRest xml:id="m2mhwu3" /> </layer> </staff> </measure> <measure xml:id="mvr9jgs" n="14"> <staff xml:id="s1y66rc1" n="1"> <layer xml:id="l16zlw0k" n="1"> <note xml:id="nb8fy2x" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> <rest xml:id="r13z51of" dur.ppq="2" dur="2" /> </layer> </staff> <staff xml:id="s1mjff2f" n="2"> <layer xml:id="l16pisja" n="5"> <mRest xml:id="m6n9ug8" /> </layer> </staff> </measure> <measure xml:id="mdcryi3" n="15"> <staff xml:id="s12e9mii" n="1"> <layer xml:id="l1vn6x" n="1"> <chord xml:id="c10221px" dur.ppq="2" dur="2" stem.dir="up"> <note xml:id="n11pvp6m" oct="3" pname="a" /> <note xml:id="n11g6wap" oct="4" pname="c" /> </chord> <rest xml:id="rwjfsmk" dur.ppq="2" dur="2" /> </layer> </staff> <staff xml:id="s17w0k4k" n="2"> <layer xml:id="l14yk48j" n="5"> <mRest xml:id="mxhh29d" /> </layer> </staff> </measure> <ending xml:id="e9gwc42" lendsym="angledown" n="1."> <measure xml:id="m8qeyo1" right="rptend" n="16"> <staff xml:id="su57hff" n="1"> <layer xml:id="l1vzip2" n="1"> <note xml:id="nrd8prp" dur.ppq="2" dur="2" oct="4" pname="f" stem.dir="up" /> <note xml:id="nkhpaki" dur.ppq="2" dur="2" oct="4" pname="a" stem.dir="up" /> </layer> </staff> <staff xml:id="sv11nb8" n="2"> <layer xml:id="lxqytq" n="5"> <mRest xml:id="mnxegnx" /> </layer> </staff> </measure> </ending> <ending xml:id="e12poiib" lendsym="angledown" n="2."> <measure xml:id="m17ahtnj" n="17"> <staff xml:id="s19eexq5" n="1"> <layer xml:id="l1o9zw64" n="1"> <note xml:id="n1ec1urh" dur.ppq="2" dur="2" oct="4" pname="a" stem.dir="up" /> <note xml:id="n12ya71g" dur.ppq="2" dur="2" oct="4" pname="g" stem.dir="up" /> </layer> </staff> <staff xml:id="s18penv9" n="2"> <layer xml:id="lntio15" n="5"> <mRest xml:id="m11bg0sc" /> </layer> </staff> </measure> </ending> <sb xml:id="s1rpjcs8" /> <measure xml:id="m1ka0ql" n="18"> <staff xml:id="sjrvu87" n="1"> <layer xml:id="l1oev7iw" n="1"> <note xml:id="nrx0brd" dur.ppq="1" dur="4" oct="4" pname="e" stem.dir="up" /> <chord xml:id="c1pwhcjw" dur.ppq="1" dur="4" stem.dir="up"> <note xml:id="nvv6nbu" oct="4" pname="g" /> <note xml:id="n1e4s3qr" oct="4" pname="b" /> </chord> <note xml:id="nquwjje" dur.ppq="1" dur="4" oct="5" pname="c" stem.dir="down" /> <note xml:id="n1nlremo" dur.ppq="1" dur="4" oct="4" pname="f" stem.dir="up" /> </layer> </staff> <staff xml:id="sxbqg3b" n="2"> <layer xml:id="l1xl9pvg" n="5"> <mRest xml:id="m1fqib2i" /> </layer> </staff> </measure> <measure xml:id="mx2e8hf" left="rptstart" n="19"> <staff xml:id="s1geca8d" n="1"> <layer xml:id="l6zigoe" n="1"> <note xml:id="nnr53u8" dur.ppq="1" dur="4" oct="4" pname="d" stem.dir="up" /> <note xml:id="n13szarq" dur.ppq="1" dur="4" oct="4" pname="b" stem.dir="down" /> <note xml:id="n1xtr5it" dur.ppq="1" dur="4" oct="4" pname="f" stem.dir="up" /> <note xml:id="n2jpfj8" dur.ppq="1" dur="4" oct="4" pname="b" stem.dir="down" /> </layer> </staff> <staff xml:id="stf1yy9" n="2"> <layer xml:id="le8pdqh" n="5"> <mRest xml:id="m1xkn1md" /> </layer> </staff> </measure> <measure xml:id="m1vt80k7" n="20"> <staff xml:id="ss4ml1d" n="1"> <layer xml:id="lb7qsvq" n="1"> <chord xml:id="c8kah6t" dur.ppq="1" dur="4" stem.dir="down"> <note xml:id="n12w5jf" oct="4" pname="g" /> <note xml:id="nkp02f2" oct="5" pname="d" /> </chord> <note xml:id="n96rzdt" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <note xml:id="n76i0f9" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <note xml:id="n15ik2ft" dur.ppq="1" dur="4" oct="4" pname="d" stem.dir="up" /> </layer> </staff> <staff xml:id="s18ii0qj" n="2"> <layer xml:id="l150rz64" n="5"> <mRest xml:id="m172j0t4" /> </layer> </staff> </measure> <measure xml:id="m1h50en2" n="21"> <staff xml:id="s1vsursq" n="1"> <layer xml:id="l1o69wi9" n="1"> <note xml:id="n10naygy" dur.ppq="1" dur="4" oct="4" pname="f" stem.dir="up" /> <note xml:id="nf97mn" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <chord xml:id="c1upfpl" dur.ppq="1" dur="4" stem.dir="up"> <note xml:id="n1gdj94m" oct="4" pname="c" /> <note xml:id="n1w5sj10" oct="4" pname="g" /> <note xml:id="nk4kl0i" oct="5" pname="c" /> </chord> <note xml:id="n7ljqku" dur.ppq="1" dur="4" oct="3" pname="b" stem.dir="up" /> </layer> </staff> <staff xml:id="s2ov1y3" n="2"> <layer xml:id="ld4kp3o" n="5"> <mRest xml:id="m17f0mgn" /> </layer> </staff> </measure> <ending xml:id="e1ujlge1" lendsym="angledown" n="1."> <measure xml:id="m1y3ut2m" right="rptend" n="22"> <staff xml:id="savs2xs" n="1"> <layer xml:id="lvxyu52" n="1"> <note xml:id="n1x3n6ci" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <rest xml:id="rxcbl80" dur.ppq="1" dur="4" /> <note xml:id="n1m8y7oj" dur.ppq="1" dur="4" oct="4" pname="d" stem.dir="up" /> <rest xml:id="r165omxr" dur.ppq="1" dur="4" /> </layer> </staff> <staff xml:id="srqzwgt" n="2"> <layer xml:id="l14hjqm3" n="5"> <mRest xml:id="mltdvcv" /> </layer> </staff> </measure> </ending> <ending xml:id="e1laxevk" lendsym="none" n="2."> <measure xml:id="maovs9" n="23"> <staff xml:id="s1j8c4om" n="1"> <layer xml:id="lue2q5r" n="1"> <note xml:id="nbkx3ma" dur.ppq="1" dur="4" oct="4" pname="g" stem.dir="up" /> <note xml:id="nmacf42" dur.ppq="1" dur="4" oct="4" pname="e" stem.dir="up" /> <note xml:id="n1t3t1d7" dur.ppq="1" dur="4" oct="4" pname="e" stem.dir="up" /> <note xml:id="n1criqod" dur.ppq="1" dur="4" oct="4" pname="c" stem.dir="up" /> </layer> </staff> <staff xml:id="sv59hrb" n="2"> <layer xml:id="lqa9pha" n="5"> <mRest xml:id="m5fkbmr" /> </layer> </staff> </measure> </ending> <measure xml:id="mzizxej" n="24"> <staff xml:id="s10tyx3d" n="1"> <layer xml:id="l3cve2y" n="1"> <note xml:id="ny5qiqw" dur.ppq="1" dur="4" oct="3" pname="b" stem.dir="up" /> <note xml:id="n1eszsm0" dur.ppq="1" dur="4" oct="3" pname="a" stem.dir="up" /> <note xml:id="ng6u5im" dur.ppq="1" dur="4" oct="3" pname="g" stem.dir="up" /> <note xml:id="n1f4j47a" dur.ppq="1" dur="4" oct="3" pname="a" stem.dir="up" /> </layer> </staff> <staff xml:id="s1g4f1or" n="2"> <layer xml:id="l1ch4xez" n="5"> <mRest xml:id="m9v9kww" /> </layer> </staff> </measure> <sb xml:id="s16lhbdg" /> <measure xml:id="mr5tche" right="end" n="25"> <staff xml:id="s1wdz96k" n="1"> <layer xml:id="l1u39b9c" n="1"> <note xml:id="nxyb0ww" dur.ppq="4" dur="1" oct="4" pname="c" /> </layer> </staff> <staff xml:id="shirdx3" n="2"> <layer xml:id="l14q1tbd" n="5"> <mRest xml:id="m49q0pj" /> </layer> </staff> </measure> </section> </score> </mdiv> </body> </music> </mei> </code></pre> <p>My understanding is that it will play #m11eyy6j six times and then execute my #mg074no three times at the beginning of the next repetition. The purpose of this seems to be to modify the timing logic of the repetitions, correct?</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> 1 month ago</strong> </div> <div class="markdown-body"> <p>OK, I understand the sentence now. </p> <p>When I try to export a timemap from my slightly longer example that contains an <code><expansion></code> element, I am getting the error you are mentioning:</p> <pre><code class="language-tsv">[Warning] 5 element(s) with a @plist could not match the target Output written to example.json.</code></pre> <p>And the output <code>example.json</code> contains the non-expanded sequence of the score. In other words, verovio is producing the expected timemap output, so I don't understand why the warning is/should be displayed.</p> <p>If I remove the <code><expansion></code> element there is no warning printed (and I get the same output timemap as expected).</p> <details> <summary> Click to view resulting timemap (sequence measures 1 through 9 in order) </summary> ```json [ { "on": [ "note-L6F1" ], "qstamp": 0, "tempo": "400.000000", "tstamp": 0 }, { "off": [ "note-L6F1" ], "on": [ "note-L10F1" ], "qstamp": 4, "tstamp": 600 }, { "off": [ "note-L10F1" ], "on": [ "note-L12F1" ], "qstamp": 7, "tstamp": 1050 }, { "off": [ "note-L12F1" ], "on": [ "note-L15F1" ], "qstamp": 10, "tstamp": 1500 }, { "off": [ "note-L15F1" ], "on": [ "note-L17F1" ], "qstamp": 13, "tstamp": 1950 }, { "off": [ "note-L17F1" ], "on": [ "note-L20F1" ], "qstamp": 16, "tstamp": 2400 }, { "off": [ "note-L20F1" ], "on": [ "note-L22F1" ], "qstamp": 19, "tstamp": 2850 }, { "off": [ "note-L22F1" ], "on": [ "note-L25F1" ], "qstamp": 22, "tstamp": 3300 }, { "off": [ "note-L25F1" ], "on": [ "note-L27F1" ], "qstamp": 25, "tstamp": 3750 }, { "off": [ "note-L27F1" ], "qstamp": 28, "tstamp": 4200 } ] ``` </details> <p>@rettinghaus and/or @lpugin could comment on if my placement of the <code><expansion></code> element in the data is correct.</p> <p>When I try to apply the expansion with the command-line version of verovio to export the timemap:</p> <pre><code class="language-bash">verovio --expand expansion-L2F1 -t timemap example.mei</code></pre> <p>I get the same warning:</p> <pre><code class="language-tsv">[Warning] 5 element(s) with a @plist could not match the target Output written to example.json.</code></pre> <p>And the expansion list was ignored (the measures are in score order from 1 through 9, not <code>[1,2,3,4,5,1,2,3,6,7,8,9]</code>.</p> <p>When using the <code>--expand</code> option to create an SVG it is working:</p> <img width="1389" alt="Screenshot 2024-09-12 at 3 46 12 AM" src="https://github.com/user-attachments/assets/ac2e1a51-e776-49b2-87e9-005da7f7ef46"> <p>(although this output is not useful to look at since an expanded visual score should remove the repeat barlines and remove the endings to be of any practical use, and note the complication with the key change I added).</p> <p>So while <code>--expand</code> is implemented for generating SVG output from verovio, it is not implemented for timemap output from verovio (and by extension MIDI). Timemaps are created at the same time as MIDI generation (as I remember) so this relates to the subject of the issue, which is that repetitions are not implemented from <code><expansion></code> yet when generating MIDI output from verovio.</p> <p>Implementing this externally from verovio would be more complicated than implementing it inside of verovio. You could expand the <code><expansion></code> list to create MEI data with the desired repeats, but there would be two problems (1) the MEI data would have identical IDs for the repeated music, and (2) if you were to fix this, highlighting the notes in the SVG score would not work for at least one repetition.</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/Dahurica"><img src="https://avatars.githubusercontent.com/u/153709867?v=4" />Dahurica</a> commented <strong> 4 weeks ago</strong> </div> <div class="markdown-body"> <p>Hello, I'm encountering an issue with repeats. When I transpose to other keys, such as C or F,, it shows me <img src="https://github.com/user-attachments/assets/5464a7db-e083-4218-9c23-0841d8820543" alt="image" /></p> <p>When I set my plist length to 70, it can render successfully on the second attempt, but if it exceeds that, an error occurs. This is my logic for handling repeats.</p> <details><summary>JavaScript</summary> <p> ```javascript const setExpansionInMEIXml = scoreData => { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(scoreData, 'application/xml'); const rootElement = xmlDoc.documentElement; let plist = []; const firstMeasure = xmlDoc.querySelector('measure[n]'); const measures = xmlDoc.querySelectorAll('measure[n]'); const endMeasure = measures[measures.length - 1]; const startMeasureArr = Array.from(xmlDoc.querySelectorAll('measure[left="rptstart"]')); const endMeasureArr = Array.from(xmlDoc.querySelectorAll('measure[right="rptend"]')); const allMeasures = Array.from(xmlDoc.querySelectorAll('measure[n]')); const startMeasureNumber = startMeasureArr[0]?.getAttribute('n'); const endMeasureNumber = endMeasureArr[0]?.getAttribute('n'); const startNumber = startMeasureArr[startMeasureArr.length - 1 >= 0 ? startMeasureArr.length - 1 : 0]?.getAttribute('n'); const endNumber = endMeasureArr[endMeasureArr.length - 1 >= 0 ? endMeasureArr.length - 1 : 0]?.getAttribute('n'); const bol = startMeasureArr.length !== endMeasureArr.length; if (bol) { if (endMeasureNumber && startMeasureNumber && startMeasureNumber > endMeasureNumber) { startMeasureArr.unshift(firstMeasure); } if (startNumber && (endNumber < startNumber || !endNumber)) { endMeasureArr.push(endMeasure); } } for (let i = 0; i < startMeasureArr.length; i++) { const noEndingArr = []; const hasEndingArr = []; const first = parseInt(startMeasureArr[i].getAttribute('n')); const end = parseInt(endMeasureArr[i].getAttribute('n')); for (let index = first; index <= end; index++) { const measure = xmlDoc.querySelector(`measure[n="${index}"]`); if (measure.parentNode.tagName !== 'ending') { noEndingArr.push(`#${measure.getAttribute('xml:id')}`); hasEndingArr.push(`#${measure.getAttribute('xml:id')}`); } else { const id = `#${measure.parentNode.getAttribute('xml:id')}`; if (!noEndingArr.includes(id)) { let ending = null; let nextDom = measure.parentNode; let num = 0; const number = parseInt(measure.parentNode.getAttribute('n').split('')[0]); while (!ending && num < 50) { nextDom = nextDom?.nextElementSibling; if (nextDom && nextDom?.tagName === 'ending') { ending = nextDom; } num++; } noEndingArr.push(id); if (ending) { const nextNumber = parseInt(ending.getAttribute('n').split('')[0]); if (nextNumber - number == 1) { const endingId = `#${nextDom.getAttribute('xml:id')}`; hasEndingArr.push(endingId); } } } } } plist.push(...noEndingArr); plist.push(...hasEndingArr); } const expansionElement = xmlDoc.createElementNS(rootElement.namespaceURI, 'expansion'); expansionElement.setAttribute('plist', plist.join(' ')); expansionElement.setAttribute('xml:id', 'expansion-full'); const meiNamespace = rootElement.namespaceURI; const targetSection = xmlDoc.getElementsByTagNameNS(meiNamespace, 'section')[0]; if (targetSection) { targetSection.insertBefore(expansionElement, targetSection.firstChild); } const serializer = new XMLSerializer(); const updatedMeiString = serializer.serializeToString(xmlDoc); return updatedMeiString; }; ``` </p> </details> <p>On Sarfira, I’m also experiencing a memory overflow issue after refreshing the page multiple times. Normally, the renderer's page memory usage fluctuates between 1.2 GB and 2.2 GB, but after multiple refreshes, it reaches over 3 GB, and rendering fails. What could be causing this?</p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/ahankinson"><img src="https://avatars.githubusercontent.com/u/163183?v=4" />ahankinson</a> commented <strong> 4 weeks ago</strong> </div> <div class="markdown-body"> <p>Diagnosing a memory leak without seeing all the code in your application is almost impossible to do. I would suggest using the browser's memory profiling tools to see if you can find out where it's going wrong. </p> </div> </div> <div class="comment"> <div class="user"> <a rel="noreferrer nofollow" target="_blank" href="https://github.com/Dahurica"><img src="https://avatars.githubusercontent.com/u/153709867?v=4" />Dahurica</a> commented <strong> 4 weeks ago</strong> </div> <div class="markdown-body"> <p>Here is my code. I didn’t add any extra content; I only generated the image, yet it still became bloated. This is my code. After refreshing 5 times on an Sarfira , the memory usage increases even more.</p> <details><summary>Javascript</summary> <p> ```javascript <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document
```

I'm not very familiar with C++ code, and I've spent a long time analyzing it with debugging tools but haven’t pinpointed the issue. How should I go about solving this?

Here’s the memory timeline screenshot I captured. b3cc740d4f943d215bcfeef170a17050

4afd8fb493c91a7c4d3b5b800e6971f8

Under normal circumstances. ae4f194fb399cabcedce170788b763bb_720

lpugin commented 4 weeks ago

No problem for me with Safari Version 18.0.1 (20619.1.26.31.7). Your memory usage looks very weird.