Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guitar bends widget #19871

Merged
merged 5 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/engraving/dom/guitarbend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
#include "note.h"
#include "part.h"
#include "score.h"
#include "staff.h"
#include "tie.h"
#include "utils.h"

namespace mu::engraving {
/****************************************
Expand Down Expand Up @@ -69,6 +71,18 @@ Note* GuitarBend::startNote() const
return toNote(startEl);
}

Note* GuitarBend::startNoteOfChain() const
{
Note* startOfChain = startNote();
GuitarBend* prevBend = findPrecedingBend();
while (startOfChain && prevBend) {
startOfChain = prevBend->startNote();
prevBend = prevBend->findPrecedingBend();
}

return startOfChain;
}

Note* GuitarBend::endNote() const
{
EngravingItem* endEl = endElement();
Expand All @@ -78,6 +92,34 @@ Note* GuitarBend::endNote() const
return toNote(endEl);
}

void GuitarBend::setEndNotePitch(int pitch)
{
Note* note = endNote();
IF_ASSERT_FAILED(note) {
return;
}

if (note->pitch() == pitch) {
return;
}

Fraction tick = note->tick();
Staff* staff = note->staff();

Key key = staff->key(tick);
Interval interval = staff->transpose(tick);
interval.flip();

int targetTpc1 = pitch2tpc(pitch, key, Prefer::NEAREST);
int targetTpc2 = transposeTpc(targetTpc1, interval, true);

score()->undoChangePitch(note, pitch, targetTpc1, targetTpc2);

computeBendAmount();

triggerLayout();
}

bool GuitarBend::isReleaseBend() const
{
return endNote()->pitch() < startNote()->pitch();
Expand Down
4 changes: 4 additions & 0 deletions src/engraving/dom/guitarbend.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ class GuitarBend final : public SLine
LineSegment* createLineSegment(System* parent) override;

Note* startNote() const;
Note* startNoteOfChain() const;

Note* endNote() const;
void setEndNotePitch(int pitch);

bool isReleaseBend() const;
bool isFullRelease() const;

Expand Down
2 changes: 2 additions & 0 deletions src/inspector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/models/text/textsettingsmodel.h
${CMAKE_CURRENT_LIST_DIR}/view/widgets/fretcanvas.cpp
${CMAKE_CURRENT_LIST_DIR}/view/widgets/fretcanvas.h
${CMAKE_CURRENT_LIST_DIR}/view/widgets/bendgridcanvas.cpp
${CMAKE_CURRENT_LIST_DIR}/view/widgets/bendgridcanvas.h
${CMAKE_CURRENT_LIST_DIR}/view/widgets/gridcanvas.cpp
${CMAKE_CURRENT_LIST_DIR}/view/widgets/gridcanvas.h
${CMAKE_CURRENT_LIST_DIR}/models/notation/notationsettingsproxymodel.cpp
Expand Down
3 changes: 2 additions & 1 deletion src/inspector/inspectormodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "models/inspectorpopupcontroller.h"

#include "view/widgets/fretcanvas.h"
#include "view/widgets/bendgridcanvas.h"
#include "view/widgets/gridcanvas.h"

#include "types/directiontypes.h"
Expand Down Expand Up @@ -109,8 +110,8 @@ void InspectorModule::registerUiTypes()
qmlRegisterUncreatableType<AmbitusTypes>("MuseScore.Inspector", 1, 0, "AmbitusTypes", "Not creatable as it is an enum type");
qmlRegisterUncreatableType<ChordSymbolTypes>("MuseScore.Inspector", 1, 0, "ChordSymbolTypes", "Not creatable as it is an enum type");
qmlRegisterType<FretCanvas>("MuseScore.Inspector", 1, 0, "FretCanvas");
qmlRegisterType<BendGridCanvas>("MuseScore.Inspector", 1, 0, "BendGridCanvas");
qmlRegisterType<GridCanvas>("MuseScore.Inspector", 1, 0, "GridCanvas");
qmlRegisterUncreatableType<BendTypes>("MuseScore.Inspector", 1, 0, "BendTypes", "Not creatable as it is an enum type");
qmlRegisterUncreatableType<TremoloBarTypes>("MuseScore.Inspector", 1, 0, "TremoloBarTypes", "Not creatable as it is an enum type");
qmlRegisterUncreatableType<TremoloTypes>("MuseScore.Inspector", 1, 0, "TremoloTypes", "Not creatable as it is an enum type");
qmlRegisterType<InspectorPopupController>("MuseScore.Inspector", 1, 0, "InspectorPopupController");
Expand Down
2 changes: 2 additions & 0 deletions src/inspector/models/abstractinspectormodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ static const QMap<mu::engraving::ElementType, InspectorModelType> NOTATION_ELEME
{ mu::engraving::ElementType::BEND, InspectorModelType::TYPE_BEND },
{ mu::engraving::ElementType::GUITAR_BEND, InspectorModelType::TYPE_BEND },
{ mu::engraving::ElementType::GUITAR_BEND_SEGMENT, InspectorModelType::TYPE_BEND },
{ mu::engraving::ElementType::GUITAR_BEND_HOLD, InspectorModelType::TYPE_BEND },
{ mu::engraving::ElementType::GUITAR_BEND_HOLD_SEGMENT, InspectorModelType::TYPE_BEND },
{ mu::engraving::ElementType::TREMOLOBAR, InspectorModelType::TYPE_TREMOLOBAR },
{ mu::engraving::ElementType::TREMOLO, InspectorModelType::TYPE_TREMOLO },
{ mu::engraving::ElementType::MEASURE_REPEAT, InspectorModelType::TYPE_MEASURE_REPEAT },
Expand Down
187 changes: 161 additions & 26 deletions src/inspector/models/notation/bends/bendsettingsmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,71 +26,68 @@
#include "dataformatter.h"
#include "types/bendtypes.h"

#include "dom/bend.h"

using namespace mu::inspector;

static constexpr int ACTIVE_POINT_INDEX = 2;

static std::set<mu::engraving::ElementType> ELEMENTS_TYPES = {
engraving::ElementType::GUITAR_BEND,
engraving::ElementType::GUITAR_BEND_SEGMENT,
engraving::ElementType::GUITAR_BEND_HOLD,
engraving::ElementType::GUITAR_BEND_HOLD_SEGMENT
};

BendSettingsModel::BendSettingsModel(QObject* parent, IElementRepositoryService* repository)
: AbstractInspectorModel(parent, repository)
{
setModelType(InspectorModelType::TYPE_BEND);
setTitle(qtrc("inspector", "Bend"));
setIcon(ui::IconCode::Code::GUITAR_BEND);

createProperties();
}

void BendSettingsModel::createProperties()
{
m_bendType = buildPropertyItem(mu::engraving::Pid::BEND_TYPE, [this](const mu::engraving::Pid pid, const QVariant& newValue) {
onPropertyValueChanged(pid, newValue);

if (newValue.toInt() != static_cast<int>(BendTypes::BendType::TYPE_CUSTOM)) {
emit requestReloadPropertyItems();
}
});

m_bendCurve = buildPropertyItem(mu::engraving::Pid::BEND_CURVE, [this](const mu::engraving::Pid pid, const QVariant& newValue) {
onPropertyValueChanged(pid, newValue);

emit requestReloadPropertyItems();
});

m_lineThickness = buildPropertyItem(mu::engraving::Pid::LINE_WIDTH);

m_bendDirection = buildPropertyItem(mu::engraving::Pid::DIRECTION);
m_showHoldLine = buildPropertyItem(mu::engraving::Pid::BEND_SHOW_HOLD_LINE);

loadBendCurve();
}

void BendSettingsModel::requestElements()
{
m_elementList = m_repository->findElementsByType(mu::engraving::ElementType::GUITAR_BEND_SEGMENT);
m_elementList.clear();

for (engraving::ElementType type : ELEMENTS_TYPES) {
m_elementList << m_repository->findElementsByType(type);
}

emit areSettingsAvailableChanged(areSettingsAvailable());
}

void BendSettingsModel::loadProperties()
{
loadPropertyItem(m_bendType);
loadPropertyItem(m_bendCurve);
loadPropertyItem(m_lineThickness, formatDoubleFunc);
loadPropertyItem(m_bendDirection);
loadPropertyItem(m_showHoldLine);

updateIsShowHoldLineAvailable();

loadBendCurve();
}

void BendSettingsModel::resetProperties()
{
m_bendType->resetToDefault();
m_bendCurve->resetToDefault();
m_lineThickness->resetToDefault();
m_bendDirection->resetToDefault();
m_showHoldLine->resetToDefault();
}

PropertyItem* BendSettingsModel::bendType() const
{
return m_bendType;
}

PropertyItem* BendSettingsModel::lineThickness() const
{
return m_lineThickness;
Expand All @@ -105,6 +102,10 @@ void BendSettingsModel::updateIsShowHoldLineAvailable()
{
bool available = true;
for (EngravingItem* item : m_elementList) {
if (!item->isGuitarBendSegment()) {
continue;
}

GuitarBendSegment* seg = toGuitarBendSegment(item);
if (seg->staffType() && !seg->staffType()->isTabStaff()) {
available = false;
Expand All @@ -118,9 +119,103 @@ void BendSettingsModel::updateIsShowHoldLineAvailable()
}
}

PropertyItem* BendSettingsModel::bendCurve() const
void BendSettingsModel::loadBendCurve()
{
EngravingItem* item = this->item();
if (!item) {
return;
}

GuitarBend* bend = guitarBend(item);
if (!bend) {
return;
}

int totBendAmount = bend->totBendAmountIncludingPrecedingBends();
int totFulls = totBendAmount / 4;
int totQuarts = totBendAmount % 4;
int endPitch = totFulls * 100 + totQuarts * 25;

int localBendAmount = bend->bendAmountInQuarterTones();
int fulls = localBendAmount / 4;
int quarts = localBendAmount % 4;
int pitchDiff = fulls * 100 + quarts * 25;
int startPitch = endPitch - pitchDiff;

bool isHold = this->isHold(item);
if (isHold) {
m_bendCurve = {
CurvePoint(0, endPitch, true),
CurvePoint(CurvePoint::MAX_TIME, endPitch, {}, true)
};

emit bendCurveChanged();

return;
}

if (bend->type() == engraving::GuitarBendType::PRE_BEND) {
m_bendCurve = { CurvePoint(0, 0, true),
CurvePoint(0, endPitch, true),
CurvePoint(CurvePoint::MAX_TIME, endPitch, { CurvePoint::MoveDirection::Vertical }, true) };
} else if (bend->isReleaseBend()) {
m_bendCurve = { CurvePoint(0, startPitch, true),
CurvePoint(0, startPitch, { CurvePoint::MoveDirection::Horizontal }, true),
CurvePoint(15, endPitch, { CurvePoint::MoveDirection::Both }),
CurvePoint(CurvePoint::MAX_TIME, endPitch, {}, true, true) };
} else {
m_bendCurve = { CurvePoint(0, startPitch, true),
CurvePoint(0, startPitch, { CurvePoint::MoveDirection::Horizontal }, true),
CurvePoint(15, endPitch, { CurvePoint::MoveDirection::Both }),
CurvePoint(CurvePoint::MAX_TIME, endPitch, {}, true, true) };
}

emit bendCurveChanged();
}

EngravingItem* BendSettingsModel::item() const
{
for (EngravingItem* item : m_elementList) {
if (contains(ELEMENTS_TYPES, item->type())) {
return item;
}
}

return nullptr;
}

bool BendSettingsModel::isHold(const EngravingItem* item) const
{
return m_bendCurve;
if (!item) {
return false;
}

return item->isGuitarBendHold() || item->isGuitarBendHoldSegment();
}

GuitarBend* BendSettingsModel::guitarBend(EngravingItem* item) const
{
if (item->isGuitarBend()) {
return toGuitarBend(item);
} else if (item->isGuitarBendHold()) {
return toGuitarBendHold(item)->guitarBend();
} else if (item->isGuitarBendHoldSegment()) {
return toGuitarBendHoldSegment(item)->guitarBendHold()->guitarBend();
} else if (item->isGuitarBendSegment()) {
return toGuitarBendSegment(item)->guitarBend();
}

return nullptr;
}

QVariantList BendSettingsModel::bendCurve() const
{
QVariantList result;
for (const CurvePoint& point : m_bendCurve) {
result << point.toMap();
}

return result;
}

PropertyItem* BendSettingsModel::bendDirection() const
Expand All @@ -137,3 +232,43 @@ bool BendSettingsModel::isShowHoldLineAvailable() const
{
return m_isShowHoldLineAvailable;
}

void BendSettingsModel::setBendCurve(const QVariantList& newBendCurve)
{
CurvePoints points = curvePointsFromQVariant(newBendCurve);

if (m_bendCurve == points) {
return;
}

if (points.empty()) {
return;
}

EngravingItem* item = this->item();
if (!item) {
return;
}

GuitarBend* bend = guitarBend(item);
if (!bend) {
return;
}

int newPitch = points[ACTIVE_POINT_INDEX].pitch;
int fulls = newPitch / 100;
int quarts = (newPitch % 100) / 25;
int bendAmount = fulls * 4 + quarts;

int pitch = bendAmount / 2 + bend->startNoteOfChain()->pitch();

beginCommand();
bend->setEndNotePitch(pitch);
// todo set time
endCommand();

updateNotation();

m_bendCurve = points;
emit bendCurveChanged();
}
Loading
Loading