Segment* Score::tick2segmentEnd(int track, int tick) const { Measure* m = tick2measure(tick); if (m == 0) { qDebug("tick2segment(): not found tick %d\n", tick); return 0; } // loop over all segments for (Segment* segment = m->first(Segment::SegChordRest); segment; segment = segment->next(Segment::SegChordRest)) { ChordRest* cr = static_cast<ChordRest*>(segment->element(track)); if (!cr) continue; // TODO LVI: check if following is correct, see exceptions in // ExportMusicXml::chord() and ExportMusicXml::rest() int endTick = cr->tick() + cr->actualTicks(); if (endTick < tick) continue; // not found yet else if (endTick == tick) { return segment; // found it } else { // endTick > tick (beyond the tick we are looking for) return 0; } } return 0; }
ChordRest* Selection::lastChordRest(int track) const { if (_el.size() == 1) { Element* el = _el[0]; if (el && el->type() == Element::NOTE) return static_cast<ChordRest*>(el->parent()); else if (el->type() == Element::CHORD || el->type() == Element::REST) return static_cast<ChordRest*>(el); return 0; } ChordRest* cr = 0; for (auto i = _el.begin(); i != _el.end(); ++i) { Element* el = *i; if (el->type() == Element::NOTE) el = ((Note*)el)->chord(); if (el->isChordRest() && static_cast<ChordRest*>(el)->segment()->segmentType() == Segment::SegChordRest) { if (track != -1 && el->track() != track) continue; if (cr) { if (((ChordRest*)el)->tick() >= cr->tick()) cr = (ChordRest*)el; } else cr = (ChordRest*)el; } } return cr; }
ChordRest* Selection::firstChordRest(int track) const { if (_el.size() == 1) { Element* el = _el[0]; if (el->type() == Element::NOTE) return static_cast<ChordRest*>(el->parent()); else if (el->type() == Element::REST) return static_cast<ChordRest*>(el); return 0; } ChordRest* cr = 0; foreach (Element* el, _el) { if (el->type() == Element::NOTE) el = el->parent(); if (el->isChordRest()) { if (track != -1 && el->track() != track) continue; if (cr) { if (static_cast<ChordRest*>(el)->tick() < cr->tick()) cr = static_cast<ChordRest*>(el); } else cr = static_cast<ChordRest*>(el); } } return cr; }
void MuseScore::tupletDialog() { if (!cs) return; ChordRest* cr = cs->getSelectedChordRest(); if (cr == 0) return; TupletDialog td; if (!td.exec()) return; Tuplet* tuplet = new Tuplet(cs); tuplet->setTrack(cr->track()); tuplet->setTick(cr->tick()); td.setupTuplet(tuplet); // tuplet->setRatio(tuplet->ratio().reduced()); Fraction f1(cr->duration()); tuplet->setDuration(f1); Fraction f = f1 * tuplet->ratio(); f.reduce(); printf("len %s ratio %s base %s\n", qPrintable(f1.print()), qPrintable(tuplet->ratio().print()), qPrintable(f.print())); tuplet->setBaseLen(Fraction(1, f.denominator())); Measure* measure = cr->measure(); tuplet->setParent(measure); cs->cmdCreateTuplet(cr, tuplet); }
void MuseScore::addTempo() { ChordRest* cr = cs->getSelectedChordRest(); if (!cr) return; // double bps = 2.0; SigEvent event = cs->sigmap()->timesig(cr->tick()); Fraction f = event.nominal(); QString text(QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd5f))); switch (f.denominator()) { case 1: text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd5d)); break; case 2: text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd5e)); break; case 4: text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd5f)); break; case 8: if(f.numerator() % 3 == 0) text = QString("%1%2%3%4 = 80").arg(QChar(0xd834)).arg(QChar(0xdd5f)).arg(QChar(0xd834)).arg(QChar(0xdd6d)); else text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd60)); break; case 16: if(f.numerator() % 3 == 0) text = QString("%1%2%3%4 = 80").arg(QChar(0xd834)).arg(QChar(0xdd60)).arg(QChar(0xd834)).arg(QChar(0xdd6d)); else text = text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd61)); break; case 32: if(f.numerator() % 3 == 0) text = QString("%1%2%3%4 = 80").arg(QChar(0xd834)).arg(QChar(0xdd61)).arg(QChar(0xd834)).arg(QChar(0xdd6d)); else text = text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd62)); break; case 64: if(f.numerator() % 3 == 0) text = QString("%1%2%3%4 = 80").arg(QChar(0xd834)).arg(QChar(0xdd62)).arg(QChar(0xd834)).arg(QChar(0xdd6d)); else text = text = QString("%1%2 = 80").arg(QChar(0xd834)).arg(QChar(0xdd63)); break; default: break; } TempoText* tt = new TempoText(cs); tt->setParent(cr->segment()); tt->setTrack(cr->track()); tt->setText(text); tt->setFollowText(true); //tt->setTempo(bps); cs->undoAddElement(tt); cv->startEdit(tt); }
void MuseScore::addTempo() { ChordRest* cr = cs->getSelectedChordRest(); if (!cr) return; // double bps = 2.0; SigEvent event = cs->sigmap()->timesig(cr->tick()); Fraction f = event.nominal(); QString text("<sym>noteQuarterUp</sym> = 80"); switch (f.denominator()) { case 1: text = "<sym>noteWhole</sym> = 80"; break; case 2: text = "<sym>noteHalfUp</sym> = 80"; break; case 4: text = "<sym>noteQuarterUp</sym> = 80"; break; case 8: if(f.numerator() % 3 == 0) text = "<sym>noteQuarterUp</sym><sym>textAugmentationDot</sym> = 80"; else text = "<sym>note8thUp</sym> = 80"; break; case 16: if(f.numerator() % 3 == 0) text = text = "<sym>note8thUp</sym><sym>textAugmentationDot</sym> = 80"; else text = "<sym>note16thUp</sym> = 80"; break; case 32: if(f.numerator() % 3 == 0) text = "<sym>note16thUp</sym><sym>textAugmentationDot</sym> = 80"; else text = "<sym>note32thUp</sym> = 80"; break; case 64: if(f.numerator() % 3 == 0) text = "<sym>note32thUp</sym><sym>textAugmentationDot</sym> = 80"; else text = "<sym>note64thUp</sym> = 80"; break; default: break; } TempoText* tt = new TempoText(cs); tt->setParent(cr->segment()); tt->setTrack(cr->track()); tt->setText(text); tt->setFollowText(true); //tt->setTempo(bps); cs->undoAddElement(tt); cv->startEdit(tt); }
int Selection::tickStart() const { switch (_state) { case SelState::RANGE: return _startSegment->tick(); break; case SelState::LIST: { ChordRest* cr = firstChordRest(); return (cr) ? cr->tick() : -1; break; } default: return -1; } }
ChordRest* Selection::firstChordRest(int track) const { ChordRest* cr = 0; foreach (Element* el, _el) { if (el->type() == NOTE) el = el->parent(); if (el->isChordRest()) { if (track != -1 && el->track() != track) continue; if (cr) { if (static_cast<ChordRest*>(el)->tick() < cr->tick()) cr = static_cast<ChordRest*>(el); } else cr = static_cast<ChordRest*>(el); } } return cr; }
ChordRest* Selection::lastChordRest(int track) const { ChordRest* cr = 0; for (ciElement i = _el.begin(); i != _el.end(); ++i) { Element* el = *i; if (el->type() == NOTE) el = ((Note*)el)->chord(); if (el->isChordRest() && static_cast<ChordRest*>(el)->segment()->subtype() == SegChordRest) { if (track != -1 && el->track() != track) continue; if (cr) { if (((ChordRest*)el)->tick() >= cr->tick()) cr = (ChordRest*)el; } else cr = (ChordRest*)el; } } return cr; }
int Selection::tickEnd() const { switch (_state) { case SelState::RANGE: { if (_endSegment) { return _endSegment->tick(); } else { // endsegment == 0 if end of score Measure* m = _score->lastMeasure(); return m->tick() + m->ticks(); } break; } case SelState::LIST: { ChordRest* cr = lastChordRest(); return (cr) ? cr->tick() : -1; break; } default: return -1; } }
void Lyrics::layout1() { setPos(textStyle().offset(spatium())); Text::layout1(); if (!parent()) // palette & clone trick return; ChordRest* cr = chordRest(); const QList<Lyrics*>* ll = &(cr->lyricsList()); qreal lh = lineSpacing() * score()->styleD(StyleIdx::lyricsLineHeight); int line = ll->indexOf(this); qreal y = lh * line + point(score()->styleS(StyleIdx::lyricsDistance)); qreal x = 0.0; // // parse leading verse number and/or punctuation, so we can factor it into layout separately // TODO: provide a way to disable this // bool hasNumber = false; // _verseNumber; qreal adjust = 0.0; QString s = plainText(true); // find: // 1) string of numbers and non-word characters at start of syllable // 2) at least one other character (indicating start of actual lyric) QRegularExpression leadingPattern("(^[\\d\\W]+)([^\\d\\W]+)"); QRegularExpressionMatch leadingMatch = leadingPattern.match(s); if (leadingMatch.hasMatch()) { // leading string QString s1 = leadingMatch.captured(1); // actual lyric //QString s2 = leadingMatch.captured(2); Text leading(*this); leading.setPlainText(s1); leading.layout1(); adjust = leading.width(); if (!s1.isEmpty() && s1[0].isDigit()) hasNumber = true; } if (textStyle().align() & AlignmentFlags::HCENTER) { // // center under notehead, not origin // however, lyrics that are melismas or have verse numbers will be forced to left alignment // TODO: provide a way to disable the automatic left alignment // qreal maxWidth; if (cr->type() == Element::Type::CHORD) maxWidth = static_cast<Chord*>(cr)->maxHeadWidth(); else maxWidth = cr->width(); // TODO: exclude ledger line for multivoice rest? qreal nominalWidth = symWidth(SymId::noteheadBlack); if (!isMelisma() && !hasNumber) // center under notehead x += nominalWidth * .5 - cr->x() - adjust * 0.5; else // force left alignment x += (width() + nominalWidth - maxWidth) * .5 - cr->x() - adjust; } else { // even for left aligned syllables, ignore leading verse numbers and/or punctuation x -= adjust; } rxpos() += x; rypos() += y; if (_ticks > 0 || _syllabic == Syllabic::BEGIN || _syllabic == Syllabic::MIDDLE) { if (_separator == nullptr) { _separator = new LyricsLine(score()); _separator->setTick(cr->tick()); score()->addUnmanagedSpanner(_separator); } _separator->setParent(this); _separator->setTick(cr->tick()); _separator->setTrack(track()); _separator->setTrack2(track()); #if defined(USE_FONT_DASH_METRIC) // if font parameters different from font cached values, compute new dash values from font metrics if (textStyle().family() != g_fontFamily && textStyle().size() != g_fontSize) { QFontMetricsF fm = textStyle().fontMetrics(spatium()); QRectF r = fm.tightBoundingRect("\u2013"); // U+2013 EN DASH g_cachedDashY = _dashY = r.y() + (r.height() * HALF); g_cachedDashLength = _dashLength = r.width(); #if defined(USE_FONT_DASH_TICKNESS) g_cachedDashThickness = _dashThickness = r.height(); #endif g_fontFamily = textStyle().family(); g_fontSize = textStyle().size(); } // if same font, use cached values else { _dashY = g_cachedDashY; _dashLength = g_cachedDashLength; #if defined(USE_FONT_DASH_TICKNESS) _dashThickness = g_cachedDashThickness; #endif } #endif } else if (_separator != nullptr) { _separator->unchain(); delete _separator; _separator = nullptr; } }