"Copy lyrics to clipboard" drops syllables if there is none at that spot in the previous verse

• Mar 2, 2022 - 08:43
Reported version
3.2
Type
Functional
Frequency
Few
Severity
S3 - Major
Reproducibility
Always
Status
active
Regression
No
Workaround
No
Project

Any SATB score where any instrument have got more than 2 voices and a specific group of notes. I attach my example.
The function involved is the "Copy lyrics to clipboard"
Wherever the score contains instances of notes group like in picture Capture1.JPG, the lyrics of and around that group of notes copied into the clipboard by the above function are messed-up.
(See also https://musescore.org/en/node/329998)

Attachment Size
Capture1.JPG 22.87 KB
a.mscz 20.63 KB

Comments

Type Wording/Translation Functional

Strangly this happens to that score only, at least so far I couldn't find any that behaves the same bogus way?

Frequency Once Few

Another intersting (and possibly related) bug: If a score (or part of it) doesn't have a 1st stanza, but starts with the 2nd (often done in MuseScore 2 for layout purposes, not needed for that in MuseScore 3 anymore), that doesn't make it into the clipboard at all.

Here's one I found, missing the first part (see above) and also missing a single syllable in the 4st stanza (here added back in bold)

  1. Tempus ad est gratiae, Hoc quod optabamus; Carmina laetitiae, Devote redamus.
  2. Deus homo factus est, Natura mirante; Mundus renovatus est A Christo regnante.
  3. Ezechiellis porta Clausa per transitur; Unde lux est orta Salus invenitur.
  4. Ergo nostra cantio, Psallat iam in lustro; Benedicat Domino: Salus Regi nostro.

Here too after the previous stanza has a (mid-word) melisma, this does seem to be related to the bug.

Attachment Size
136_Gaudete.mscz 18.51 KB
Title copy lyrics to clipboard makes mistakes for voices above 2 "Copy lyrics to clipboard" drops syllables if there is none at that spot in the previous verse

So I strongly suspect that a syllable gets lost if the previous verse doesn't have one at that spot, that'd explain both problems outlined above.

I've verified that theory with some other scores of mine, like one that has ony 2 verses (and the Refrain in verse 2, without a verse 1).

It also misses all lyrics that are not attached to voice 1 notes!

This though seems pretty easy to fix (in 3.x, but I assume master to be very similar):

diff --git a/libmscore/score.cpp b/libmscore/score.cpp
index 0eef195092..9c4bdfb8fe 100644
--- a/libmscore/score.cpp
+++ b/libmscore/score.cpp
@@ -4399,7 +4399,7 @@ QString Score::extractLyrics()
       QString result;
       masterScore()->setExpandRepeats(true);
       SegmentType st = SegmentType::ChordRest;
-      for (int track = 0; track < ntracks(); track += VOICES) {
+      for (int track = 0; track < ntracks(); track++) {
             bool found = false;
             size_t maxLyrics = 1;
             const RepeatList& rlist = repeatList();

Need to check for unwanted side effercs though

Edit: Hmm, no, seems too simple, with that change voice 2 lyrics are placed at the end, rather than intermixed with voice 1 lyrics. Might still be better than getting ignored alltogether though?

Also it ignores lyrics above stave (a known issue though, marked as TODO), might be easy to fix (in 3.x, but I assume master to be very similar):

diff --git a/libmscore/score.cpp b/libmscore/score.cpp
index 0eef195092..23d9509097 100644
--- a/libmscore/score.cpp
+++ b/libmscore/score.cpp
 
@@ -4421,7 +4421,7 @@ QString Score::extractLyrics()
                                     maxLyrics = cr->lyrics().size();
                               if (playCount >= int(cr->lyrics().size()))
                                     continue;
-                              Lyrics* l = cr->lyrics(playCount, Placement::BELOW);  // TODO: ABOVE
+                              Lyrics* l = cr->lyrics(playCount);
                               if (!l)
                                     continue;
                               found = true;
@@ -4450,7 +4450,7 @@ QString Score::extractLyrics()
                                           maxLyrics = cr->lyrics().size();
                                     if (lyricsNumber >= cr->lyrics().size())
                                           continue;
-                                    Lyrics* l = cr->lyrics(lyricsNumber, Placement::BELOW);  // TODO
+                                    Lyrics* l = cr->lyrics(lyricsNumber);
                                     if (!l)
                                           continue;
                                     found = true;

Again, need to check for unwanted side effects

Hmm, no such method exists, so would need to get added (in 3.x, but I assume master to be very similar):

diff --git a/libmscore/chordrest.cpp b/libmscore/chordrest.cpp
index 5c8341ab3c..66bcd654e7 100644
--- a/libmscore/chordrest.cpp
+++ b/libmscore/chordrest.cpp
@@ -1336,6 +1336,15 @@ Shape ChordRest::shape() const
 //   lyrics
 //---------------------------------------------------------
 
+Lyrics* ChordRest::lyrics(int no) const
+      {
+      for (Lyrics* l : _lyrics) {
+            if (l->no() == no)
+                  return l;
+            }
+      return 0;
+      }
+
 Lyrics* ChordRest::lyrics(int no, Placement p) const
       {
       for (Lyrics* l : _lyrics) {
diff --git a/libmscore/chordrest.h b/libmscore/chordrest.h
index 41a595f17c..e2b60d8c63 100644
--- a/libmscore/chordrest.h
+++ b/libmscore/chordrest.h
@@ -136,6 +136,7 @@ class ChordRest : public DurationElement {
 
       const std::vector<Lyrics*>& lyrics() const { return _lyrics; }
       std::vector<Lyrics*>& lyrics()             { return _lyrics; }
+      Lyrics* lyrics(int verse) const;
       Lyrics* lyrics(int verse, Placement) const;
       int lastVerse(Placement) const;
       bool isMelismaEnd() const;

See https://github.com/musescore/MuseScore/pull/10709 on this particular 'side track', for the master branch (I've added it to https://github.com/musescore/MuseScore/pull/9000 for 3,x too)