Skip to content

Commit

Permalink
Merge pull request #26100 from mike-spa/fixLocalTimeSigIssues
Browse files Browse the repository at this point in the history
Fix a bunch of different issue connected to local time signatures
  • Loading branch information
miiizen authored Jan 24, 2025
2 parents 5b330c5 + c55afd0 commit 3255273
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 21 deletions.
3 changes: 3 additions & 0 deletions src/engraving/dom/check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ void Measure::checkMeasure(staff_idx_t staffIdx, bool useGapRests)
LOGN("in measure underrun %6d at %d-%d track %zu", tick().ticks(),
(currentPos / stretch).ticks(), (expectedPos / stretch).ticks(), track);
fillGap(expectedPos, currentPos - expectedPos, track, stretch, useGapRests);
if (currentPos >= f) {
break;
}
}

DurationElement* de = cr;
Expand Down
5 changes: 3 additions & 2 deletions src/engraving/dom/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,10 @@ void Score::expandVoice(Segment* s, track_idx_t track)
//
Measure* m = s->measure();
Fraction stick = ps ? ps->tick() : m->tick();
Fraction stretch = staff(track2staff(track))->timeStretch(stick);
Fraction ticks = s->tick() - stick;
if (ticks.isNotZero()) {
setRest(stick, track, ticks, false, 0);
setRest(stick, track, ticks * stretch, false, 0);
}

//
Expand All @@ -701,7 +702,7 @@ void Score::expandVoice(Segment* s, track_idx_t track)
if (ticks == m->ticks()) {
addRest(s, track, TDuration(DurationType::V_MEASURE), 0);
} else {
setRest(s->tick(), track, ticks, false, 0);
setRest(s->tick(), track, ticks * stretch, false, 0);
}
}

Expand Down
44 changes: 26 additions & 18 deletions src/engraving/dom/edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,18 +1201,21 @@ bool Score::rewriteMeasures(Measure* fm, const Fraction& ns, staff_idx_t staffId
Measure* nm = nullptr;
LayoutBreak* sectionBreak = nullptr;

// disable local time sig modifications in linked staves
if (staffIdx != muse::nidx && masterScore()->excerpts().size() > 0) {
MScore::setError(MsError::CANNOT_CHANGE_LOCAL_TIMESIG_HAS_EXCERPTS);
return false;
}

//
// split into Measure segments fm-lm
//
auto foundLM = [fm, staffIdx](MeasureBase* curMeas, Measure* curLM) {
if (!curMeas || !curMeas->isMeasure() || curLM->sectionBreak()) {
return true;
}
Segment* timeSigSeg = toMeasure(curMeas)->first(SegmentType::TimeSig);
if (timeSigSeg && curMeas != fm) {
return staffIdx == muse::nidx || timeSigSeg->element(staff2track(staffIdx));
}
return false;
};
for (MeasureBase* measure = fm;; measure = measure->next()) {
if (!measure || !measure->isMeasure() || lm->sectionBreak()
|| (toMeasure(measure)->first(SegmentType::TimeSig) && measure != fm)) {
if (foundLM(measure, lm)) {
// save section break to reinstate after rewrite
LayoutBreak* layoutBreak = lm->sectionBreakElement();

Expand Down Expand Up @@ -1317,6 +1320,9 @@ bool Score::rewriteMeasures(Measure* fm, const Fraction& ns, staff_idx_t staffId
}
Segment* s = nm->undoGetSegment(SegmentType::TimeSig, nm->tick());
for (size_t i = 0; i < nstaves(); ++i) {
if (staffIdx != muse::nidx && i != staffIdx) {
continue;
}
if (!s->element(i * VOICES)) {
TimeSig* ots = staff(i)->timeSig(nm->tick());
if (ots) {
Expand Down Expand Up @@ -1376,10 +1382,15 @@ void Score::cmdAddTimeSig(Measure* fm, staff_idx_t staffIdx, TimeSig* ts, bool l
startStaffIdx = staffIdx;
endStaffIdx = startStaffIdx + 1;
} else {
// TODO: get index for this score
LOGD("cmdAddTimeSig: unable to write local time signature change to linked score");
startStaffIdx = 0;
endStaffIdx = 0;
const Staff* thisStaff = staff(staffIdx);
const Staff* linkedStaff = thisStaff->findLinkedInScore(score);
if (linkedStaff) {
startStaffIdx = linkedStaff->idx();
endStaffIdx = startStaffIdx + 1;
} else {
startStaffIdx = 0;
endStaffIdx = 0;
}
}
} else {
startStaffIdx = 0;
Expand Down Expand Up @@ -1455,7 +1466,9 @@ void Score::cmdAddTimeSig(Measure* fm, staff_idx_t staffIdx, TimeSig* ts, bool l
// we will only add time signatures if this succeeds
// this means, however, that the rewrite cannot depend on the time signatures being in place
if (mf) {
if (!mScore->rewriteMeasures(mf, ns, local ? staffIdx : muse::nidx)) {
auto staffIdxRangeOnMaster = getStaffIdxRange(mScore);
if (staffIdxRangeOnMaster.second != staffIdxRangeOnMaster.first
&& !mScore->rewriteMeasures(mf, ns, local ? staffIdxRangeOnMaster.first : muse::nidx)) {
undoStack()->activeCommand()->unwind();
return;
}
Expand Down Expand Up @@ -1514,11 +1527,6 @@ void Score::cmdAddTimeSig(Measure* fm, staff_idx_t staffIdx, TimeSig* ts, bool l

void Score::cmdRemoveTimeSig(TimeSig* ts)
{
if (ts->isLocal() && masterScore()->excerpts().size() > 0) {
MScore::setError(MsError::CANNOT_CHANGE_LOCAL_TIMESIG_HAS_EXCERPTS);
return;
}

Measure* m = ts->measure();
Segment* s = ts->segment();

Expand Down
2 changes: 1 addition & 1 deletion src/engraving/rendering/score/systemlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,7 +850,7 @@ void SystemLayout::layoutSystemElements(System* system, LayoutContext& ctx)

// don't layout any tuplets covered by this top level tuplet for this voice--
// they've already been laid out by layoutTuplet().
skipTo[track] = de->tick() + de->ticks();
skipTo[track] = de->tick() + de->actualTicks();
}
}

Expand Down
Binary file added src/engraving/tests/timesig_data/timeSig-11.mscz
Binary file not shown.
108 changes: 108 additions & 0 deletions src/engraving/tests/timesig_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,111 @@ TEST_F(Engraving_TimesigTests, timesig_78216)
EXPECT_FALSE(m3->findSegment(SegmentType::TimeSig, m3->endTick())) << "Should be no timesig at the end of measure 3.";
delete score;
}

TEST_F(Engraving_TimesigTests, timesig_11)
{
MasterScore* score = ScoreRW::readScore(TIMESIG_DATA_DIR + u"timeSig-11.mscz");
EXPECT_TRUE(score);
score->doLayout();

TimeSig* timeSig = Factory::createTimeSig(score->dummy()->segment());
timeSig->setSig(Fraction(5, 4));

TimeSig* timeSig2 = Factory::createTimeSig(score->dummy()->segment());
timeSig2->setSig(Fraction(7, 8));

TimeSig* timeSig3 = Factory::createTimeSig(score->dummy()->segment());
timeSig3->setSig(Fraction(3, 4));

Measure* secondMeas = score->firstMeasure()->nextMeasure();
Measure* thirdMeas = secondMeas->nextMeasure();
staff_idx_t oboeStaff = 1;
staff_idx_t clarinetStaff = 2;

// Add local timeSig to Clarinet staff at meas 3
score->startCmd(TranslatableString::untranslatable("Engraving time signature tests"));
score->cmdAddTimeSig(thirdMeas, clarinetStaff, timeSig, true);
score->endCmd();
// Check timeSig exist at meas 3 and is only on Clarinet
Segment* timeSigSegment = thirdMeas->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
EXPECT_TRUE(timeSigSegment);
for (staff_idx_t stfIdx = 0; stfIdx < score->nstaves(); ++stfIdx) {
EngravingItem* timeSig = timeSigSegment->element(staff2track(stfIdx));
if (stfIdx == clarinetStaff) {
EXPECT_TRUE(timeSig);
} else {
EXPECT_FALSE(timeSig);
}
}

// Add local timeSig to Clarinet staff at meas 2
score->startCmd(TranslatableString::untranslatable("Engraving time signature tests"));
score->cmdAddTimeSig(secondMeas, clarinetStaff, timeSig2, true);
score->endCmd();
// Check timeSig exist at meas 2 and is only on Clarinet
Segment* timeSigSegment2 = secondMeas->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
EXPECT_TRUE(timeSigSegment2);
for (staff_idx_t stfIdx = 0; stfIdx < score->nstaves(); ++stfIdx) {
EngravingItem* timeSig = timeSigSegment2->element(staff2track(stfIdx));
if (stfIdx == clarinetStaff) {
EXPECT_TRUE(timeSig);
} else {
EXPECT_FALSE(timeSig);
}
}
// Check no other timeSigs were added at measure 3
for (staff_idx_t stfIdx = 0; stfIdx < score->nstaves(); ++stfIdx) {
EngravingItem* timeSig = timeSigSegment->element(staff2track(stfIdx));
if (stfIdx == clarinetStaff) {
EXPECT_TRUE(timeSig);
} else {
EXPECT_FALSE(timeSig);
}
}

// Add local timeSig to Oboe staff at meas 2
score->startCmd(TranslatableString::untranslatable("Engraving time signature tests"));
score->cmdAddTimeSig(secondMeas, oboeStaff, timeSig3, true);
score->endCmd();
// Check timeSig exist at meas 2 on Oboe and Clarinet
for (staff_idx_t stfIdx = 0; stfIdx < score->nstaves(); ++stfIdx) {
EngravingItem* timeSig = timeSigSegment2->element(staff2track(stfIdx));
if (stfIdx == oboeStaff || stfIdx == clarinetStaff) {
EXPECT_TRUE(timeSig);
} else {
EXPECT_FALSE(timeSig);
}
}
// Check no other timeSigs were added at measure 3
for (staff_idx_t stfIdx = 0; stfIdx < score->nstaves(); ++stfIdx) {
EngravingItem* timeSig = timeSigSegment->element(staff2track(stfIdx));
if (stfIdx == clarinetStaff) {
EXPECT_TRUE(timeSig);
} else {
EXPECT_FALSE(timeSig);
}
}

// Check all timeSigs have been correctly cloned to parts
for (Score* partScore : score->scoreList()) {
if (partScore->isMaster()) {
continue;
}
Segment* timeSigSeg1 = partScore->tick2segment(secondMeas->tick(), true, SegmentType::TimeSig);
Segment* timeSigSeg2 = partScore->tick2segment(thirdMeas->tick(), true, SegmentType::TimeSig);
EXPECT_TRUE(timeSigSeg1);
EXPECT_TRUE(timeSigSeg2);
if (partScore->name() == u"Oboe") {
EXPECT_TRUE(timeSigSeg1->element(0));
EXPECT_FALSE(timeSigSeg2->element(0));
} else if (partScore->name() == u"Clarinet in B♭") {
EXPECT_TRUE(timeSigSeg1->element(0));
EXPECT_TRUE(timeSigSeg2->element(0));
} else {
EXPECT_FALSE(timeSigSeg1->element(0));
EXPECT_FALSE(timeSigSeg2->element(0));
}
}

delete score;
}

0 comments on commit 3255273

Please sign in to comment.