//============================================================================= // MuseScore // Music Composition & Notation // // Copyright (C) 2009-2011 Werner Schweer // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 // as published by the Free Software Foundation and appearing in // the file LICENCE.GPL //============================================================================= #include "excerpt.h" #include "score.h" #include "part.h" #include "xml.h" #include "staff.h" #include "box.h" #include "textframe.h" #include "style.h" #include "page.h" #include "text.h" #include "slur.h" #include "tie.h" #include "sig.h" #include "tempo.h" #include "measure.h" #include "rest.h" #include "stafftype.h" #include "tuplet.h" #include "chord.h" #include "note.h" #include "lyrics.h" #include "segment.h" #include "tupletmap.h" #include "tiemap.h" #include "layoutbreak.h" #include "harmony.h" #include "beam.h" #include "utils.h" #include "tremolo.h" #include "barline.h" #include "undo.h" #include "bracketItem.h" namespace Ms { //--------------------------------------------------------- // Excerpt //--------------------------------------------------------- Excerpt::Excerpt(const Excerpt& ex, bool copyPartScore) : QObject(), _oscore(ex._oscore), _title(ex._title), _parts(ex._parts), _tracks(ex._tracks) { _partScore = (copyPartScore && ex._partScore) ? ex._partScore->clone() : nullptr; } //--------------------------------------------------------- // ~Excerpt //--------------------------------------------------------- Excerpt::~Excerpt() { delete _partScore; } //--------------------------------------------------------- // read //--------------------------------------------------------- void Excerpt::read(XmlReader& e) { const QList& pl = _oscore->parts(); QString name; while (e.readNextStartElement()) { const QStringRef& tag = e.name(); if (tag == "name") name = e.readElementText(); else if (tag == "title") _title = e.readElementText().trimmed(); else if (tag == "part") { int partIdx = e.readInt(); if (partIdx < 0 || partIdx >= pl.size()) qDebug("Excerpt::read: bad part index"); else _parts.append(pl.at(partIdx)); } } if (_title.isEmpty()) _title = name.trimmed(); } //--------------------------------------------------------- // operator!= //--------------------------------------------------------- bool Excerpt::operator!=(const Excerpt& e) const { if (e._oscore != _oscore) return true; if (e._title != _title) return true; if (e._parts != _parts) return true; if (e._tracks != _tracks) return true; return false; } //--------------------------------------------------------- // operator== //--------------------------------------------------------- bool Excerpt::operator==(const Excerpt& e) const { if (e._oscore != _oscore) return false; if (e._title != _title) return false; if (e._parts != _parts) return false; if (e._tracks != _tracks) return false; return true; } //--------------------------------------------------------- // createExcerpt //--------------------------------------------------------- void Excerpt::createExcerpt(Excerpt* excerpt) { MasterScore* oscore = excerpt->oscore(); Score* score = excerpt->partScore(); QList& parts = excerpt->parts(); QList srcStaves; // clone layer: for (int i = 0; i < 32; ++i) { score->layerTags()[i] = oscore->layerTags()[i]; score->layerTagComments()[i] = oscore->layerTagComments()[i]; } score->setCurrentLayer(oscore->currentLayer()); score->layer().clear(); foreach (const Layer& l, oscore->layer()) score->layer().append(l); score->setPageNumberOffset(oscore->pageNumberOffset()); // Set instruments and create linked staffs for (const Part* part : parts) { Part* p = new Part(score); p->setInstrument(*part->instrument()); p->setPartName(part->partName()); for (Staff* staff : *part->staves()) { Staff* s = new Staff(score); s->setPart(p); // s->setStaffType(0, *staff->staffType(0)); // TODO s->init(staff); s->setDefaultClefType(staff->defaultClefType()); // the order of staff - s matters as staff should be the first entry in the // created link list to make primaryStaff() work // TODO: change implementation, maybe create an explicit "primary" flag score->undo(new Link(s, staff)); p->staves()->append(s); score->staves().append(s); srcStaves.append(staff->idx()); } score->appendPart(p); } // Fill tracklist (map all tracks of a stave) if (excerpt->tracks().isEmpty()) { QMultiMap tracks; for (Staff* s : score->staves()) { const LinkedElements* ls = s->links(); if (ls == 0) continue; for (auto le : *ls) { Staff* ps = toStaff(le); if (ps->primaryStaff()) { for (int i = 0; i < VOICES; i++) { tracks.insert(ps->idx() * VOICES + i % VOICES, s->idx() * VOICES + i % VOICES); } break; } } } excerpt->setTracks(tracks); } cloneStaves(oscore, score, srcStaves, excerpt->tracks()); // create excerpt title and title frame for all scores if not already there MeasureBase* measure = oscore->first(); if (!measure || !measure->isVBox()) { qDebug("original score has no header frame"); oscore->insertMeasure(ElementType::VBOX, measure); measure = oscore->first(); } VBox* titleFrameScore = toVBox(measure); measure = score->first(); Q_ASSERT(measure->isVBox()); VBox* titleFramePart = toVBox(measure); titleFramePart->copyValues(titleFrameScore); QString partLabel = excerpt->title(); // parts.front()->longName(); if (!partLabel.isEmpty()) { Text* txt = new Text(score, Tid::INSTRUMENT_EXCERPT); txt->setPlainText(partLabel); measure->add(txt); score->setMetaTag("partName", partLabel); } // initial layout of score score->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO); score->doLayout(); // handle transposing instruments if (oscore->styleB(Sid::concertPitch) != score->styleB(Sid::concertPitch)) { for (const Staff* staff : score->staves()) { if (staff->staffType(Fraction(0,1))->group() == StaffGroup::PERCUSSION) continue; // if this staff has no transposition, and no instrument changes, we can skip it Interval interval = staff->part()->instrument()->transpose(); if (interval.isZero() && staff->part()->instruments()->size() == 1) continue; bool flip = false; if (oscore->styleB(Sid::concertPitch)) { interval.flip(); // flip the transposition for the original instrument flip = true; // transposeKeys() will flip transposition for each instrument change } int staffIdx = staff->idx(); int startTrack = staffIdx * VOICES; int endTrack = startTrack + VOICES; Fraction endTick = Fraction(0,1); if (score->lastSegment()) endTick = score->lastSegment()->tick(); score->transposeKeys(staffIdx, staffIdx+1, Fraction(0,1), endTick, interval, true, flip); for (auto segment = score->firstSegmentMM(SegmentType::ChordRest); segment; segment = segment->next1MM(SegmentType::ChordRest)) { interval = staff->part()->instrument(segment->tick())->transpose(); if (interval.isZero()) continue; if (oscore->styleB(Sid::concertPitch)) interval.flip(); for (auto e : segment->annotations()) { if (!e->isHarmony() || (e->track() < startTrack) || (e->track() >= endTrack)) continue; Harmony* h = toHarmony(e); int rootTpc = Ms::transposeTpc(h->rootTpc(), interval, true); int baseTpc = Ms::transposeTpc(h->baseTpc(), interval, true); // mmrests are on by default in part // if this harmony is attached to an mmrest, // be sure to transpose harmony in underlying measure as well for (ScoreElement* se : h->linkList()) { Harmony* hh = static_cast(se); // skip links to other staves (including in other scores) if (hh->staff() != h->staff()) continue; score->undoTransposeHarmony(hh, rootTpc, baseTpc); } } } } } // update style values if spatium different for part if (oscore->spatium() != score->spatium()) { //score->spatiumChanged(oscore->spatium(), score->spatium()); score->styleChanged(); } // second layout of score score->setPlaylistDirty(); oscore->rebuildMidiMapping(); oscore->updateChannel(); score->setLayoutAll(); score->doLayout(); } //--------------------------------------------------------- // deleteExcerpt //--------------------------------------------------------- void MasterScore::deleteExcerpt(Excerpt* excerpt) { Q_ASSERT(excerpt->oscore() == this); Score* partScore = excerpt->partScore(); if (!partScore) { qDebug("deleteExcerpt: no partScore"); return; } // unlink the staves in the excerpt for (Staff* st : partScore->staves()) { bool hasLinksInMaster = false; if (st->links()) { for (auto le : *st->links()) { if (le->score() == this) { hasLinksInMaster = true; break; } } } if (hasLinksInMaster) { int staffIdx = st->idx(); // unlink the spanners for (auto i = partScore->spanner().begin(); i != partScore->spanner().cend(); ++i) { Spanner* sp = i->second; if (sp->staffIdx() == staffIdx) sp->undoUnlink(); } int sTrack = staffIdx * VOICES; int eTrack = sTrack + VOICES; // unlink elements and annotation for (Segment* s = partScore->firstSegmentMM(SegmentType::All); s; s = s->next1MM()) { for (int track = eTrack - 1; track >= sTrack; --track) { Element* el = s->element(track); if (el) el->undoUnlink(); } for (Element* e : s->annotations()) { if (e->staffIdx() == staffIdx) e->undoUnlink(); } } // unlink the staff undo(new Unlink(st)); } } undo(new RemoveExcerpt(excerpt)); } //--------------------------------------------------------- // cloneSpanner //--------------------------------------------------------- static void cloneSpanner(Spanner* s, Score* score, int dstTrack, int dstTrack2) { // don’t clone voltas for track != 0 if (s->type() == ElementType::VOLTA && s->track() != 0) return; Spanner* ns = toSpanner(s->linkedClone()); ns->setScore(score); ns->setParent(0); ns->setTrack(dstTrack); ns->setTrack2(dstTrack2); if (ns->type() == ElementType::SLUR) { // set start/end element for slur ChordRest* cr1 = s->startCR(); ChordRest* cr2 = s->endCR(); ns->setStartElement(0); ns->setEndElement(0); if (cr1 && cr1->links()) { for (ScoreElement* e : *cr1->links()) { ChordRest* cr = toChordRest(e); if (cr == cr1) continue; if ((cr->score() == score) && (cr->tick() == ns->tick()) && cr->track() == dstTrack) { ns->setStartElement(cr); break; } } } if (cr2 && cr2->links()) { for (ScoreElement* e : *cr2->links()) { ChordRest* cr = toChordRest(e); if (cr == cr2) continue; if ((cr->score() == score) && (cr->tick() == ns->tick2()) && cr->track() == dstTrack2) { ns->setEndElement(cr); break; } } } if (!ns->startElement()) qDebug("clone Slur: no start element"); if (!ns->endElement()) qDebug("clone Slur: no end element"); } score->undo(new AddElement(ns)); } //--------------------------------------------------------- // cloneTuplets //--------------------------------------------------------- static void cloneTuplets(ChordRest* ocr, ChordRest* ncr, Tuplet* ot, TupletMap& tupletMap, Measure* m, int track) { ot->setTrack(ocr->track()); Tuplet* nt = tupletMap.findNew(ot); if (nt == 0) { nt = toTuplet(ot->linkedClone()); nt->setTrack(track); nt->setParent(m); nt->setScore(ncr->score()); tupletMap.add(ot, nt); Tuplet* nt1 = nt; while (ot->tuplet()) { Tuplet* nt2 = tupletMap.findNew(ot->tuplet()); if (nt2 == 0) { nt2 = toTuplet(ot->tuplet()->linkedClone()); nt2->setTrack(track); nt2->setParent(m); nt2->setScore(ncr->score()); tupletMap.add(ot->tuplet(), nt2); } nt2->add(nt1); nt1->setTuplet(nt2); ot = ot->tuplet(); nt1 = nt2; } } nt->add(ncr); ncr->setTuplet(nt); } //--------------------------------------------------------- // cloneStaves //--------------------------------------------------------- void Excerpt::cloneStaves(Score* oscore, Score* score, const QList& map, QMultiMap& trackList) { TieMap tieMap; MeasureBaseList* nmbl = score->measures(); for (MeasureBase* mb = oscore->measures()->first(); mb; mb = mb->next()) { MeasureBase* nmb = 0; if (mb->isHBox()) nmb = new HBox(score); else if (mb->isVBox()) nmb = new VBox(score); else if (mb->isTBox()) { nmb = new TBox(score); Text* text = toTBox(mb)->text(); Element* ne = text->linkedClone(); ne->setScore(score); nmb->add(ne); } else if (mb->isMeasure()) { Measure* m = toMeasure(mb); Measure* nm = new Measure(score); nmb = nm; nm->setTick(m->tick()); nm->setTicks(m->ticks()); nm->setTimesig(m->timesig()); nm->setRepeatCount(m->repeatCount()); nm->setRepeatStart(m->repeatStart()); nm->setRepeatEnd(m->repeatEnd()); nm->setRepeatJump(m->repeatJump()); nm->setIrregular(m->irregular()); nm->setNo(m->no()); nm->setNoOffset(m->noOffset()); nm->setBreakMultiMeasureRest(m->breakMultiMeasureRest()); //TODO nm->setEndBarLineType( // m->endBarLineType(), // m->endBarLineGenerated(), // m->endBarLineVisible(), // m->endBarLineColor()); // Fraction ts = nm->len(); int tracks = oscore->nstaves() * VOICES; for (int srcTrack = 0; srcTrack < tracks; ++srcTrack) { TupletMap tupletMap; // tuplets cannot cross measure boundaries int strack = trackList.value(srcTrack, -1); Tremolo* tremolo = 0; for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) { Segment* ns = nullptr; //create segment later, on demand for (Element* e : oseg->annotations()) { if (e->generated()) continue; if ((e->track() == srcTrack && strack != -1) || (e->systemFlag() && srcTrack == 0)) { Element* ne = e->linkedClone(); // reset offset as most likely it will not fit PropertyFlags f = ne->propertyFlags(Pid::OFFSET); if (f == PropertyFlags::UNSTYLED) { ne->setPropertyFlags(Pid::OFFSET, PropertyFlags::STYLED); ne->resetProperty(Pid::OFFSET); } ne->setTrack(strack == -1 ? 0 : strack); ne->setScore(score); if (!ns) ns = nm->getSegment(oseg->segmentType(), oseg->tick()); ns->add(ne); // for chord symbols, // re-render with new style settings if (ne->isHarmony()) { Harmony* h = toHarmony(ne); h->render(); } } } //If track is not mapped skip the following if (trackList.value(srcTrack, -1) == -1) continue; //There are probably more destination tracks for the same source QList t = trackList.values(srcTrack); for (int track : t) { //Clone KeySig TimeSig and Clefs if voice 1 of source staff is not mapped to a track Element* oef = oseg->element(srcTrack & ~3); if (oef && (oef->isTimeSig() || oef->isKeySig()) && oef->tick().isZero() && !(trackList.size() == (score->excerpt()->parts().size() * VOICES))) { Element* ne = oef->linkedClone(); ne->setTrack(track & ~3); ne->setScore(score); ns = nm->getSegment(oseg->segmentType(), oseg->tick()); ns->add(ne); } Element* oe = oseg->element(srcTrack); int adjustedBarlineSpan = 0; if (srcTrack % VOICES == 0 && oseg->segmentType() == SegmentType::BarLine) { // mid-measure barline segment // may need to clone barline from a previous staff and/or adjust span int oIdx = srcTrack / VOICES; if (!oe) { // no barline on this staff in original score, // but check previous staves for (int i = oIdx - 1; i >= 0; --i) { oe = oseg->element(i * VOICES); if (oe) break; } } if (oe) { // barline found, now check span BarLine* bl = toBarLine(oe); int oSpan1 = bl->staff()->idx(); int oSpan2 = oSpan1 + bl->spanStaff(); if (oSpan1 <= oIdx && oIdx < oSpan2) { // this staff is within span // calculate adjusted span for excerpt int oSpan = oSpan2 - oIdx; adjustedBarlineSpan = qMin(oSpan, score->nstaves()); } else { // this staff is not within span oe = nullptr; } } } if (oe && !oe->generated()) { Element* ne; ne = oe->linkedClone(); ne->setTrack(track); if (!(ne->track() % VOICES) && ne->isRest()) toRest(ne)->setGap(false); ne->setScore(score); if (oe->type() == ElementType::BAR_LINE && adjustedBarlineSpan) { BarLine* nbl = toBarLine(ne); nbl->setSpanStaff(adjustedBarlineSpan); } else if (oe->isChordRest()) { ChordRest* ocr = toChordRest(oe); ChordRest* ncr = toChordRest(ne); if (ocr->beam() && !ocr->beam()->empty() && ocr->beam()->elements().front() == ocr) { Beam* nb = ocr->beam()->clone(); nb->clear(); nb->setTrack(track); nb->setScore(score); nb->add(ncr); ncr->setBeam(nb); } Tuplet* ot = ocr->tuplet(); if (ot) cloneTuplets(ocr, ncr, ot, tupletMap, nm, track); if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); if (on->tieFor()) { Tie* tie = toTie(on->tieFor()->linkedClone()); tie->setScore(score); nn->setTieFor(tie); tie->setStartNote(nn); tie->setTrack(nn->track()); tieMap.add(on->tieFor(), tie); } if (on->tieBack()) { Tie* tie = tieMap.findNew(on->tieBack()); if (tie) { nn->setTieBack(tie); tie->setEndNote(nn); } else { qDebug("cloneStaves: cannot find tie"); } } // add back spanners (going back from end to start spanner element // makes sure the 'other' spanner anchor element is already set up) // 'on' is the old spanner end note and 'nn' is the new spanner end note for (Spanner* oldSp : on->spannerBack()) { if (oldSp->startElement() && oldSp->endElement() && oldSp->startElement()->track() > oldSp->endElement()->track()) continue; Note* newStart = Spanner::startElementFromSpanner(oldSp, nn); if (newStart != nullptr) { Spanner* newSp = toSpanner(oldSp->linkedClone()); newSp->setNoteSpan(newStart, nn); score->addElement(newSp); } else { qDebug("cloneStaves: cannot find spanner start note"); } } for (Spanner* oldSp : on->spannerFor()) { if (oldSp->startElement() && oldSp->endElement() && oldSp->startElement()->track() <= oldSp->endElement()->track()) continue; Note* newEnd = Spanner::endElementFromSpanner(oldSp, nn); if (newEnd != nullptr) { Spanner* newSp = toSpanner(oldSp->linkedClone()); newSp->setNoteSpan(nn, newEnd); score->addElement(newSp); } else { qDebug("cloneStaves: cannot find spanner end note"); } } } // two note tremolo if (och->tremolo() && och->tremolo()->twoNotes()) { if (och == och->tremolo()->chord1()) { if (tremolo) qDebug("unconnected two note tremolo"); tremolo = toTremolo(och->tremolo()->linkedClone()); tremolo->setScore(nch->score()); tremolo->setParent(nch); tremolo->setTrack(nch->track()); tremolo->setChords(nch, 0); nch->setTremolo(tremolo); } else if (och == och->tremolo()->chord2()) { if (!tremolo) qDebug("first note for two note tremolo missing"); else { tremolo->setChords(tremolo->chord1(), nch); nch->setTremolo(tremolo); } } else qDebug("inconsistent two note tremolo"); } } } if (!ns) ns = nm->getSegment(oseg->segmentType(), oseg->tick()); ns->add(ne); } Segment* tst = nm->segments().firstCRSegment(); if (srcTrack % VOICES && !(track % VOICES) && (!tst || (!tst->element(track)))) { Rest* rest = new Rest(score); rest->setTicks(nm->ticks()); rest->setDurationType(nm->ticks()); rest->setTrack(track); Segment* segment = nm->getSegment(SegmentType::ChordRest, nm->tick()); segment->add(rest); } } } } } nmb->linkTo(mb); for (Element* e : mb->el()) { if (e->isLayoutBreak()) { LayoutBreak::Type st = toLayoutBreak(e)->layoutBreakType(); if (st == LayoutBreak::Type::PAGE || st == LayoutBreak::Type::LINE) continue; } int track = -1; if (e->track() != -1) { // try to map track track = trackList.value(e->track(), -1); if (track == -1) { // even if track not in excerpt, we need to clone system elements if (e->systemFlag()) track = 0; else continue; } } Element* ne; // link text - title, subtitle, also repeats (eg, coda/segno) // measure numbers are not stored in this list, but they should not be cloned anyhow // layout breaks other than section were skipped above, // but section breaks do need to be cloned & linked // other measure-attached elements (?) are cloned but not linked if (e->isText() && toText(e)->tid() == Tid::INSTRUMENT_EXCERPT) { // skip part name in score continue; } else if (e->isTextBase() || e->isLayoutBreak()) { ne = e->clone(); ne->setAutoplace(true); ne->linkTo(e); } else ne = e->clone(); ne->setScore(score); ne->setTrack(track); nmb->add(ne); } nmbl->add(nmb); } int n = map.size(); for (int dstStaffIdx = 0; dstStaffIdx < n; ++dstStaffIdx) { Staff* srcStaff = oscore->staff(map[dstStaffIdx]); Staff* dstStaff = score->staff(dstStaffIdx); if (srcStaff->primaryStaff()) { int span = srcStaff->barLineSpan(); int sIdx = srcStaff->idx(); if (dstStaffIdx == 0 && span == 0) { // this is first staff of new score, // but it was somewhere within a barline span in the old score // so, find beginning of span for (int i = 0; i <= sIdx; ++i) { span = oscore->staff(i)->barLineSpan(); if (i + span > sIdx) { sIdx = i; break; } } } int eIdx = sIdx + span; for (int staffIdx = sIdx; staffIdx < eIdx; ++staffIdx) { if (!map.contains(staffIdx)) --span; } if (dstStaffIdx + span > n) span = n - dstStaffIdx - 1; dstStaff->setBarLineSpan(span); int idx = 0; for (BracketItem* bi : srcStaff->brackets()) { dstStaff->setBracketType(idx, bi->bracketType()); dstStaff->setBracketSpan(idx, bi->bracketSpan()); } } } for (auto i : oscore->spanner()) { Spanner* s = i.second; int dstTrack = -1; int dstTrack2 = -1; if (s->type() == ElementType::VOLTA) { //always export voltas to first staff in part dstTrack = 0; dstTrack2 = 0; cloneSpanner(s, score, dstTrack, dstTrack2); } else if (s->isHairpin()) { //always export these spanners to first voice of the destination staff QList track1; for (int ii = s->track(); ii < s->track() + VOICES; ii++) { track1 += trackList.values(ii); } for (int track : track1) { if (!(track % VOICES)) cloneSpanner(s, score, track, track); } } else { if (trackList.value(s->track(), -1) == -1 || trackList.value(s->track2(), -1) == -1) continue; QList track1 = trackList.values(s->track()); QList track2 = trackList.values(s->track2()); if (track1.length() != track2.length()) continue; //export other spanner if staffidx matches for (int ii = 0; ii < track1.length(); ii++) { dstTrack = track1.at(ii); dstTrack2 = track2.at(ii); cloneSpanner(s, score, dstTrack, dstTrack2); } } } } //--------------------------------------------------------- // cloneStaff // staves are in same score //--------------------------------------------------------- void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff) { Score* score = srcStaff->score(); TieMap tieMap; score->undo(new Link(dstStaff, srcStaff)); int srcStaffIdx = srcStaff->idx(); int dstStaffIdx = dstStaff->idx(); for (Measure* m = score->firstMeasure(); m; m = m->nextMeasure()) { int sTrack = srcStaffIdx * VOICES; int eTrack = sTrack + VOICES; for (int srcTrack = sTrack; srcTrack < eTrack; ++srcTrack) { TupletMap tupletMap; // tuplets cannot cross measure boundaries int dstTrack = dstStaffIdx * VOICES + (srcTrack - sTrack); Tremolo* tremolo = 0; for (Segment* seg = m->first(); seg; seg = seg->next()) { Element* oe = seg->element(srcTrack); if (oe == 0 || oe->generated()) continue; if (oe->isTimeSig()) continue; Element* ne = 0; if (oe->isClef()) { // only clone clef if it matches staff group and does not exists yet Clef* clef = toClef(oe); Fraction tick = seg->tick(); if (ClefInfo::staffGroup(clef->concertClef()) == dstStaff->constStaffType(Fraction(0,1))->group() && dstStaff->clefType(tick) != clef->clefTypeList()) { ne = oe->clone(); } } else ne = oe->linkedClone(); if (ne) { ne->setTrack(dstTrack); ne->setParent(seg); ne->setScore(score); if (ne->isChordRest()) { ChordRest* ncr = toChordRest(ne); if (ncr->tuplet()) { ncr->setTuplet(0); //TODO nested tuplets } } score->undoAddElement(ne); } if (oe->isChordRest()) { ChordRest* ocr = toChordRest(oe); ChordRest* ncr = toChordRest(ne); Tuplet* ot = ocr->tuplet(); if (ot) cloneTuplets(ocr, ncr, ot, tupletMap, m, dstTrack); // remove lyrics from chord // since only one set of lyrics is used with linked staves foreach (Lyrics* l, ncr->lyrics()) { if (l) l->unlink(); } qDeleteAll(ncr->lyrics()); ncr->lyrics().clear(); for (Element* e : seg->annotations()) { if (e->generated() || e->systemFlag()) continue; if (e->track() != srcTrack) continue; switch (e->type()) { // exclude certain element types // this should be same list excluded in Score::undoAddElement() case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: case ElementType::FRET_DIAGRAM: case ElementType::HARMONY: case ElementType::FIGURED_BASS: case ElementType::DYNAMIC: case ElementType::LYRICS: // not normally segment-attached continue; default: Element* ne1 = e->clone(); ne1->setTrack(dstTrack); ne1->setParent(seg); ne1->setScore(score); score->undoAddElement(ne1); } } if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); if (on->tieFor()) { Tie* tie = toTie(on->tieFor()->linkedClone()); tie->setScore(score); nn->setTieFor(tie); tie->setStartNote(nn); tie->setTrack(nn->track()); tieMap.add(on->tieFor(), tie); } if (on->tieBack()) { Tie* tie = tieMap.findNew(on->tieBack()); if (tie) { nn->setTieBack(tie); tie->setEndNote(nn); } else { qDebug("cloneStave: cannot find tie"); } } // add back spanners (going back from end to start spanner element // makes sure the 'other' spanner anchor element is already set up) // 'on' is the old spanner end note and 'nn' is the new spanner end note for (Spanner* oldSp : on->spannerBack()) { Note* newStart = Spanner::startElementFromSpanner(oldSp, nn); if (newStart != nullptr) { Spanner* newSp = toSpanner(oldSp->linkedClone()); newSp->setNoteSpan(newStart, nn); score->addElement(newSp); } else { qDebug("cloneStave: cannot find spanner start note"); } } } // two note tremolo if (och->tremolo() && och->tremolo()->twoNotes()) { if (och == och->tremolo()->chord1()) { if (tremolo) qDebug("unconnected two note tremolo"); tremolo = toTremolo(och->tremolo()->linkedClone()); tremolo->setScore(nch->score()); tremolo->setParent(nch); tremolo->setTrack(nch->track()); tremolo->setChords(nch, 0); nch->setTremolo(tremolo); } else if (och == och->tremolo()->chord2()) { if (!tremolo) qDebug("first note for two note tremolo missing"); else { tremolo->setChords(tremolo->chord1(), nch); nch->setTremolo(tremolo); } } else qDebug("inconsistent two note tremolo"); } } } } } } for (auto i : score->spanner()) { Spanner* s = i.second; int staffIdx = s->staffIdx(); int dstTrack = -1; int dstTrack2 = -1; if (s->type() != ElementType::VOLTA) { //export other spanner if staffidx matches if (srcStaffIdx == staffIdx) { dstTrack = dstStaffIdx * VOICES + s->voice(); dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES); } } if (dstTrack == -1) continue; cloneSpanner(s, score, dstTrack, dstTrack2); } } //--------------------------------------------------------- // cloneStaff2 // staves are potentially in different scores //--------------------------------------------------------- void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& stick, const Fraction& etick) { Score* oscore = srcStaff->score(); Score* score = dstStaff->score(); Excerpt* oex = oscore->excerpt(); Excerpt* ex = score->excerpt(); QMultiMap otracks, tracks; if (oex) otracks = oex->tracks(); if (ex) tracks = ex->tracks(); Measure* m1 = oscore->tick2measure(stick); Measure* m2 = oscore->tick2measure(etick); if (m2->tick() < etick) // end of score m2 = 0; TieMap tieMap; int srcStaffIdx = srcStaff->idx(); int dstStaffIdx = dstStaff->idx(); int sTrack = srcStaffIdx * VOICES; int eTrack = sTrack + VOICES; QMap map; for (int i = sTrack; i < eTrack; i++) { if (!oex && !ex) map.insert(i, dstStaffIdx * VOICES + i % VOICES); else if (oex && !ex) { if (otracks.key(i, -1) != -1) map.insert(i, otracks.key(i)); } else if (!oex && ex) { for (int j : tracks.values(i)) { if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) { map.insert(i, j); break; } } } else if (oex && ex) { if (otracks.key(i, -1) != -1) { for (int j : tracks.values(otracks.key(i))) { if (dstStaffIdx * VOICES <= j && j < (dstStaffIdx + 1) * VOICES) { map.insert(i, j); break; } } } } } for (Measure* m = m1; m && (m != m2); m = m->nextMeasure()) { Measure* nm = score->tick2measure(m->tick()); for (int srcTrack : map.keys()) { TupletMap tupletMap; // tuplets cannot cross measure boundaries int dstTrack = map.value(srcTrack); for (Segment* oseg = m->first(); oseg; oseg = oseg->next()) { Element* oe = oseg->element(srcTrack); if (oe == 0 || oe->generated()) continue; if (oe->type() == ElementType::TIMESIG) continue; Segment* ns = nm->getSegment(oseg->segmentType(), oseg->tick()); Element* ne = oe->linkedClone(); ne->setTrack(dstTrack); ne->setParent(ns); ne->setScore(score); score->undoAddElement(ne); if (oe->isChordRest()) { ChordRest* ocr = toChordRest(oe); ChordRest* ncr = toChordRest(ne); Tuplet* ot = ocr->tuplet(); if (ot) { Tuplet* nt = tupletMap.findNew(ot); if (nt == 0) { // nt = new Tuplet(*ot); nt = toTuplet(ot->linkedClone()); nt->clear(); nt->setTrack(dstTrack); nt->setParent(m); tupletMap.add(ot, nt); } ncr->setTuplet(nt); nt->add(ncr); } for (Element* e : oseg->annotations()) { if (e->generated() || e->systemFlag()) continue; if (e->track() != srcTrack) continue; switch (e->type()) { // exclude certain element types // this should be same list excluded in Score::undoAddElement() case ElementType::STAFF_TEXT: case ElementType::SYSTEM_TEXT: case ElementType::FRET_DIAGRAM: case ElementType::HARMONY: case ElementType::FIGURED_BASS: case ElementType::DYNAMIC: case ElementType::LYRICS: // not normally segment-attached continue; default: Element* ne1 = e->clone(); ne1->setTrack(dstTrack); ne1->setParent(ns); ne1->setScore(score); score->undoAddElement(ne1); } } if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); if (on->tieFor()) { Tie* tie = toTie(on->tieFor()->linkedClone()); tie->setScore(score); nn->setTieFor(tie); tie->setStartNote(nn); tie->setTrack(nn->track()); tieMap.add(on->tieFor(), tie); } if (on->tieBack()) { Tie* tie = tieMap.findNew(on->tieBack()); if (tie) { nn->setTieBack(tie); tie->setEndNote(nn); } else { qDebug("cloneStave: cannot find tie"); } } } } } } } } for (auto i : oscore->spanner()) { Spanner* s = i.second; if (!(s->tick() >= stick && s->tick2() < etick)) continue; int staffIdx = s->staffIdx(); int dstTrack = -1; int dstTrack2 = -1; if (s->type() != ElementType::VOLTA) { //export other spanner if staffidx matches if (srcStaffIdx == staffIdx) { dstTrack = dstStaffIdx * VOICES + s->voice(); dstTrack2 = dstStaffIdx * VOICES + (s->track2() % VOICES); } } if (dstTrack == -1) continue; cloneSpanner(s, score, dstTrack, dstTrack2); } } //--------------------------------------------------------- // createAllExcerpt //--------------------------------------------------------- QList Excerpt::createAllExcerpt(MasterScore *score) { QList all; for (Part* part : score->parts()) { if (part->show()) { Excerpt* e = new Excerpt(score); e->parts().append(part); for (int i = part->startTrack(), j = 0; i < part->endTrack(); i++, j++) { e->tracks().insert(i, j); } QString name = createName(part->partName(), all); e->setTitle(name); all.append(e); } } return all; } //--------------------------------------------------------- // createName //--------------------------------------------------------- QString Excerpt::createName(const QString& partName, QList& excerptList) { QString name = partName.simplified(); int count = 0; // no of occurrences of partName for (Excerpt* e : excerptList) { // if already exists, change to if (e->title().compare(name) == 0) e->setTitle(e->title() + " 1"); QRegExp rx("^(.+)\\s\\d+$"); if (rx.indexIn(e->title()) > -1 && rx.cap(1) == name) count++; } if (count > 0) name += QString(" %1").arg(count + 1); return name; } //--------------------------------------------------------- // setPartScore //--------------------------------------------------------- void Excerpt::setPartScore(Score* s) { _partScore = s; s->setExcerpt(this); } }