Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #24702: Voice too long corruption #24948

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/engraving/dom/noteentry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,33 @@ Ret Score::putNote(const Position& p, bool replace)

ChordRest* cr = m_is.cr();

// If there's an overlapping ChordRest at the current input position, shorten it...
if (!cr) {
MasterScore* ms = masterScore();
ChordRest* prevCr = m_is.segment()->nextChordRest(m_is.track(), /*backwards*/ true, /*stopAtMeasureBoundary*/ true);
if (prevCr && prevCr->endTick() > m_is.tick()) {
const Fraction overlapDuration = prevCr->endTick() - m_is.tick();
const Fraction desiredDuration = prevCr->ticks() - overlapDuration;

const InputState inputStateToRestore = m_is; // because changeCRlen will alter the input state
ms->changeCRlen(prevCr, desiredDuration, /*fillWithRest*/ false);

// Fill the difference with tied notes if necessary...
const Fraction difference = desiredDuration - prevCr->ticks();
if (prevCr->isChord() && difference.isNotZero()) {
Fraction startTick = prevCr->endTick();
Chord* prevChord = toChord(prevCr);
const std::vector<TDuration> durationList = toDurationList(difference, true);
for (const TDuration& dur : durationList) {
prevChord = ms->addChord(startTick, dur, prevChord, /*genTie*/ bool(prevChord), prevChord->tuplet());
startTick += dur.fraction();
}
}

m_is = inputStateToRestore;
}
}

auto checkTied = [&](){
if (!cr || !cr->isChord()) {
return false;
Expand Down
14 changes: 11 additions & 3 deletions src/engraving/dom/segment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,15 +585,23 @@ Segment* Segment::nextCR(track_idx_t track, bool sameStaff) const
// get the next ChordRest, start at this segment
//---------------------------------------------------------

ChordRest* Segment::nextChordRest(track_idx_t track, bool backwards) const
ChordRest* Segment::nextChordRest(track_idx_t track, bool backwards, bool stopAtMeasureBoundary) const
{
for (const Segment* seg = this; seg; seg = backwards ? seg->prev1() : seg->next1()) {
const Segment* seg = this;
while (seg) {
EngravingItem* el = seg->element(track);
if (el && el->isChordRest()) {
return toChordRest(el);
}

if (backwards) {
seg = stopAtMeasureBoundary ? seg->prev() : seg->prev1();
continue;
}

seg = stopAtMeasureBoundary ? seg->next() : seg->next1();
}
return 0;
return nullptr;
}

EngravingItem* Segment::element(track_idx_t track) const
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/dom/segment.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ class Segment final : public EngravingItem

Segment* nextCR(track_idx_t track = muse::nidx, bool sameStaff = false) const;

ChordRest* nextChordRest(track_idx_t track, bool backwards = false) const;
ChordRest* nextChordRest(track_idx_t track, bool backwards = false, bool stopAtMeasureBoundary = false) const;

EngravingItem* element(track_idx_t track) const;

Expand Down
14 changes: 14 additions & 0 deletions src/notation/internal/notationnoteinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,20 @@ void NotationNoteInput::setCurrentVoice(voice_idx_t voiceIndex)
}

mu::engraving::InputState& inputState = score()->inputState();

// TODO: Inserting notes to a new voice in the middle of a tuplet is not yet supported. In this case
// we'll move the input to the start of the tuplet...
if (const Segment* prevSeg = inputState.segment()) {
const ChordRest* prevCr = prevSeg->cr(inputState.track());
//! NOTE: if there's an existing ChordRest at the new voiceIndex, we don't need to move the cursor
if (prevCr && prevCr->topTuplet() && !prevSeg->cr(voiceIndex)) {
Segment* newSeg = score()->tick2segment(prevCr->topTuplet()->tick());
if (newSeg) {
inputState.setSegment(newSeg);
}
}
}

inputState.setVoice(voiceIndex);
notifyAboutStateChanged();
}
Expand Down