Changing voice of note causes other notes to disappear when rests are missing

• Dec 31, 2015 - 11:26
S4 - Minor

Nightly d45cc19, Win 7 / MS 2.0.2, Win 10.

The attached MuseScore file is an extract from a larger one created by MIDI import (original midi file is also attached for reference).

Select the bottom note (F) in the last chord of bar 1. Now change the voice of this note from 1 to 2 using the voice 2 button (Ctrl + Alt + 2).

Expected result: Note changes to voice 2.
Actual result: Note changes to voice 2 and last (voice 2) chord disappears!

This artefact has appeared in several imported midi files and the immediate cause is always missing rests.

Attachment Size
disappearing_notes.mscz 8.95 KB
haydn_andante.mid 6.54 KB


Since you did it yourself, it would at least be useful to describe exactly the complete and precise steps you made, from the Midi file, to arrive at the result of the attached mscz. file. This would save time to others. Thanks.
For now, you indicate the arrival and the result, but not the way to achieve it and for reproduce.

You can reproduce it from the MIDI file:

1. Imported the attached MIDI file, using the following settings in the MIDI import panel:

MuseScore instrument: Classical Guitar
Max Quant: Quaver
Max voices: 3
Tuplets: 3-plet only
Show staccatto: No

2. Insert an anacrusis of 1 bar of 1/4 note duration.

3. Select from the first note in bar 2 to the end of the piece, and cut and paste so that the music starts at the beginning (i.e shift everything one 1/4 note to the left).

4. Go to bar 18. Select the bottom F in the last chord. Change the voice of the note from V1 to V2 by clicking on the voice 2 button OR using the shortcut Ctrl + Alt + 2.

Result: The last chord in bar 20 disappears.

OK, thanks, I can now reproduce with a simple test case:

1) voice 1: 6 C C C C (four half notes)
2) voice 2 6 A A A A (four half notes)
3) delete second half note from voice 2, then delete the rest
4) select the second half note in voice 1
5) move to voice 2 (click voice icon 2 or press Ctrl+Alt+2)

Result: the voice 2 half note in the next measure disappears.

Looking at the code, it seems the calls to expandVoice() and/or makeGapVoice() are not doing the right things here. I kind of suspect this may turn out to be related to #28886: Full measure rest created in next measure if changing length of chordrest in voice 2,3,4 creates rest at end of measure, but maybe not. Anyhow, I will look into this.

BTW, same bug exists in the above if at step 5, you run Edit / Tools / Fill With Slashes, which also uses expandVoice().

I had been under the impression this function was supposed to fill any gaps in the current voice up to the current segment *and* from there to the next cr, and then makeGapVoice() would take that voice and create a gap of the desired length. But in fact, if expandVoice() finds no gap before the current segment, it returns immediately rather than filling to the next cr, even if there is a gap at the current segment. Maybe that is correct behavior in some situations, but in this case, makeGapVoice() behaves poorly if there is already a gap present, leading it to cr4eate a gap incorrectly starting with the following cr.

So I'd like to better understand the intended function of expandVoice() and the various makeGap functions. My - perhaps naive - impression is that we should "fix" expandVoice() to always fill to the next cr, since it serves that function in some cases already (if the current segment is within or preceded by a gap). This fixes both issues (with the change voice and slash fill commands).

BTW, it looks like the full measure rest issue mentioend above does stem from the same basic area code, but it's probably not really related. I think we're just failing to consider the case of the next cr being in the next measure in the last four lines of expandVoice(). Either that or maybe there is some other context in which we actually *do* want the next measure filled with a measure rest.

Never mind that last bit about the full measure rest - I think the code in expandVoice() is fine. The issue is that we really don't need to be calling expandVoice() at all in this case. The call happens at the end of the main loop in changeCRlen(). If there are no more durations in the list, we really don't need to do the expandVoice(). Probably we could tweak the logic of changeCRlen() to no do the unnecessary expandVoice(). But I'll leave that for another time / person.

Status (old) active patch (code needs review)

Here is my proposed fix:

Currently, expandVoice() will fill a gap that precedes the current segment, and it will also fill a gap *at* the current segment but *only* if there was also a gap preceding it. I have changed it to always fill a gap at the current segment, whether there is also a gap preceding it or not. This fixes the two bug (in the change voice and slash fill commands), and hopefully no other code was depending on the current behavior. I also added comments to make the behavior more explicit.

Hopefully this is the right solution. If not, then I hope we can take the opportunity to better document what is actually supposed to happen.