void TscoreMeasure::checkBarLine() { if (duration() && m_free == 0) { // update bar line if (m_barLine->parentItem() != lastNote()->parentItem()) { m_barLine->setParentItem(lastNote()); QPointF barLinePos = lastNote()->mapFromParent(QPointF(lastNote()->rightX() - 0.2, 0.0)); m_barLine->setLine(barLinePos.x(), lastNote()->staff()->upperLinePos(), barLinePos.x(), lastNote()->staff()->upperLinePos() + 8.0); m_barLine->show(); } } else if (m_free > 0) { m_barLine->hide(); } else // TODO: It should never occur - delete it when tested qDebug() << debug() << "ARE YOU MAD? Measure has more notes than the meter allows!"; }
void TscoreMeasure::fill() { QList<TscoreNote*> notesToShift; int remainDur = m_staff->shiftFromMeasure(id() + 1, m_free, notesToShift); qDebug() << debug() << "fill, remain" << remainDur << "to shift:" << notesToShift.count(); for (int i = 0; i < notesToShift.size(); ++i) { m_notes.append(notesToShift[i]); connect(notesToShift[i], &TscoreNote::noteGoingToChange, this, &TscoreMeasure::noteChangedSlot); } if (remainDur) { // next measure has a part of a new note qDebug() << debug() << remainDur << "remained in the next measure"; auto firstInNext = m_staff->measures()[id() + 1]->firstNote()->note(); Tnote newNote(*firstInNext, Trhythm(remainDur, firstInNext->isRest())); auto inserted = m_staff->insertNote(newNote, lastNoteId() + 1, lastNote()->isReadOnly()); // copyRhythmParams(inserted, firstInNext->rtm); fixStemDirection(inserted); insertNote(inserted->index() - firstNoteId(), inserted); m_staff->updateNotesPos(); if (!inserted->note()->isRest()) // add a tie inserted->tieWithNext(); } else { updateRhythmicGroups(); resolveBeaming(0); checkBarLine(); } content(this); }
void TscoreMeasure::addNewNote(Tnote& newNote) { // prepare tie, if any to restore it after new note will be created auto lastN = lastNote(); auto lastTie = lastN->note()->rtm.tie(); // backup tie state to revert it after new note will be added // create a new note on the staff auto inserted = m_staff->insertNote(lastN->index() + 1, newNote); fixStemDirection(lastNote()); qDebug() << debug() << "creating new note after" << lastN->index() << lastN->note()->toText() << newNote.rtm.string(); if (!newNote.isRest()) { lastN->tieWithNext(); if (lastTie == Trhythm::e_tieCont || lastTie == Trhythm::e_tieStart) inserted->tieWithNext(); } }
/** * iterate through notes in backward order (right to left), take note by note to release required duration * create a list from taken notes to send it to the next measure * split the latest note (the most right one in the measure) if necessary * half of the duration remains in current measure at the end tied with * a new note that has to be created and push to the beginning of the next measure */ int TscoreMeasure::releaseAtEnd(int dur, QList<TscoreNote*>& notesToOut, Tnote& newNote, int endNote) { int noteNr = m_notes.count() - 1; while (noteNr >= endNote && dur > 0) { auto lastN = lastNote(); int lastDur = lastN->note()->duration(); if (lastDur > dur) { // last note is longer than required space - split it then create and move the rest of its duration to the next measure if (Trhythm(lastDur - dur).rhythm() == Trhythm::e_none) { // subtracting can't be solved by single note split(lastN); // then split on two notes continue; // and call while loop again } Trhythm oldRhythm(lastN->note()->rtm); // preserve tie and stem direction lastN->setRhythm(Trhythm(lastDur - dur, lastN->note()->isRest())); copyRhythmParams(lastN, oldRhythm); // lastN->note()->rtm.setStemDown(oldRhythm.stemDown()); // if (oldRhythm.tie()) // lastN->note()->rtm.setTie(Trhythm::e_tieCont); newNote = Tnote(*lastN->note(), Trhythm(dur, lastN->note()->isRest())); // newNote.rtm.setStemDown(oldRhythm.stemDown()); lastDur = dur; } else { // last note is the same long or smaller than required space - so move it to the next measure notesToOut << m_notes.takeLast(); disconnect(notesToOut.last(), &TscoreNote::noteGoingToChange, this, &TscoreMeasure::noteChangedSlot); auto b = notesToOut.last()->beam(); if (b) { m_beams.removeOne(b); delete b; } } dur -= lastDur; m_free += lastDur; // note was taken out so there is more free space in the measure } return dur; }
void TscoreMeasure::setStaff(TscoreStaff* st) { m_staff = st; setParent(st); for (TscoreBeam* b : m_beams) b->changeStaff(st); if (st->firstMeasure() == this) { // this measure was the last and went at the beginning of the next staff auto first = firstNote(); if (first->note()->rtm.tie() > Trhythm::e_tieStart) { // tie continues or ends on first note auto prev = first->prevNote(); if (prev->tie()) prev->tie()->checkStaves(); else // TODO: delete if not occurs qDebug() << debug() << "first note has tie flag set but tie doesn't exists"; } } else if (st->lastMeasure() == this) { // this measure was the first and went at the end of previous staff auto last = lastNote(); if (last->tie()) last->tie()->checkStaves(); } }
//################################################################################################# //################### PROTECTED ############################################ //################################################################################################# void TscoreMeasure::noteChangedSlot(TscoreNote* sn) { Tnote newNote; // new note that has to be created when rhythm duration is split QList<TscoreNote*> notesToOut; if (sn->rhythmChanged()) { // TODO: if new duration of changed note is longer than whole measure duration - split it here and create new measure with single note // - still we don't know new pitch int prevDur = sn->rhythm()->duration(); int newDur = sn->note()->duration(); int nextMeasDur = 0; qDebug() << debug() << "rhythm changed from" << sn->rhythm()->string() << "to" << sn->note()->rtm.string() << "free" << m_free; if (m_free - (newDur - prevDur) < 0) { // There is not enough space for this note with longer duration /** 1. Try to release measure (move notes after this @p sn one to the next measure) */ int leftDur = releaseAtEnd(newDur - prevDur - m_free, notesToOut, newNote, sn->index() - firstNoteId() + 1); if (leftDur) { sn->moveNote(sn->note()->isRest() ? 0 : sn->newNotePos()); // update position of note head sn->staff()->updatePitch(sn->index()); // update Tnote according to new head position /** 2. There is still not enough space for new duration - splitting duration of this @p sn note to fill free space in this measure * and create part of that duration in the next one */ nextMeasDur = newDur - (m_free + prevDur); Trhythm oldRhythm(sn->note()->rtm); Trhythm newRtmOfChanged(m_free + prevDur); if (newRtmOfChanged.rhythm() == Trhythm::e_none) { /** 3. Unfortunately remained free space in the measure can't be filled with single rhythm value */ qDebug() << debug() << "To fill measure with remaining duration need to split note of" << m_free + prevDur; TrhythmList solvedList; Trhythm::resolve(m_free + prevDur, solvedList); if (solvedList.size() == 2) { solvedList.first().setRest(oldRhythm.isRest()); solvedList.last().setRest(oldRhythm.isRest()); sn->note()->rtm.setRhythm(solvedList.first().duration()); copyRhythmParams(sn, oldRhythm); Tnote n2(*sn->note(), solvedList.last()); // TODO: common code with @p split auto inserted = m_staff->insertNote(n2, sn->index() + 1, sn->isReadOnly()); fixStemDirection(inserted); m_notes.insert(inserted->index() - firstNoteId(), inserted); connect(inserted, &TscoreNote::noteGoingToChange, this, &TscoreMeasure::noteChangedSlot); // inserted->setGroup(sn->group()); m_staff->updateNotesPos(inserted->index()); if (!sn->note()->isRest()) restoreTie(oldRhythm.tie(), sn); } else qDebug() << debug() << "Can not resolve duration of" << m_free + prevDur; } else { sn->note()->rtm.setRhythm(newRtmOfChanged); copyRhythmParams(sn, oldRhythm); } } qDebug() << debug() << "RECALCULATED, remained" << nextMeasDur; updateRhythmicGroups(); resolveBeaming(sn->rhythmGroup()); checkBarLine(); if (nextMeasDur) { /** 4. At the beginning of the next staff, create new note of the same pitch with remaining duration. */ Trhythm nextMeasRtm(nextMeasDur); if (nextMeasRtm.rhythm() == Trhythm::e_none) { splitThenInsert(nextMeasDur, lastNoteId() + 1, *sn->note(), sn->isReadOnly()); } else { auto nextMeasNote = m_staff->insertNote(lastNoteId() + 1, Tnote(*sn->note(), nextMeasRtm), sn->isReadOnly()); fixStemDirection(nextMeasNote); } if (!sn->note()->isRest()) lastNote()->tieWithNext(); } } else if (newDur == prevDur) { if (sn->note()->isRest() != sn->rhythm()->isRest()) qDebug() << debug() << "note" << sn->index() << "changed to/from rest"; resolveBeaming(sn->rhythmGroup(), sn->rhythmGroup()); checkBarLine(); } else { // measure duration is less than meter - take notes from the next measure m_free += prevDur - newDur; qDebug() << debug() << "needs duration" << m_free; fill(); } } shiftReleased(notesToOut, newNote); }
qreal TscoreMeasure::notesWidth() { if (!isEmpty()) return lastNote()->rightX() - firstNote()->x(); else return 0.0; }
void TscoreMeasure::insertNote(int id, TscoreNote* sn) { // qDebug() << debug() << sn->note()->rtm.xmlType() << "inserting id" << id << "count" << m_notes.count() << "free" << m_free; int noteDur = sn->note()->duration(); int resolveAfter = 0; // un-resolvable duration of new note QList<TscoreNote*> notesToOut; Tnote newNote; // new note that has to be created when rhythm duration is split if (sn->note()->rhythm() != Trhythm::e_none && m_free - noteDur < 0) { // move notes to the next measure if not enough space int toRelease = releaseAtEnd(noteDur - m_free, notesToOut, newNote, id); if (toRelease > 0) { // There is still not enough space if (m_free > 0) { // measure has some free space for the new note but not for entire if (Trhythm(m_free).rhythm() == Trhythm::e_none) { Trhythm subRhythm(noteDur - m_free, sn->note()->isRest()); subRhythm.setTie(sn->note()->rtm.tie()); subRhythm.setStemDown(sn->note()->rtm.stemDown()); if (subRhythm.rhythm() == Trhythm::e_none) qDebug() << debug() << "can not shrink note to duration" << noteDur - m_free; sn->setRhythm(subRhythm); splitThenInsert(m_free, id, *sn->note(), sn->isReadOnly()); insertNote(lastNoteId() + 1, sn); if (!sn->note()->isRest()) lastNote()->tieWithNext(); return; } Trhythm oldRhythm(sn->note()->rtm); sn->setRhythm(Trhythm(m_free, sn->note()->isRest())); copyRhythmParams(sn, oldRhythm); // sn->note()->rtm.setTie(oldRhythm.tie()); // sn->note()->rtm.setStemDown(oldRhythm.stemDown()); if (newNote.rhythm() != Trhythm::e_none) // TODO: remove when tested qDebug() << "NEW NOTE is already created!!!"; Trhythm newRtm(noteDur - m_free, sn->note()->isRest()); if (newRtm.rhythm() == Trhythm::e_none) // two notes have to be added resolveAfter = noteDur - m_free; // do it after else newNote = Tnote(*sn->note(), newRtm); noteDur = m_free; } else { // measure has not at all space qDebug() << debug() << "move entire note" << sn->note()->toText(); notesToOut << sn; m_staff->shiftToMeasure(m_staff->measures().indexOf(this) + 1, notesToOut); return; } } } m_free -= noteDur; m_notes.insert(id, sn); qDebug() << debug() << "measure got a note" << sn->note()->rtm.xmlType() << "FREE" << m_free << "out" << notesToOut.count(); connect(sn, &TscoreNote::noteGoingToChange, this, &TscoreMeasure::noteChangedSlot); updateRhythmicGroups(); resolveBeaming(sn->rhythmGroup()); checkBarLine(); shiftReleased(notesToOut, newNote); if (resolveAfter) { auto lastN = lastNote(); splitThenInsert(resolveAfter, lastN->index() + 1, *lastN->note(), lastN->isReadOnly()); } content(this); }
void NoteList::reset() { while (anyNotesPressed()) deleteNote(lastNote()); }