Merge pull request #12253 from alexpavlov96/gp_bend
implemented stretched bends for correct import from guitar pro
This commit is contained in:
commit
b0a2e290f8
19 changed files with 670 additions and 14 deletions
|
@ -86,8 +86,8 @@ static const PitchValues PREBEND_RELEASE_CURVE = { PitchValue(0, 100),
|
|||
// Bend
|
||||
//---------------------------------------------------------
|
||||
|
||||
Bend::Bend(Note* parent)
|
||||
: EngravingItem(ElementType::BEND, parent, ElementFlag::MOVABLE)
|
||||
Bend::Bend(Note* parent, ElementType type)
|
||||
: EngravingItem(type, parent, ElementFlag::MOVABLE)
|
||||
{
|
||||
initElementStyle(&bendStyle);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ enum class BendType {
|
|||
CUSTOM
|
||||
};
|
||||
|
||||
class Bend final : public EngravingItem
|
||||
class Bend : public EngravingItem // TODO: bring back "final" keyword
|
||||
{
|
||||
M_PROPERTY(String, fontFace, setFontFace)
|
||||
M_PROPERTY(double, fontSize, setFontSize)
|
||||
|
@ -70,9 +70,9 @@ public:
|
|||
bool setProperty(Pid propertyId, const PropertyValue&) override;
|
||||
PropertyValue propertyDefault(Pid) const override;
|
||||
|
||||
private:
|
||||
protected: /// TODO: bring back "private" keyword after removing StretchedBend class
|
||||
friend class Factory;
|
||||
Bend(Note* parent);
|
||||
Bend(Note* parent, ElementType type = ElementType::BEND);
|
||||
|
||||
mu::draw::Font font(double) const;
|
||||
BendType parseBendTypeFromCurve() const;
|
||||
|
@ -82,7 +82,8 @@ private:
|
|||
PitchValues m_points;
|
||||
|
||||
mu::PointF m_notePos;
|
||||
double m_noteWidth;
|
||||
double m_noteWidth = 0;
|
||||
double m_noteHeight = 0;
|
||||
};
|
||||
} // namespace mu::engraving
|
||||
#endif
|
||||
|
|
|
@ -106,6 +106,7 @@ class TextBase;
|
|||
class Hairpin;
|
||||
class HairpinSegment;
|
||||
class Bend;
|
||||
class StretchedBend;
|
||||
class TremoloBar;
|
||||
class MeasureRepeat;
|
||||
class Tuplet;
|
||||
|
@ -351,6 +352,7 @@ public:
|
|||
CONVERT(Hairpin, HAIRPIN)
|
||||
CONVERT(HairpinSegment, HAIRPIN_SEGMENT)
|
||||
CONVERT(Bend, BEND)
|
||||
CONVERT(StretchedBend, STRETCHED_BEND)
|
||||
CONVERT(TremoloBar, TREMOLOBAR)
|
||||
CONVERT(MeasureRepeat, MEASURE_REPEAT)
|
||||
CONVERT(Tuplet, TUPLET)
|
||||
|
@ -680,6 +682,7 @@ CONVERT(MMRestRange)
|
|||
CONVERT(Hairpin)
|
||||
CONVERT(HairpinSegment)
|
||||
CONVERT(Bend)
|
||||
CONVERT(StretchedBend)
|
||||
CONVERT(TremoloBar)
|
||||
CONVERT(MeasureRepeat)
|
||||
CONVERT(MMRest)
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
#include "tremolobar.h"
|
||||
#include "fret.h"
|
||||
#include "bend.h"
|
||||
#include "stretchedbend.h"
|
||||
#include "lyrics.h"
|
||||
#include "figuredbass.h"
|
||||
#include "slur.h"
|
||||
|
@ -178,6 +179,7 @@ EngravingItem* Factory::doCreateItem(ElementType type, EngravingItem* parent)
|
|||
case ElementType::HARMONY: return new Harmony(parent->isSegment() ? toSegment(parent) : dummy->segment());
|
||||
case ElementType::FRET_DIAGRAM: return new FretDiagram(parent->isSegment() ? toSegment(parent) : dummy->segment());
|
||||
case ElementType::BEND: return new Bend(parent->isNote() ? toNote(parent) : dummy->note());
|
||||
case ElementType::STRETCHED_BEND: return new StretchedBend(parent->isNote() ? toNote(parent) : dummy->note());
|
||||
case ElementType::TREMOLOBAR: return new TremoloBar(parent);
|
||||
case ElementType::LYRICS: return new Lyrics(parent->isChordRest() ? toChordRest(parent) : dummy->chord());
|
||||
case ElementType::FIGURED_BASS: return new FiguredBass(parent->isSegment() ? toSegment(parent) : dummy->segment());
|
||||
|
@ -308,7 +310,17 @@ std::shared_ptr<Beam> Factory::makeBeam(System* parent)
|
|||
return std::shared_ptr<Beam>(createBeam(parent));
|
||||
}
|
||||
|
||||
CREATE_ITEM_IMPL(Bend, ElementType::BEND, Note, isAccessibleEnabled)
|
||||
//CREATE_ITEM_IMPL(Bend, ElementType::BEND, Note, isAccessibleEnabled)
|
||||
CREATE_ITEM_IMPL(StretchedBend, ElementType::STRETCHED_BEND, Note, isAccessibleEnabled)
|
||||
|
||||
Bend* Factory::createBend(Note * parent, ElementType type, bool isAccessibleEnabled)
|
||||
{
|
||||
Bend* b = new Bend(parent, type);
|
||||
b->setAccessibleEnabled(isAccessibleEnabled);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
MAKE_ITEM_IMPL(Bend, Note)
|
||||
|
||||
CREATE_ITEM_IMPL(Bracket, ElementType::BRACKET, EngravingItem, isAccessibleEnabled)
|
||||
|
|
|
@ -59,9 +59,11 @@ public:
|
|||
static Beam* createBeam(System* parent, bool isAccessibleEnabled = true);
|
||||
static std::shared_ptr<Beam> makeBeam(System* parent);
|
||||
|
||||
static Bend* createBend(Note* parent, bool isAccessibleEnabled = true);
|
||||
static Bend* createBend(Note* parent, ElementType type = ElementType::BEND, bool isAccessibleEnabled = true);
|
||||
static std::shared_ptr<Bend> makeBend(Note* parent);
|
||||
|
||||
static StretchedBend* createStretchedBend(Note* parent, bool isAccessibleEnabled = true);
|
||||
|
||||
static Bracket* createBracket(EngravingItem* parent, bool isAccessibleEnabled = true);
|
||||
static std::shared_ptr<Bracket> makeBracket(EngravingItem* parent);
|
||||
static BracketItem* createBracketItem(EngravingItem* parent);
|
||||
|
|
|
@ -39,6 +39,8 @@ set(LIBMSCORE_SRC
|
|||
${CMAKE_CURRENT_LIST_DIR}/beam.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/bend.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/bend.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/stretchedbend.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/stretchedbend.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/box.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/box.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/bracket.cpp
|
||||
|
|
|
@ -1207,6 +1207,9 @@ void Note::add(EngravingItem* e)
|
|||
case ElementType::NOTEDOT:
|
||||
_dots.push_back(toNoteDot(e));
|
||||
break;
|
||||
case ElementType::STRETCHED_BEND:
|
||||
m_bend = toStretchedBend(e);
|
||||
// fallthrough
|
||||
case ElementType::FINGERING:
|
||||
case ElementType::SYMBOL:
|
||||
case ElementType::IMAGE:
|
||||
|
@ -1252,6 +1255,9 @@ void Note::remove(EngravingItem* e)
|
|||
_dots.pop_back();
|
||||
break;
|
||||
|
||||
case ElementType::STRETCHED_BEND:
|
||||
m_bend = nullptr;
|
||||
// fallthrough
|
||||
case ElementType::TEXT:
|
||||
case ElementType::SYMBOL:
|
||||
case ElementType::IMAGE:
|
||||
|
|
|
@ -195,6 +195,7 @@ private:
|
|||
bool _play = true; ///< note is not played if false
|
||||
mutable bool _mark = false; ///< for use in sequencer
|
||||
bool _fixed = false; ///< for slash notation
|
||||
StretchedBend* m_bend = nullptr;
|
||||
|
||||
DirectionH _userMirror = DirectionH::AUTO; ///< user override of mirror
|
||||
DirectionV _userDotPosition = DirectionV::AUTO; ///< user override of dot position
|
||||
|
@ -537,6 +538,8 @@ public:
|
|||
|
||||
void relateSlide(Note& start) { _relatedSlide = &start._attachedSlide; }
|
||||
|
||||
StretchedBend* bend() const { return m_bend; }
|
||||
|
||||
bool isHammerOn() const { return _isHammerOn; }
|
||||
void setIsHammerOn(bool hammerOn) { _isHammerOn = hammerOn; }
|
||||
|
||||
|
|
494
src/engraving/libmscore/stretchedbend.cpp
Normal file
494
src/engraving/libmscore/stretchedbend.cpp
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stretchedbend.h"
|
||||
|
||||
#include "staff.h"
|
||||
#include "score.h"
|
||||
#include "chord.h"
|
||||
#include "draw/fontmetrics.h"
|
||||
#include "log.h"
|
||||
|
||||
using namespace mu;
|
||||
using namespace mu::draw;
|
||||
using namespace mu::engraving;
|
||||
|
||||
namespace mu::engraving {
|
||||
//---------------------------------------------------------
|
||||
// label
|
||||
//---------------------------------------------------------
|
||||
|
||||
static const char* label[] = {
|
||||
"", "\u00BC", "\u00BD", "\u00BE", /// 0, 1/4, 1/2, 3/4
|
||||
"full", "1\u00BC", "1\u00BD", "1\u00BE", /// 1, 1+1/4...
|
||||
"2", "2\u00BC", "2\u00BD", "2\u00BE", /// 2, ...
|
||||
"3" /// 3
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// textFlags
|
||||
//---------------------------------------------------------
|
||||
|
||||
static int textFlags = Qt::AlignHCenter | Qt::AlignBottom | Qt::TextDontClip;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// forward declarations of static functions
|
||||
//---------------------------------------------------------
|
||||
|
||||
static void drawText(mu::draw::Painter* painter, const PointF& pos, const String& text);
|
||||
static RectF textBoundingRect(const mu::draw::FontMetrics& fm, const PointF& pos, const String& text);
|
||||
static PainterPath bendCurveFromPoints(const PointF& p1, const PointF& p2);
|
||||
static int bendTone(int notePitch);
|
||||
|
||||
//---------------------------------------------------------
|
||||
// static values
|
||||
//---------------------------------------------------------
|
||||
|
||||
static constexpr double s_bendHeightMultiplier = .2; /// how much height differs for bend pitches
|
||||
|
||||
//---------------------------------------------------------
|
||||
// StretchedBend
|
||||
//---------------------------------------------------------
|
||||
|
||||
StretchedBend::StretchedBend(Note* parent)
|
||||
: Bend(parent, ElementType::STRETCHED_BEND)
|
||||
{
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// fillSegments
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::fillSegments()
|
||||
{
|
||||
m_bendSegments.clear();
|
||||
size_t n = m_points.size();
|
||||
if (n < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointF src = (m_points[0].pitch == 0)
|
||||
? PointF(m_noteWidth + m_spatium * .8, 0)
|
||||
: PointF(m_noteWidth * .5, -m_noteHeight * .5 - m_spatium * .2);
|
||||
|
||||
PointF dest(0, 0);
|
||||
|
||||
int firstPointPitch = m_points.front().pitch;
|
||||
int lastPointPitch = m_points.back().pitch;
|
||||
m_releasedToInitial = (0 == lastPointPitch);
|
||||
|
||||
double baseBendHeight = m_spatium * 1.5;
|
||||
|
||||
bool skipNext = false; // need to skip some points
|
||||
|
||||
for (size_t pt = 0; pt < n - 1; pt++) {
|
||||
if (skipNext) {
|
||||
skipNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int pitch = m_points[pt].pitch;
|
||||
int nextPitch = m_points[pt + 1].pitch;
|
||||
|
||||
BendSegmentType type = BendSegmentType::NO_TYPE;
|
||||
int tone = bendTone(nextPitch);
|
||||
bool untilNextSegment = false;
|
||||
|
||||
/// PRE-BEND (+BEND, +RELEASE)
|
||||
if (pt == 0 && pitch != 0) {
|
||||
int prebendTone = bendTone(pitch);
|
||||
double minY = std::min(-m_notePos.y(), src.y());
|
||||
dest = PointF(src.x(), minY - bendHeight(prebendTone) - baseBendHeight);
|
||||
m_bendSegments.push_back({ src, dest, BendSegmentType::LINE_UP, prebendTone });
|
||||
src.ry() = dest.y();
|
||||
}
|
||||
|
||||
/// PRE-BEND - - -
|
||||
if (pitch == nextPitch) {
|
||||
if (pt == (n - 2)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pt == 0) {
|
||||
type = BendSegmentType::LINE_STROKED;
|
||||
}
|
||||
} else {
|
||||
bool bendUp = pitch < nextPitch;
|
||||
|
||||
if (pt < n - 2) {
|
||||
double nextNextPitch = m_points[pt + 2].pitch;
|
||||
bool nextBendUp = nextPitch < nextNextPitch;
|
||||
if (bendUp == nextBendUp) {
|
||||
nextPitch = nextNextPitch;
|
||||
skipNext = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bendUp) {
|
||||
double minY = std::min(-m_notePos.y(), src.y());
|
||||
dest.ry() = minY - bendHeight(tone) - baseBendHeight;
|
||||
type = BendSegmentType::CURVE_UP;
|
||||
} else {
|
||||
if (m_releasedToInitial) {
|
||||
dest.ry() = 0;
|
||||
} else {
|
||||
dest.ry() = src.y() + baseBendHeight;
|
||||
}
|
||||
|
||||
type = BendSegmentType::CURVE_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != BendSegmentType::NO_TYPE) {
|
||||
m_bendSegments.push_back({ src, dest, type, tone });
|
||||
}
|
||||
|
||||
src = dest;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// stretchSegments
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::stretchSegments()
|
||||
{
|
||||
if (m_bendSegments.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// find end of the whole bend
|
||||
double bendEnd = nextSegmentX();
|
||||
|
||||
for (BendSegment& seg : m_bendSegments) {
|
||||
if (seg.type != BendSegmentType::LINE_UP) {
|
||||
seg.dest.rx() = bendEnd;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bendSegments.size() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t segsSize = m_bendSegments.size();
|
||||
auto& lastSeg = m_bendSegments[segsSize - 1];
|
||||
auto& prevSeg = m_bendSegments[segsSize - 2];
|
||||
if (lastSeg.type != BendSegmentType::LINE_UP && prevSeg.type != BendSegmentType::LINE_UP) {
|
||||
lastSeg.dest.rx() = bendEnd;
|
||||
double newCoord = prevSeg.src.x() + (bendEnd - prevSeg.src.x()) / 2;
|
||||
prevSeg.dest.rx() = newCoord;
|
||||
lastSeg.src.rx() = newCoord;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// layout
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::layout()
|
||||
{
|
||||
preLayout();
|
||||
layoutDraw(true);
|
||||
postLayout();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// draw
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::draw(mu::draw::Painter* painter) const
|
||||
{
|
||||
TRACE_OBJ_DRAW;
|
||||
|
||||
setupPainter(painter);
|
||||
layoutDraw(false, painter);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// layoutDraw
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::layoutDraw(const bool layoutMode, mu::draw::Painter* painter) const
|
||||
{
|
||||
if (!layoutMode && !painter) {
|
||||
return;
|
||||
}
|
||||
|
||||
double prebendStrokeLength = m_spatium * .5;
|
||||
|
||||
for (const BendSegment& bendSegment : m_bendSegments) {
|
||||
const PointF& src = bendSegment.src;
|
||||
const PointF& dest = bendSegment.dest;
|
||||
const String& text = String(label[bendSegment.tone]);
|
||||
|
||||
switch (bendSegment.type) {
|
||||
case BendSegmentType::LINE_UP:
|
||||
{
|
||||
if (layoutMode) {
|
||||
m_boundingRect.unite(RectF(src.x(), src.y(), dest.x() - src.x(), dest.y() - src.y()));
|
||||
m_boundingRect.unite(m_arrowUp.translated(dest).boundingRect());
|
||||
|
||||
mu::draw::FontMetrics fm(font(m_spatium));
|
||||
m_boundingRect.unite(textBoundingRect(fm, dest, text));
|
||||
} else {
|
||||
painter->drawLine(LineF(src, dest));
|
||||
painter->setBrush(curColor());
|
||||
painter->drawPolygon(m_arrowUp.translated(dest));
|
||||
drawText(painter, dest, text);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BendSegmentType::CURVE_UP:
|
||||
case BendSegmentType::CURVE_DOWN:
|
||||
{
|
||||
bool bendUp = (bendSegment.type == BendSegmentType::CURVE_UP);
|
||||
double endY = dest.y() + m_bendArrowWidth * (bendUp ? 1 : -1);
|
||||
|
||||
PainterPath path = bendCurveFromPoints(src, PointF(dest.x(), endY));
|
||||
const auto& arrowPath = (bendUp ? m_arrowUp : m_arrowDown);
|
||||
|
||||
if (layoutMode) {
|
||||
m_boundingRect.unite(path.boundingRect());
|
||||
m_boundingRect.unite(arrowPath.translated(dest).boundingRect());
|
||||
} else {
|
||||
painter->setBrush(BrushStyle::NoBrush);
|
||||
painter->drawPath(path);
|
||||
painter->setBrush(curColor());
|
||||
painter->drawPolygon(arrowPath.translated(dest));
|
||||
}
|
||||
|
||||
if (bendUp || !m_releasedToInitial) {
|
||||
if (layoutMode) {
|
||||
mu::draw::FontMetrics fm(font(m_spatium));
|
||||
m_boundingRect.unite(textBoundingRect(fm, dest - PointF(m_spatium, 0), text));
|
||||
} else {
|
||||
double textLabelOffset = (!bendUp && !m_releasedToInitial ? m_spatium : 0);
|
||||
PointF textPoint = dest + PointF(textLabelOffset, -textLabelOffset);
|
||||
drawText(painter, textPoint, text);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BendSegmentType::LINE_STROKED:
|
||||
{
|
||||
if (layoutMode) {
|
||||
m_boundingRect.unite(RectF(src.x(), src.y(), dest.x() - src.x(), dest.y() - src.y()));
|
||||
} else {
|
||||
PainterPath path;
|
||||
path.moveTo(src + PointF(m_bendArrowWidth, 0));
|
||||
path.lineTo(dest);
|
||||
Pen p(painter->pen());
|
||||
p.setStyle(PenStyle::DashLine);
|
||||
painter->strokePath(path, p);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// preLayout
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::preLayout()
|
||||
{
|
||||
m_spatium = spatium();
|
||||
m_boundingRect = RectF();
|
||||
Note* note = toNote(explicitParent());
|
||||
m_notePos = note->pos();
|
||||
m_noteWidth = note->width();
|
||||
m_noteHeight = note->height();
|
||||
|
||||
fillArrows();
|
||||
fillSegments();
|
||||
stretchSegments();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// postLayout
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::postLayout()
|
||||
{
|
||||
double lw = lineWidth();
|
||||
m_boundingRect.adjust(-lw, -lw, lw, lw);
|
||||
setbbox(m_boundingRect);
|
||||
setPos(0.0, 0.0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// setupPainter
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::setupPainter(mu::draw::Painter* painter) const
|
||||
{
|
||||
Pen pen(curColor(), lineWidth(), PenStyle::SolidLine, PenCapStyle::RoundCap, PenJoinStyle::RoundJoin);
|
||||
painter->setPen(pen);
|
||||
painter->setBrush(Brush(curColor()));
|
||||
|
||||
mu::draw::Font f = font(spatium() * MScore::pixelRatio);
|
||||
painter->setFont(f);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// glueNeighbor
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::prepareBends(std::vector<StretchedBend*>& bends)
|
||||
{
|
||||
/// glueing extra bends together
|
||||
for (StretchedBend* bend : bends) {
|
||||
bend->glueNeighbor();
|
||||
}
|
||||
|
||||
/// deleting reduntant bends
|
||||
auto reduntantIt = std::partition(bends.begin(), bends.end(), [](StretchedBend* bend) { return bend->m_reduntant; });
|
||||
|
||||
for (auto bendIt = bends.begin(); bendIt != reduntantIt; bendIt++) {
|
||||
StretchedBend* bendToRemove = *bendIt;
|
||||
EngravingObject* parentObj = bendToRemove->parent();
|
||||
if (Note* note = dynamic_cast<Note*>(parentObj)) {
|
||||
note->remove(bendToRemove);
|
||||
}
|
||||
|
||||
delete bendToRemove;
|
||||
bendToRemove = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// glueNeighbor
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::glueNeighbor()
|
||||
{
|
||||
if (m_reduntant) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Note*> ties = toNote(parent())->tiedNotes();
|
||||
for (Note* t : ties) {
|
||||
assert(!!t);
|
||||
if (t->bend() && t != parent()) {
|
||||
auto bend = t->bend();
|
||||
|
||||
auto& lastPoints = bend->points();
|
||||
for (int i = 1; i < lastPoints.size(); ++i) {
|
||||
m_points.push_back(lastPoints[i]);
|
||||
}
|
||||
|
||||
t->remove(bend);
|
||||
bend->m_reduntant = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// fillArrows
|
||||
//---------------------------------------------------------
|
||||
|
||||
void StretchedBend::fillArrows()
|
||||
{
|
||||
double aw = 0;
|
||||
m_bendArrowWidth = aw = score()->styleMM(Sid::bendArrowWidth);
|
||||
|
||||
m_arrowUp.clear();
|
||||
m_arrowDown.clear();
|
||||
|
||||
m_arrowUp << PointF(0, 0) << PointF(aw * .5, aw) << PointF(-aw * .5, aw);
|
||||
m_arrowDown << PointF(0, 0) << PointF(aw * .5, -aw) << PointF(-aw * .5, -aw);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// drawText
|
||||
//---------------------------------------------------------
|
||||
|
||||
void drawText(mu::draw::Painter* painter, const PointF& pos, const String& text)
|
||||
{
|
||||
painter->drawText(RectF(pos.x(), pos.y(), .0, .0), textFlags, text);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// textBoundingRect
|
||||
//---------------------------------------------------------
|
||||
|
||||
RectF textBoundingRect(const mu::draw::FontMetrics& fm, const PointF& pos, const String& text)
|
||||
{
|
||||
return fm.boundingRect(RectF(pos.x(), pos.y(), 0, 0), textFlags, text);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// bendCurveFromPoints
|
||||
//---------------------------------------------------------
|
||||
|
||||
PainterPath bendCurveFromPoints(const PointF& p1, const PointF& p2)
|
||||
{
|
||||
PainterPath path;
|
||||
|
||||
path.moveTo(p1.x(), p1.y());
|
||||
path.cubicTo(p1.x() + (p2.x() - p1.x()) / 2, p1.y(), p2.x(), p1.y() + (p2.y() - p1.y()) / 4, p2.x(), p2.y());
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// nextSegmentX
|
||||
//---------------------------------------------------------
|
||||
|
||||
double StretchedBend::nextSegmentX() const
|
||||
{
|
||||
Segment* nextSeg = toNote(parent())->chord()->segment()->nextInStaff(
|
||||
staffIdx(), SegmentType::ChordRest | SegmentType::BarLine | SegmentType::EndBarLine);
|
||||
if (!nextSeg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return nextSeg->pagePos().x() - pagePos().x() - m_spatium;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// bendPitch
|
||||
//---------------------------------------------------------
|
||||
|
||||
int bendTone(int notePitch)
|
||||
{
|
||||
return (notePitch + 12) / 25;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// bendHeight
|
||||
//---------------------------------------------------------
|
||||
|
||||
double StretchedBend::bendHeight(int bendIdx) const
|
||||
{
|
||||
return m_spatium * (bendIdx + 1) * s_bendHeightMultiplier;
|
||||
}
|
||||
}
|
90
src/engraving/libmscore/stretchedbend.h
Normal file
90
src/engraving/libmscore/stretchedbend.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
* MuseScore
|
||||
* Music Composition & Notation
|
||||
*
|
||||
* Copyright (C) 2021 MuseScore BVBA and others
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __STRETCHED_BEND_H__
|
||||
#define __STRETCHED_BEND_H__
|
||||
|
||||
#include "bend.h"
|
||||
|
||||
namespace mu::engraving {
|
||||
class Factory;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// @@ StretchedBend
|
||||
//---------------------------------------------------------
|
||||
|
||||
class StretchedBend final : public Bend
|
||||
{
|
||||
public:
|
||||
StretchedBend* clone() const override { return new StretchedBend(*this); }
|
||||
|
||||
void layout() override;
|
||||
void draw(mu::draw::Painter*) const override;
|
||||
|
||||
static void prepareBends(std::vector<StretchedBend*>& bends);
|
||||
|
||||
private:
|
||||
friend class mu::engraving::Factory;
|
||||
|
||||
StretchedBend(Note* parent);
|
||||
|
||||
void fillSegments(); // converting points from file to bend segments
|
||||
void stretchSegments(); // stretching until end of chord duration
|
||||
void glueNeighbor(); // fixing the double appearance of some bends
|
||||
|
||||
void layoutDraw(const bool layoutMode, mu::draw::Painter* painter = nullptr) const; /// loop for both layout and draw logic
|
||||
void preLayout();
|
||||
void postLayout();
|
||||
|
||||
void setupPainter(mu::draw::Painter* painter) const;
|
||||
void fillArrows();
|
||||
double nextSegmentX() const;
|
||||
double bendHeight(int bendIdx) const;
|
||||
|
||||
bool m_reduntant = false; // marks that the bend was 'glued' to neighbour and is now unnecessary
|
||||
|
||||
enum class BendSegmentType {
|
||||
NO_TYPE = -1,
|
||||
LINE_UP,
|
||||
CURVE_UP,
|
||||
CURVE_DOWN,
|
||||
LINE_STROKED
|
||||
};
|
||||
|
||||
struct BendSegment {
|
||||
PointF src;
|
||||
PointF dest;
|
||||
BendSegmentType type = BendSegmentType::NO_TYPE;
|
||||
int tone = -1;
|
||||
};
|
||||
|
||||
std::vector<BendSegment> m_bendSegments;
|
||||
|
||||
PolygonF m_arrowUp;
|
||||
PolygonF m_arrowDown;
|
||||
double m_spatium = 0;
|
||||
double m_bendArrowWidth = 0;
|
||||
mutable RectF m_boundingRect;
|
||||
bool m_releasedToInitial = false;
|
||||
};
|
||||
} // namespace mu::engraving
|
||||
#endif
|
|
@ -113,6 +113,7 @@ enum class ElementType {
|
|||
HARMONY,
|
||||
FRET_DIAGRAM,
|
||||
BEND,
|
||||
STRETCHED_BEND,
|
||||
TREMOLOBAR,
|
||||
VOLTA,
|
||||
HAIRPIN_SEGMENT,
|
||||
|
|
|
@ -187,6 +187,7 @@ static const std::vector<Item<ElementType> > ELEMENT_TYPES = {
|
|||
{ ElementType::HARMONY, "Harmony", QT_TRANSLATE_NOOP("engraving", "Chord symbol") },
|
||||
{ ElementType::FRET_DIAGRAM, "FretDiagram", QT_TRANSLATE_NOOP("engraving", "Fretboard diagram") },
|
||||
{ ElementType::BEND, "Bend", QT_TRANSLATE_NOOP("engraving", "Bend") },
|
||||
{ ElementType::STRETCHED_BEND, "Bend", QT_TRANSLATE_NOOP("engraving", "Bend") },
|
||||
{ ElementType::TREMOLOBAR, "TremoloBar", QT_TRANSLATE_NOOP("engraving", "Tremolo bar") },
|
||||
{ ElementType::VOLTA, "Volta", QT_TRANSLATE_NOOP("engraving", "Volta") },
|
||||
{ ElementType::HAIRPIN_SEGMENT, "HairpinSegment", QT_TRANSLATE_NOOP("engraving", "Hairpin segment") },
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "libmscore/factory.h"
|
||||
#include "libmscore/arpeggio.h"
|
||||
#include "libmscore/box.h"
|
||||
#include "libmscore/bend.h"
|
||||
#include "libmscore/bracketItem.h"
|
||||
#include "libmscore/clef.h"
|
||||
#include "libmscore/chord.h"
|
||||
|
@ -49,7 +48,7 @@
|
|||
#include "libmscore/tuplet.h"
|
||||
#include "libmscore/volta.h"
|
||||
#include "libmscore/harmonicmark.h"
|
||||
#include "libmscore/excerpt.h"
|
||||
#include "libmscore/stretchedbend.h"
|
||||
|
||||
#include "../importgtp.h"
|
||||
|
||||
|
@ -329,6 +328,11 @@ void GPConverter::convert(const std::vector<std::unique_ptr<GPMasterBar> >& mast
|
|||
}
|
||||
|
||||
addTempoMap();
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend::prepareBends(m_bends);
|
||||
#endif
|
||||
|
||||
addFermatas();
|
||||
addContinuousSlideHammerOn();
|
||||
}
|
||||
|
@ -1676,7 +1680,12 @@ void GPConverter::addBend(const GPNote* gpnote, Note* note)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend* bend = mu::engraving::Factory::createStretchedBend(note);
|
||||
#else
|
||||
Bend* bend = mu::engraving::Factory::createBend(note);
|
||||
#endif
|
||||
|
||||
auto gpBend = gpnote->bend();
|
||||
|
||||
bool bendHasMiddleValue{ true };
|
||||
|
@ -1719,6 +1728,7 @@ void GPConverter::addBend(const GPNote* gpnote, Note* note)
|
|||
|
||||
bend->setTrack(note->track());
|
||||
note->add(bend);
|
||||
m_bends.push_back(bend);
|
||||
}
|
||||
|
||||
void GPConverter::addLineElement(ChordRest* cr, std::vector<TextLineBase*>& elements, ElementType muType, TextLineImportType importType,
|
||||
|
|
|
@ -34,6 +34,7 @@ class LetRing;
|
|||
class PalmMute;
|
||||
class Vibrato;
|
||||
class Ottava;
|
||||
class Bend;
|
||||
|
||||
class GPConverter
|
||||
{
|
||||
|
@ -217,6 +218,13 @@ private:
|
|||
|
||||
Hairpin* _lastHairpin = nullptr;
|
||||
std::vector<Ottava*> m_lastOttavas;
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
std::vector<StretchedBend*> m_bends;
|
||||
#else
|
||||
std::vector<Bend*> m_bends;
|
||||
#endif
|
||||
|
||||
Measure* _lastMeasure = nullptr;
|
||||
bool m_showCapo = true; // TODO-gp : settings
|
||||
};
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "libmscore/arpeggio.h"
|
||||
#include "libmscore/articulation.h"
|
||||
#include "libmscore/barline.h"
|
||||
#include "libmscore/bend.h"
|
||||
#include "libmscore/box.h"
|
||||
#include "libmscore/bracket.h"
|
||||
#include "libmscore/chord.h"
|
||||
|
@ -1136,6 +1135,10 @@ bool GuitarPro4::read(IODevice* io)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend::prepareBends(m_bends);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "libmscore/arpeggio.h"
|
||||
#include "libmscore/articulation.h"
|
||||
#include "libmscore/barline.h"
|
||||
#include "libmscore/bend.h"
|
||||
#include "libmscore/box.h"
|
||||
#include "libmscore/bracket.h"
|
||||
#include "libmscore/chord.h"
|
||||
|
@ -954,6 +953,10 @@ bool GuitarPro5::read(IODevice* io)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend::prepareBends(m_bends);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "libmscore/arpeggio.h"
|
||||
#include "libmscore/articulation.h"
|
||||
#include "libmscore/barline.h"
|
||||
#include "libmscore/bend.h"
|
||||
#include "libmscore/box.h"
|
||||
#include "libmscore/bracket.h"
|
||||
#include "libmscore/bracketItem.h"
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
#include <libmscore/articulation.h>
|
||||
#include <libmscore/keysig.h>
|
||||
#include <libmscore/harmony.h>
|
||||
#include <libmscore/bend.h>
|
||||
#include "libmscore/stretchedbend.h"
|
||||
#include <libmscore/tremolobar.h>
|
||||
#include <libmscore/segment.h>
|
||||
#include <libmscore/rehearsalmark.h>
|
||||
|
@ -759,7 +759,13 @@ void GuitarPro::readBend(Note* note)
|
|||
if (numPoints == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend* bend = Factory::createStretchedBend(note);
|
||||
#else
|
||||
Bend* bend = Factory::createBend(note);
|
||||
#endif
|
||||
|
||||
//TODO-ws bend->setNote(note);
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
int bendTime = readInt();
|
||||
|
@ -770,6 +776,7 @@ void GuitarPro::readBend(Note* note)
|
|||
//TODO-ws bend->setAmplitude(amplitude);
|
||||
bend->setTrack(note->track());
|
||||
note->add(bend);
|
||||
m_bends.push_back(bend);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -2741,6 +2748,11 @@ bool GuitarPro3::read(IODevice* io)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
StretchedBend::prepareBends(m_bends);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,12 @@ protected:
|
|||
QTextCodec* _codec { 0 };
|
||||
Slur** slurs { nullptr };
|
||||
|
||||
#ifdef ENGRAVING_USE_STRETCHED_BENDS
|
||||
std::vector<StretchedBend*> m_bends;
|
||||
#else
|
||||
std::vector<Bend*> m_bends;
|
||||
#endif
|
||||
|
||||
void skip(qint64 len);
|
||||
void read(void* p, qint64 len);
|
||||
int readUChar();
|
||||
|
|
Loading…
Reference in a new issue