Note* searchTieNote114(Note* note) { Note* note2 = 0; Chord* chord = note->chord(); Segment* seg = chord->segment(); Part* part = chord->part(); int strack = part->staves()->front()->idx() * VOICES; int etrack = strack + part->staves()->size() * VOICES; while ((seg = seg->next1(Segment::Type::ChordRest))) { for (int track = strack; track < etrack; ++track) { Chord* c = toChord(seg->element(track)); if (c == 0 || (!c->isChord()) || (c->track() != chord->track())) continue; int staffIdx = c->staffIdx() + c->staffMove(); if (staffIdx != chord->staffIdx() + chord->staffMove()) // cannot happen? continue; for (Note* n : c->notes()) { if (n->pitch() == note->pitch()) { if (note2 == 0 || c->track() == chord->track()) note2 = n; } } } if (note2) break; } return note2; }
void Ambitus::updateRange() { if (!segment()) return; Chord* chord; int firstTrack = track(); int lastTrack = firstTrack + VOICES-1; int pitchTop = -1000; int pitchBottom = 1000; int tpcTop = 0; // Initialized to prevent warning int tpcBottom = 0; // Initialized to prevent warning int trk; Measure* meas = segment()->measure(); Segment* segm = meas->findSegment(SegmentType::ChordRest, segment()->tick()); bool stop = meas->sectionBreak(); while (segm) { // moved to another measure? if (segm->measure() != meas) { // if section break has been found, stop here if (stop) break; // update meas and stop condition meas = segm->measure(); stop = meas->sectionBreak(); } // scan all relevant tracks of this segment for chords for (trk = firstTrack; trk <= lastTrack; trk++) { Element* e = segm->element(trk); if (!e || !e->isChord()) continue; chord = toChord(e); // update pitch range (with associated tpc's) for (Note* n : chord->notes()) { if (!n->play()) // skip notes which are not to be played continue; int pitch = n->ppitch(); if (pitch > pitchTop) { pitchTop = pitch; tpcTop = n->tpc(); } if (pitch < pitchBottom) { pitchBottom = pitch; tpcBottom = n->tpc(); } } } segm = segm->nextCR(); } if (pitchTop > -1000) { // if something has been found, update this _topPitch = pitchTop; _bottomPitch = pitchBottom; _topTpc = tpcTop; _bottomTpc = tpcBottom; } }
Note* nextChordNote(Note* note) { int track = note->track(); int fromTrack = (track / VOICES) * VOICES; int toTrack = fromTrack + VOICES; // TODO : limit to same instrument, not simply to same staff! Segment* seg = note->chord()->segment()->nextCR(track, true); while (seg) { Element* targetElement = seg->elementAt(track); // if a chord exists in the same track, return its top note if (targetElement && targetElement->isChord()) return toChord(targetElement)->upNote(); // if not, return topmost chord in track range for (int i = fromTrack ; i < toTrack; i++) { targetElement = seg->elementAt(i); if (targetElement && targetElement->isChord()) return toChord(targetElement)->upNote(); } seg = seg->nextCR(track, true); } return nullptr; }
void Tuplet::layout() { if (_elements.empty()) { qDebug("Tuplet::layout(): tuplet is empty"); return; } // is in a TAB without stems, skip any format: tuplets are not shown if (staff() && staff()->isTabStaff() && staff()->staffType()->slashStyle()) return; qreal _spatium = spatium(); if (_numberType != NumberType::NO_TEXT) { if (_number == 0) { _number = new Text(score()); _number->setTextStyleType(TextStyleType::TUPLET); _number->setTrack(track()); _number->setParent(this); _number->setVisible(visible()); } if (_numberType == NumberType::SHOW_NUMBER) _number->setXmlText(QString("%1").arg(_ratio.numerator())); else _number->setXmlText(QString("%1:%2").arg(_ratio.numerator()).arg(_ratio.denominator())); } else { if (_number) { if (_number->selected()) score()->deselect(_number); delete _number; _number = 0; } } // // find out main direction // if (_direction == Direction::AUTO) { int up = 1; foreach (const DurationElement* e, _elements) { if (e->isChord()) { const Chord* c = toChord(e); if (c->stemDirection() != Direction::AUTO) up += c->stemDirection() == Direction::UP ? 1000 : -1000; else up += c->up() ? 1 : -1; } else if (e->isTuplet()) { // TODO } } _isUp = up > 0; }
bool isTied(const Segment *seg, int strack, int voice, Ms::Tie*(Note::*tieFunc)() const) { ChordRest *cr = static_cast<ChordRest *>(seg->element(strack + voice)); if (cr && cr->isChord()) { Chord *chord = toChord(cr); const auto ¬es = chord->notes(); for (const Note *note: notes) { if ((note->*tieFunc)()) return true; } } return false; }
Dynamic* lookupDynamic(Element* e) { Dynamic* d = 0; Segment* s = 0; if (e && e->isChord()) s = toChord(e)->segment(); if (s) { for (Element* ee : s->annotations()) { if (ee->isDynamic() && ee->track() == e->track() && ee->placeBelow()) { d = toDynamic(ee); break; } } } if (d) { if (!d->autoplace()) d = 0; } return d; }
Note* searchTieNote(Note* note) { Note* note2 = 0; Chord* chord = note->chord(); Segment* seg = chord->segment(); Part* part = chord->part(); int strack = part->staves()->front()->idx() * VOICES; int etrack = strack + part->staves()->size() * VOICES; if (chord->isGraceBefore()) { // grace before // try to tie to note in parent chord chord = toChord(chord->parent()); note2 = chord->findNote(note->pitch()); if (note2) return note2; } else if (chord->isGraceAfter()) { // grace after // we will try to tie to note in next normal chord, below // meanwhile, set chord to parent chord so the endTick calculation will make sense chord = toChord(chord->parent()); } else { // normal chord // try to tie to grace note after if present QVector<Chord*> gna = chord->graceNotesAfter(); if (!gna.empty()) { Chord* gc = gna[0]; note2 = gc->findNote(note->pitch()); if (note2) return note2; } } // at this point, chord is a regular chord, not a grace chord // and we are looking for a note in the *next* chord (grace or regular) // calculate end of current note duration // but err on the safe side in case there is roundoff in tick count int endTick = chord->tick() + chord->actualTicks() - 1; while ((seg = seg->next1(Segment::Type::ChordRest))) { // skip ahead to end of current note duration as calculated above // but just in case, stop if we find element in current track if (seg->tick() < endTick && !seg->element(chord->track())) continue; for (int track = strack; track < etrack; ++track) { Element* e = seg->element(track); if (e == 0 || !e->isChord()) continue; Chord* c = toChord(e); // if there are grace notes before, try to tie to first one QVector<Chord*> gnb = c->graceNotesBefore(); if (!gnb.empty()) { Chord* gc = gnb[0]; Note* gn2 = gc->findNote(note->pitch()); if (gn2) return gn2; } int staffIdx = c->staffIdx() + c->staffMove(); if (staffIdx != chord->staffIdx() + chord->staffMove()) // cannot happen? continue; for (Note* n : c->notes()) { if (n->pitch() == note->pitch()) { if (note2 == 0 || c->track() == chord->track()) note2 = n; } } } if (note2) break; } return note2; }
void Tremolo::layout() { qreal _spatium = spatium() * mag(); qreal w2 = _spatium * score()->styleS(Sid::tremoloWidth).val() * .5; qreal lw = _spatium * score()->styleS(Sid::tremoloStrokeWidth).val(); qreal td = _spatium * score()->styleS(Sid::tremoloDistance).val(); path = QPainterPath(); qreal ty = 0.0; for (int i = 0; i < _lines; i++) { path.addRect(-w2, ty, 2.0 * w2, lw); ty += td; } // QRectF rect = path.boundingRect(); // if ((parent() == 0) && !twoNotes()) // rect.setHeight(rect.height() + _spatium); _chord1 = toChord(parent()); if (_chord1 == 0) { // just for the palette QTransform shearTransform; shearTransform.shear(0.0, -(lw / 2.0) / w2); path = shearTransform.map(path); setbbox(path.boundingRect()); addbbox(QRectF(bbox().x(), bbox().bottom(), bbox().width(), _spatium)); return; } Note* anchor1 = _chord1->upNote(); Stem* stem = _chord1->stem(); qreal x, y, h; if (stem) { x = stem->pos().x(); y = stem->pos().y(); h = stem->stemLen(); } else { // center tremolo above note x = anchor1->x() + anchor1->headWidth() * .5; y = anchor1->y(); h = 2.0 * _spatium + bbox().height(); if (anchor1->line() > 4) h *= -1; } if (!twoNotes()) { // // single note tremolos // bool up = _chord1->up(); int line = up ? _chord1->upLine() : _chord1->downLine(); static const qreal t[3][2][4][2] = { // normal stem { // DOWN { // even line odd line { 6, 5 }, // line 1 { 6 - 2 * .8, 5 - 2 * .8 }, // line 2 { 6 - 4 * .8, 3 }, // line 3 { 2 , 3 } // line 4 }, // UP { // even line odd line { -6, -5 }, // line 1 { -6, -5 }, // line 2 { -6, -3 - 4 * .8 }, // line 3 { -2 - 6 * .8, -3 - 6 * .8 } // line 4 } }, // stem with hook { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, // stem with beam { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, }; int idx = _chord1->hook() ? 1 : (_chord1->beam() ? 2 : 0); y = (line + t[idx][up][_lines-1][line & 1]) * spatium() * .5 / mag(); QTransform shearTransform; shearTransform.shear(0.0, -(lw / 2.0) / w2); path = shearTransform.map(path); setbbox(path.boundingRect()); setPos(x, y); return; } y += (h - bbox().height()) * .5; // // two chord tremolo // Segment* s = _chord1->segment()->next(); while (s) { if (s->element(track()) && (s->element(track())->type() == ElementType::CHORD)) break; s = s->next(); } if (s == 0) { qDebug("no second note of tremolo found"); return; } _chord2 = toChord(s->element(track())); _chord2->setTremolo(this); Stem* stem1 = _chord1->stem(); Stem* stem2 = _chord2->stem(); // compute the y coordinates of the tips of the stems qreal y1, y2; qreal firstChordStaffY; if (stem2 && stem1) { // stemPageYOffset variable is used for the case when the first // chord is cross-staff firstChordStaffY = stem1->pagePos().y() - stem1->y(); // y coordinate of the staff of the first chord y1 = stem1->y() + stem1->p2().y(); y2 = stem2->pagePos().y() - firstChordStaffY + stem2->p2().y(); // ->p2().y() is better than ->stemLen() } else { firstChordStaffY = _chord1->pagePos().y() - _chord1->y(); // y coordinate of the staff of the first chord y1 = _chord1->stemPosBeam().y() - firstChordStaffY + _chord1->defaultStemLength(); y2 = _chord2->stemPosBeam().y() - firstChordStaffY + _chord2->defaultStemLength(); } // improve the case when one stem is up and another is down if (_chord1->beams() == 0 && _chord2->beams() == 0 && _chord1->up() != _chord2->up()) { qreal meanNote1Y = .5 * (_chord1->upNote()->pagePos().y() - firstChordStaffY + _chord1->downNote()->pagePos().y() - firstChordStaffY); qreal meanNote2Y = .5 * (_chord2->upNote()->pagePos().y() - firstChordStaffY + _chord2->downNote()->pagePos().y() - firstChordStaffY); y1 = .5 * (y1 + meanNote1Y); y2 = .5 * (y2 + meanNote2Y); } y = (y1 + y2) * .5; if (!_chord1->up()) { y -= path.boundingRect().height() * .5; } if (!_chord2->up()) { y -= path.boundingRect().height() * .5; } // compute the x coordinates of the inner edge of the stems qreal x2 = _chord2->stemPosBeam().x(); if (_chord2->up() && stem2) x2 -= stem2->lineWidth(); qreal x1 = _chord1->stemPosBeam().x(); if (!_chord1->up() && stem1) x1 += stem1->lineWidth(); x = (x1 + x2) * .5 - _chord1->pagePos().x(); QTransform xScaleTransform; // TODO const qreal H_MULTIPLIER = score()->styleS(Sid::tremoloBeamLengthMultiplier).val(); const qreal H_MULTIPLIER = 0.62; // TODO const qreal MAX_H_LENGTH = _spatium * score()->styleS(Sid::tremoloBeamLengthMultiplier).val(); const qreal MAX_H_LENGTH = _spatium * 12.0; qreal xScaleFactor = qMin(H_MULTIPLIER * (x2 - x1), MAX_H_LENGTH); xScaleFactor /= (2.0 * w2); xScaleTransform.scale(xScaleFactor, 1.0); path = xScaleTransform.map(path); qreal beamYOffset = 0.0; if (_chord1->beams() == _chord2->beams() && _chord1->beam()) { int beams = _chord1->beams(); qreal beamHalfLineWidth = point(score()->styleS(Sid::beamWidth)) * .5 * mag(); beamYOffset = beams * _chord1->beam()->beamDist() - beamHalfLineWidth; if (_chord1->up() != _chord2->up()) { // cross-staff beamYOffset = 2 * beamYOffset + beamHalfLineWidth; } else if (!_chord1->up() && !_chord2->up()) { beamYOffset = -beamYOffset; } } QTransform shearTransform; if (_chord1->beams() == 0 && _chord2->beams() == 0) { if (_chord1->up() && !_chord2->up()) shearTransform.shear(0.0, (y2 - y1 - path.boundingRect().height()) / (x2 - x1)); else if (!_chord1->up() && _chord2->up()) shearTransform.shear(0.0, (y2 - y1 + path.boundingRect().height()) / (x2 - x1)); else shearTransform.shear(0.0, (y2 - y1) / (x2 - x1)); } else { shearTransform.shear(0.0, (y2 - y1) / (x2 - x1)); } path = shearTransform.map(path); setbbox(path.boundingRect()); setPos(x, y + beamYOffset); }
void StringData::fretChords(Chord * chord) const { int nFret, minFret, maxFret, nNewFret, nTempFret; int nString, nNewString, nTempString; if(bFretting) return; bFretting = true; // we need to keep track of string allocation int bUsed[strings()]; // initially all strings are available for(nString=0; nString<strings(); nString++) bUsed[nString] = 0; // we also need the notes sorted in order of string (from highest to lowest) and then pitch QMap<int, Note *> sortedNotes; int count = 0; // store staff pitch offset at this tick, to speed up actual note pitch calculations // (ottavas not implemented yet) int transp = chord->staff() ? chord->part()->instrument()->transpose().chromatic : 0; // TODO: tick? int pitchOffset = /*chord->staff()->pitchOffset(chord->segment()->tick())*/ - transp; // if chord parent is not a segment, the chord is special (usually a grace chord): // fret it by itself, ignoring the segment if (chord->parent()->type() != ElementType::SEGMENT) sortChordNotes(sortedNotes, chord, pitchOffset, &count); else { // scan each chord of seg from same staff as 'chord', inserting each of its notes in sortedNotes Segment* seg = chord->segment(); int trk; int trkFrom = (chord->track() / VOICES) * VOICES; int trkTo = trkFrom + VOICES; for(trk = trkFrom; trk < trkTo; ++trk) { Element* ch = seg->elist().at(trk); if (ch && ch->type() == ElementType::CHORD) sortChordNotes(sortedNotes, toChord(ch), pitchOffset, &count); } } // determine used range of frets minFret = INT32_MAX; maxFret = INT32_MIN; foreach(Note* note, sortedNotes) { if (note->string() != STRING_NONE) bUsed[note->string()]++; if (note->fret() != FRET_NONE && note->fret() < minFret) minFret = note->fret(); if (note->fret() != FRET_NONE && note->fret() > maxFret) maxFret = note->fret(); } // scan chord notes from highest, matching with strings from the highest foreach(Note * note, sortedNotes) { nString = nNewString = note->string(); nFret = nNewFret = note->fret(); note->setFretConflict(false); // assume no conflicts on this note // if no fretting (any invalid fretting has been erased by sortChordNotes() ) if (nString == STRING_NONE /*|| nFret == FRET_NONE || getPitch(nString, nFret) != note->pitch()*/) { // get a new fretting if (!convertPitch(note->pitch(), pitchOffset, &nNewString, &nNewFret) ) { // no way to fit this note in this tab: // mark as fretting conflict note->setFretConflict(true); // store fretting change without affecting chord context if (nFret != nNewFret) note->undoChangeProperty(Pid::FRET, nNewFret); if (nString != nNewString) note->undoChangeProperty(Pid::STRING, nNewString); continue; } // note can be fretted: use string else { bUsed[nNewString]++; } } // if the note string (either original or newly assigned) is also used by another note if (bUsed[nNewString] > 1) { // attempt to find a suitable string, from topmost for (nTempString=0; nTempString < strings(); nTempString++) { if (bUsed[nTempString] < 1 && (nTempFret=fret(note->pitch(), nTempString, pitchOffset)) != FRET_NONE) { bUsed[nNewString]--; // free previous string bUsed[nTempString]++; // and occupy new string nNewFret = nTempFret; nNewString = nTempString; break; } } } // TODO : try to optimize used fret range, avoiding eccessively open positions // if fretting did change, store as a fret change if (nFret != nNewFret) note->undoChangeProperty(Pid::FRET, nNewFret); if (nString != nNewString) note->undoChangeProperty(Pid::STRING, nNewString); }