Voice 2 note stems face wrong way when noteheads made invisible

• Apr 25, 2021 - 20:36
S3 - Major

OS: Windows 10 (10.0), Arch.: x86_64, MuseScore version (64-bit):, revision: 3224f34

  1. Enter the following series of notes in voice 1 (for example):
  2. Return the cursor to the start of the measure and enter a quarter note at C3, then a quarter note at E3:
  3. Make both voice 2 noteheads invisible.

Expected result: The voice 1 and 2 noteheads merge and the stems of voice 2 point in the opposite direction to those of voice 1.
Actual result: The voice 2 stems are facing the wrong way:

Note: This feature works OK in MS 2.3.2.


I have an idea of the commit that broke this. Easy enough to guess...
One more painful side effect, (somewhere in April 2020 e.g. ...)

No, I don't. I do remember a change reg. collapsing matching rests, is that what you mean? I'd still not know what commit that might have been.
(Hmm, no, can't be that)

"I do remember a change reg. collapsing matching rests, is that what you mean? "
Of course! April 20, 2020
(if I mistaken something, between April 15 - result of first image - and April 20 anyway - second image)

This issue is not caused by the commit in question. It already existed in 2.3.2 and in the earliest builds of MuseScore 3. If the half rest in voice 2 were to be deleted or made invisible, or if the pattern were to be repeated throughout the measure, or if the score were in 2/4, you would get the same undesired result, no matter which build of the software you are using. For the real cause of this issue, see https://github.com/musescore/MuseScore/pull/1692.

The workaround is simply to flip the note immediately after making it invisible.

  1. Open the attached MS 2.3.2 file in MS 2.3.2. Note that voice 2 stems are pointing DOWN.
  2. Open the same file in 3.6.2.
    Expected result: Voice 2 stems should also be pointing DOWN.
    Actual result: Voice 2 stems are pointing UP instead.

If the bug is the same in both versions, shouldn't the files look the same? But if there is an additional bug in MS3 wouldn't this explain the difference?

Attachment Size
v2_note_stems.mscz 5.11 KB

I think this is the inevitable effect of combing two very good improvements - the one I made that @mattmcclinch referenced ( https://github.com/musescore/MuseScore/pull/1692), and the one Matt made that @cadiz1 referenced (https://github.com/musescore/MuseScore/pull/5371). I would claim both of those were good and desirable changes in general, but they do have this unfortunate side effect when combined. And it's definitely more my fault than Matt's.

Thinking through the reason for my original change, it was so a completely invisible voice 2 would not interfere with voice 1 stem directions. But voice 2 is not completely invisible here - only the noteheads are invisible. The stems are still visible. So basically, the original check I made wasn't strong enough. In particular, here:


As the comment says, the algorithm is designed to "consider chord visible if any note is visible". That's not good enough. It should also be considered visible if the stem or any other part of the chord is visible. And probably there is better way of checking that - like, the chord should probably already be managing this.

Checking for stem (and hook) visibility there is not helping the issue though, I tried this:

diff --git a/libmscore/measure.cpp b/libmscore/measure.cpp
index 3a5d552a8..729707588 100644
--- a/libmscore/measure.cpp
+++ b/libmscore/measure.cpp
@@ -2793,7 +2793,11 @@ bool Measure::hasVoices(int staffIdx, Fraction stick, Fraction len) const
                         if (cr->tick() + cr->actualTicks() <= stick)
                         bool v = false;
-                        if (cr->isChord()) {
+                        if (cr->isStem() && cr->visible())
+                              v = true;
+                        else if (cr->isHook() && cr->visible())
+                              v = true;
+                        else if (cr->isChord()) {
                               // consider chord visible if any note is visible
                               Chord* c = toChord(cr);
                               for (Note* n : c->notes()) {

And the attached test score

Attachment Size
test.mscz 2.87 KB

Duh! Dumb idea...
OK, then let's try this:

diff --git a/libmscore/measure.cpp b/libmscore/measure.cpp
index 3a5d552a8..691b831f9 100644
--- a/libmscore/measure.cpp
+++ b/libmscore/measure.cpp
@@ -2802,6 +2802,9 @@ bool Measure::hasVoices(int staffIdx, Fraction stick, Fraction len) const
+                              // same if stem or hook(s) are visible
+                              if ((c->stem() || c->hook()) && c->visible())
+                                    v = true;
                         else if (cr->isRest())
                               v = cr->visible() && !toRest(cr)->isGap();

Works with the above sample score

No they don't. And they shouldn't either IMHO.
Hiding the rest has the same result as deleting it.
If you want/need it, flip the stems manually.

Sadly. And I disagree on the rest. The previous behavior was preferable, by far: namely, an action in one voice (deleting/hiding a rest for example) must not, should not, strictly speaking have any effect on the direction of the note stems in the other voices. In guitar writing, and its multi-voice context, this is a permanent mess. It may seem trivial to toggle a note with the X shortcut, but when you have to do it systematically in many situations - and check that you haven't forgotten any (when this problem didn't exist before), it's just a boring regression. So, precisely, just to avoid: "If you want/need it, flip the stems manually."
Minor problem for you, even non-existent, major and boring problem for me in my daily practice.

Indeed I don't. I do for Closed Score SATB though.
Maybe a staff property for this is in order (similar to the 'merge matching rests'), se we both can get what we need, via a template for example.
Feel free to file a Suggestion for that

@cadiz1 you might want to check again - it has not worked the way you describe for years. The change of mine that caused this is in 2.3.2 as well. The feature I implemented - making sure invisible content in other voices doesn't affect voice 1 unnecessarily - was buggy enough that it didn't work in some cases, including with rests, but it definitely worked exactly as expected for hidden notes.

Try entering four quarter notes in voice 1 (top line F, say), then four more in voice 2 (bottom space F), then mark all the quarter note noteheads in voice 2 invisible. The voice 1 stems will return down. This is by design and quite necessary behavior for many cases, such a when adding invisible notes for playback purposes. And in many of those cases, needing to use "X" to flip the stems is not an acceptable workaround, because if this is a transposing instrument, those stem directions might not be correct in both concert and transposed pitches. That was actually the main reason this feature was requested and implemented.

In any case, the change proposed by Jojo would actually fix this very case - because you are hiding noteheads only, but not stems. So this years-old bug will be fixed. Be happy, not sad :-). With Jojo's change, the only time voice 1 stems will revert to default is if voice 2 is completely empty - not just noteheads hidden, but stems, rests, and everything else. If anything in voice 2 is visible, the stems remain. Since your use case has you hiding noteheads only, you'll see only improvement, just like everyone else will.

But it is true that an invisible rest will not force a stem up. For most cases this is exactly as expected. It does mean there might be cases where you still need to manually flip a stem up if the only thing in voice 2 is an invisible rest, because again, this is what is desired in the majority of cases.

Unfortunatly this seems to have caused a regression: a (visible) voice 1 is all up-stem in presence of an invisible voice 2! Only exception seems to be rests.

Fix version