cmdSplitMeasure() must break notes in other Voices using toDurationList()

• Mar 25, 2017 - 13:14
Reported version
2.2
Type
Functional
Severity
S3 - Major
Status
closed
Project

I seems to have make a segfault with #183846: Splitting irregular measures produce extra rests (I need to verify this, though). If I split measure before the selected 16th rest:

Screenshot at 2017-03-25 08-53-29.png

Then segfault:

1   raise                                                                                      0x7fffee8dfa10 
2   abort                                                                                      0x7fffee8e113a 
3   QMessageLogger::fatal(const char *, ...) const                                             0x7fffef282e21 
4   qt_assert(const char *, const char *, int)                                                 0x7fffef27e3de 
5   Ms::TDuration::TDuration                             durationtype.cpp                 422  0x11991a4      
6   Ms::TrackList::write                                 range.cpp                        384  0x13a1cea      
7   Ms::ScoreRange::write                                range.cpp                        552  0x13a2be8      
8   Ms::Score::cmdSplitMeasure                           splitMeasure.cpp                 49   0x13a77e0      
9   Ms::ScoreView::cmd                                   scoreview.cpp                    3130 0xa53a54       
10  Ms::MuseScore::cmd                                   musescore.cpp                    4685 0xbbce60       
11  Ms::MuseScore::cmd                                   musescore.cpp                    4287 0xbbaa7a       
12  Ms::MuseScore::qt_static_metacall                    moc_musescore_IKSZSQWDH4YDZS.cpp 734  0x10151e5      
13  QMetaObject::activate(QObject *, int, int, void * *)                                       0x7fffef4a2d49 
14  Ms::ScoreTab::actionTriggered                        moc_scoretab_QR4BWIRYNXAVBM.cpp  195  0x101c6d9      
15  Ms::ScoreTab::qt_static_metacall                     moc_scoretab_QR4BWIRYNXAVBM.cpp  97   0x101c2e4      
16  QMetaObject::activate(QObject *, int, int, void * *)                                       0x7fffef4a2d49 
17  QActionGroup::triggered(QAction *)                                                         0x7ffff01564ff 
18  ??                                                                                         0x7ffff0157079 
19  QMetaObject::activate(QObject *, int, int, void * *)                                       0x7fffef4a2d49 
20  QAction::triggered(bool)                                                                   0x7ffff0152c92 
... 

Let me investigate right now...


Comments

Title SegFault when split measure 2nd voice rest Q_ASSERT Failure when split measure 2nd voice rest

Ok, I figured out that the issue is not a *SegFault*, but rather is a Q_ASSERT failure, which only occurs when run on a *debug build*.

TDuration::TDuration(const Fraction& l, bool truncate, int maxDots, DurationType maxType)
      {
      setType(maxType); // use maxType to avoid testing all types if you know that l is smaller than a certain DurationType
      setDots(maxDots);
      truncateToFraction(l, maxDots);
      Q_ASSERT(truncate || (fraction() - l).numerator() == 0); // check for exact fit
      }

in that assert for my earlier attached example:

fraction() is a half note.
l is 11/16

Therefore fraction() - l doesn't evaluate to zero...so isn't an exact fit.

I am noting that on latest 2.1 commit, there is no assert failure when splitting before the dotted eight, or splitting before the first eight rest, but only when splitting before the sixteenth rest.

There is an actual bug, since the result in 2.1-nightly ae09a44:

split184061-one-measure-whole-note-voice-2-after-split-2.1.png

splits the whole note into only a half note and a quarter note...which do not fill entire duration of whole note. Neither of the resulting notes fill their entire measure.

I'm changing variable name Fraction "rest" in TrackList::write to "remaining", because it appears to represent that reamining time in the measure being written to. "rest" is just confusing because a Rest is something different.

I'm trying to figure out why TrackList::write will break up rests according to toDurationList:

if (e->type() == Element::Type::REST || e->type() == Element::Type::REPEAT_MEASURE) {
        for (TDuration k : toDurationList(d, false)) {

But doesn't use toDurationList for breaking up chords, and instead just sets duration of note to d (the minimum of what is remaining in the measure and the duration of the note being split up:

        c->setDuration(d);

I think should use toDurationlist for both cases, to handle note duration which can't be easily split up into two smaller durations.

Two tests:

split184061-no-tie:
split184061-no-tie_.png

when split 11/16 through becomes:
split184061-no-tie-ref_.png

and split184061-keep-tie:
split184061-keep-tie_.png

when split 11/16 through becomes:
split184061-keep-tie-ref_.png

The strange thing is those test work from inside mscore, but the second one is not working from inside mtest because it is not copying that final quarter note when it saves the file for comparison. So I need to look into that. But other than that, I'm sharing my changes for others to comment before I make some more tests and submit PR:

https://github.com/musescore/MuseScore/compare/2.1...ericfont:184061-Sp…

The strange thing is those test work from inside mscore, but the second one is not working from inside mtest because it is not copying that final quarter note when it saves the file for comparison. So I need to look into that.

Turns out I had to add score->doLayout() to the test in order for that quarter note to actually become a segment in the initial score. I don't quite understand why that is necessary, to be honest.

Actually I'd better readjust that last test so it doesn't have any rests, because the problem is split measure undoes the rest organization, but there is another feature request that says should instead try to preserve as much of repeats organization before and after the split.

Anyway I'd appreciate any suggestions about what strange behavior to test. I'm wondering if we allow to split measures where one or more instruments have a measure rest. I would think measure rests are unbreakable, so maybe we need to prohibit that...

I put in the feature request #166096: Make individual beat rests when splitting a measure because when you split a measure on a multi instrument score and fewer than all of the instruments have notes, those who do not have notes have no way of knowing that the measure has been shortened if there are measure rests in both measures, or actually either measure. This will put a human off on his count to reenter the song. It is normal that if a measure has fewer than a whole measure's rest, the rests are indicated with the correct count. The song that I noticed this on was in 3/4 time that started with a pickup of 1 beat. There was a repeat after beat 2 of the last measure of the phrase, with another 1 beat pickup starting the next phrase. Both sides of the repeat had whole measure rests. How is a human supposed to know these measures are shortened. Sorry I don't remember which song it is, but it looked something like this:

broken measure.png

Of course I fixed it when I realized the problem, but MS should do that.

I'm all in agreement that measure rests are extremely confusing way to represent the result of split measures. I think measure rests only should be used when measure's actual beats equals its time signature, which is not the case for newly-split measures. So I support that.

But in the latest pic I shared, you will notice that the split completely reorganized how the empty space is divided into rests. What I think is if there are already rest elements (other than Measure Rest), then each of those rest elements should be preserved in same manner that they were before the split if possible (and if that isn't possible, for instance if a rest was straddling the tick of the split, then that rest should be broken up). But no combining of rests. There is always the "Simplify Durations" function which user could manually apply after performing the split, which could merge rests according to the time Sig's subdivision if desired.

After further testing I realize that when the parts are extracted, both measures are counted as part of multi measure rests. When I manually changed the rests before the repeat to 1/4 notes it broke the multi measure rest but making a 1/4 rest left it included in the ensuing multi measure rest. Making the measure after the repeat 2 1/8 rests caused it to break the multi measure rest.

You can test this by making a score with more than 1 instrument. Leave at least 1 instrument with no notes (for the multi measure rests) split the measure and make the changes I described above. Include a repeat sign in the middle of the split measure.

#20 - I agree with what you said about existing rests. The user was obviously happy with the rests before the split, so they should be left as is unless one is split.

Status (old) patch (code needs review) fixed

Fixed in branch 2.1, commit 10edf46add

fix #184061 Split Measure must break notes w/ toDurationList()

Splitting a measure via cmdSplitMeasure() may require that notes in other voices break apart their duration into something that cannot be represented with just a single note. Therefore it is necessary to use toDurationList() to break the note's duration up into multiple durations that are tied together to represent the original note before the split.