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

A trip to Bendsville #19746

Merged
merged 2 commits into from Oct 25, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file modified fonts/mscore/MusescoreIcon.ttf
Binary file not shown.
5 changes: 5 additions & 0 deletions src/engraving/dom/actionicon.h
Expand Up @@ -57,6 +57,11 @@ enum class ActionIconType {

PARENTHESES,
BRACKETS,

STANDARD_BEND,
PRE_BEND,
GRACE_NOTE_BEND,
SLIGHT_BEND,
};

//! Dummy element, used for drag&drop
Expand Down
6 changes: 6 additions & 0 deletions src/engraving/dom/bend.h
Expand Up @@ -36,6 +36,12 @@ class Factory;
// @@ Bend
//---------------------------------------------------------

/**********************************************************
* OBSOLETE CLASS
* Used to represent bends before version 4.2. Now
* replaced by the GuitarBend class.
*********************************************************/

enum class BendType {
BEND = 0,
BEND_RELEASE,
Expand Down
77 changes: 75 additions & 2 deletions src/engraving/dom/chord.cpp
Expand Up @@ -35,6 +35,7 @@
#include "chordline.h"
#include "drumset.h"
#include "factory.h"
#include "guitarbend.h"
#include "hook.h"
#include "key.h"
#include "ledgerline.h"
Expand Down Expand Up @@ -2302,7 +2303,7 @@ void Chord::setSlash(bool flag, bool stemless)
// end into this chord or no.
//---------------------------------------------------------

void Chord::updateEndsGlissando()
void Chord::updateEndsGlissandoOrGuitarBend()
{
m_endsGlissando = false; // assume no glissando ends here
// scan all chord notes for glissandi ending on this chord
Expand Down Expand Up @@ -2357,7 +2358,11 @@ double Chord::intrinsicMag() const
m *= style().styleD(Sid::smallNoteMag);
}
if (m_noteType != NoteType::NORMAL) {
m *= style().styleD(Sid::graceNoteMag);
bool tabGraceSizeException = staffType()->isTabStaff() && isPreBendOrGraceBendStart()
&& !style().styleB(Sid::useCueSizeFretForGraceBends);
if (!tabGraceSizeException) {
m *= style().styleD(Sid::graceNoteMag);
}
}
return m;
}
Expand Down Expand Up @@ -2551,6 +2556,73 @@ void Chord::toGraceAfter()
}
}

bool Chord::isPreBendOrGraceBendStart() const
{
if (!isGrace()) {
return false;
}

for (const Note* note : m_notes) {
GuitarBend* gb = note->bendFor();
if (gb && (gb->type() == GuitarBendType::PRE_BEND || gb->type() == GuitarBendType::GRACE_NOTE_BEND)) {
return true;
}
}

return false;
}

bool Chord::preOrGraceBendSpacingExceptionInTab() const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain the purpose of this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Normally, grace notes are placed to the left of their main note (like in the normal staff here). In TABs however, because the main note isn't actually plucked when playing these bends, there are cases where the grace note should overlap the main note, which is why I need this spacing exception.

{
if (!staffType()->isTabStaff() || !isGrace()) {
return false;
}

std::vector<GuitarBend*> bends;
for (Note* note : m_notes) {
GuitarBend* bendFor = note->bendFor();
if (bendFor) {
GuitarBendType bendType = bendFor->type();
if (bendType == GuitarBendType::PRE_BEND || bendType == GuitarBendType::GRACE_NOTE_BEND) {
bends.push_back(bendFor);
break;
}
}
}

if (bends.empty() || bends.size() < m_notes.size()) {
return false;
}

Chord* endChord = bends.front()->endNote()->chord();
if (!endChord) {
return false;
}

GuitarBendType type = bends.front()->type();
for (GuitarBend* gb : bends) {
if (gb->type() != type || (gb->endNote() && gb->endNote()->chord() != endChord)) {
return false;
}
}

if (type == GuitarBendType::PRE_BEND) {
return true;
}

for (Note* note : endChord->notes()) {
GuitarBend* bendBack = note->bendBack();
if (bendBack) {
Note* startNote = bendBack->startNote();
if (!startNote || startNote->chord() != this) {
return false;
}
}
}

return bends.size() < endChord->notes().size();
}

//---------------------------------------------------------
// tremoloChordType
//---------------------------------------------------------
Expand Down Expand Up @@ -2603,6 +2675,7 @@ EngravingItem* Chord::nextElement()
break;
}

case ElementType::GUITAR_BEND_SEGMENT:
case ElementType::GLISSANDO_SEGMENT:
case ElementType::TIE_SEGMENT: {
SpannerSegment* s = toSpannerSegment(e);
Expand Down
9 changes: 6 additions & 3 deletions src/engraving/dom/chord.h
Expand Up @@ -166,9 +166,9 @@ class Chord final : public ChordRest
void setTremolo(Tremolo* t, bool applyLogic = true);

ChordLine* chordLine() const;
bool endsGlissando() const { return m_endsGlissando; }
void setEndsGlissando(bool val) { m_endsGlissando = val; }
void updateEndsGlissando();
bool endsGlissandoOrGuitarBend() const { return m_endsGlissando; }
void setEndsGlissandoOrGuitarBend(bool val) { m_endsGlissando = val; }
void updateEndsGlissandoOrGuitarBend();
StemSlash* stemSlash() const { return m_stemSlash; }
bool slash();
void setSlash(bool flag, bool stemless);
Expand Down Expand Up @@ -209,6 +209,9 @@ class Chord final : public ChordRest
bool isGrace() const { return m_noteType != NoteType::NORMAL; }
void toGraceAfter();

bool isPreBendOrGraceBendStart() const;
bool preOrGraceBendSpacingExceptionInTab() const;

void setTrack(track_idx_t val) override;

double dotPosX() const { return m_dotPosX; }
Expand Down
122 changes: 122 additions & 0 deletions src/engraving/dom/cmd.cpp
Expand Up @@ -43,6 +43,8 @@
#include "drumset.h"
#include "dynamic.h"
#include "factory.h"
#include "glissando.h"
#include "guitarbend.h"
#include "hairpin.h"
#include "harmony.h"
#include "key.h"
Expand Down Expand Up @@ -834,6 +836,126 @@ Note* Score::setGraceNote(Chord* ch, int pitch, NoteType type, int len)
return note;
}

Note* Score::addEndNoteForBend(Note* startNote)
{
track_idx_t track = startNote->track();
Chord* startChord = startNote->chord();
Segment* startSegment = startChord->segment();

Segment* endSegment = startSegment->nextCR(track);
if (!endSegment) {
return nullptr;
}

EngravingItem* item = endSegment->elementAt(track);
if (!item || !item->isRest()) {
return nullptr;
}

Rest* rest = toRest(item);
Fraction duration = std::min(startChord->ticks(), rest->ticks());
NoteVal noteVal = startNote->noteVal();

endSegment = setNoteRest(endSegment, track, noteVal, duration);
Chord* endChord = endSegment ? toChord(endSegment->elementAt(track)) : nullptr;
Note* endNote = endChord ? endChord->upNote() : nullptr;
if (endNote) {
endNote->transposeDiatonic(1, true, false);
}

return endNote;
}

GuitarBend* Score::addGuitarBend(GuitarBendType type, Note* note, Note* endNote)
{
if (note->isPreBendStart()) {
return nullptr;
}

if (note->isGraceBendStart() && type != GuitarBendType::PRE_BEND) {
return nullptr;
}

Chord* chord = note->chord();

if (type == GuitarBendType::BEND) {
for (Spanner* sp : note->spannerFor()) {
if (sp->isGuitarBend() || sp->isGlissando()) {
return nullptr;
}
}

if (!endNote) {
endNote = Glissando::guessFinalNote(chord, note);
}

if (!endNote) {
endNote = addEndNoteForBend(note);
}

if (!endNote) {
return nullptr;
}
}

GuitarBend* bend = new GuitarBend(score()->dummy()->note());
bend->setAnchor(Spanner::Anchor::NOTE);
bend->setTick(chord->tick());
bend->setTrack(chord->track());

if (type == GuitarBendType::BEND) {
bend->setType(chord->isGrace() ? GuitarBendType::GRACE_NOTE_BEND : type);
bend->setStartElement(note);
bend->setTick2(endNote->tick());
bend->setTrack2(endNote->track());
bend->setEndElement(endNote);
bend->setParent(note);
GuitarBend::fixNotesFrettingForStandardBend(note, endNote);
} else {
bend->setType(type);
bend->setTick2(chord->tick());
bend->setTrack2(chord->track());

if (type == GuitarBendType::PRE_BEND || type == GuitarBendType::GRACE_NOTE_BEND) {
// Create grace note
Note* graceNote = setGraceNote(chord, note->pitch(), NoteType::APPOGGIATURA, Constants::DIVISION / 2);
graceNote->transposeDiatonic(-1, true, false);
GuitarBend::fixNotesFrettingForGraceBend(graceNote, note);

Chord* graceChord = graceNote->chord();
for (EngravingObject* item : graceChord->linkList()) {
Chord* linkedGrace = toChord(item);
linkedGrace->undoChangeProperty(Pid::NO_STEM, true);
linkedGrace->undoChangeProperty(Pid::BEAM_MODE, BeamMode::NONE);
}

// Add bend
bend->setParent(graceNote);
bend->setStartElement(graceNote);
bend->setEndElement(note);
} else if (type == GuitarBendType::SLIGHT_BEND) {
bend->setParent(note);
bend->setStartElement(note);
// Slight bends don't end on another note
bend->setEndElement(note);
}
}

Chord* startChord = bend->startNote()->chord();
if (startChord->isGrace()) {
for (EngravingObject* item : startChord->linkList()) {
Chord* linkedGrace = toChord(item);
if (linkedGrace->staffType()->isTabStaff()) {
linkedGrace->undoChangeProperty(Pid::NO_STEM, true);
linkedGrace->undoChangeProperty(Pid::BEAM_MODE, BeamMode::NONE);
}
}
}

score()->undoAddElement(bend);
return bend;
}

//---------------------------------------------------------
// createCRSequence
// Create a rest or chord of len f.
Expand Down
2 changes: 2 additions & 0 deletions src/engraving/dom/dom.cmake
Expand Up @@ -121,6 +121,8 @@ set(DOM_SRC
${CMAKE_CURRENT_LIST_DIR}/gradualtempochange.h
${CMAKE_CURRENT_LIST_DIR}/groups.cpp
${CMAKE_CURRENT_LIST_DIR}/groups.h
${CMAKE_CURRENT_LIST_DIR}/guitarbend.cpp
${CMAKE_CURRENT_LIST_DIR}/guitarbend.h
${CMAKE_CURRENT_LIST_DIR}/hairpin.cpp
${CMAKE_CURRENT_LIST_DIR}/hairpin.h
${CMAKE_CURRENT_LIST_DIR}/harmonicmark.cpp
Expand Down
10 changes: 6 additions & 4 deletions src/engraving/dom/edit.cpp
Expand Up @@ -2798,6 +2798,7 @@ void Score::deleteItem(EngravingItem* el)
case ElementType::RASGUEADO_SEGMENT:
case ElementType::HARMONIC_MARK_SEGMENT:
case ElementType::PICK_SCRAPE_SEGMENT:
case ElementType::GUITAR_BEND_SEGMENT:
{
el = toSpannerSegment(el)->spanner();
undoRemoveElement(el);
Expand Down Expand Up @@ -5866,6 +5867,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
|| et == ElementType::NOTE
|| et == ElementType::TEXT
|| et == ElementType::GLISSANDO
|| et == ElementType::GUITAR_BEND
|| et == ElementType::BEND
|| (et == ElementType::CHORD && toChord(element)->isGrace())
) {
Expand All @@ -5879,7 +5881,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
links = 0;
}
}
if (links == 0) {
if (links == 0 || !addToLinkedStaves) {
undo(new AddElement(element));
return;
}
Expand All @@ -5889,8 +5891,8 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
if (e == parent) {
ne = element;
} else {
if (element->isGlissando()) { // and other spanners with Anchor::NOTE
Note* newEnd = Spanner::endElementFromSpanner(toGlissando(element), e);
if (element->isGlissando() || element->isGuitarBend()) { // and other spanners with Anchor::NOTE
Note* newEnd = Spanner::endElementFromSpanner(toSpanner(element), e);
if (newEnd) {
ne = element->linkedClone();
toSpanner(ne)->setNoteSpan(toNote(e), newEnd);
Expand Down Expand Up @@ -6267,7 +6269,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool
}
}
undo(new AddElement(nsp));
} else if (et == ElementType::GLISSANDO) {
} else if (et == ElementType::GLISSANDO || et == ElementType::GUITAR_BEND) {
undo(new AddElement(toSpanner(ne)));
} else if (element->isTremolo() && toTremolo(element)->twoNotes()) {
Tremolo* tremolo = toTremolo(element);
Expand Down
6 changes: 5 additions & 1 deletion src/engraving/dom/engravingitem.cpp
Expand Up @@ -1298,7 +1298,11 @@ PropertyPropagation EngravingItem::propertyPropagation(const EngravingItem* dest
const Score* destinationScore = destinationItem->score();
const bool isTextProperty = propertyGroup(propertyId) == PropertyGroup::TEXT;

if ((isTextProperty && isPropertyLinkedToMaster(propertyId)) || sourceScore == destinationScore) {
if (propertyGroup(propertyId) != PropertyGroup::TEXT && sourceScore == destinationScore) {
return PropertyPropagation::NONE;
}

if ((isTextProperty && isPropertyLinkedToMaster(propertyId))) {
return PropertyPropagation::PROPAGATE;
}

Expand Down
2 changes: 1 addition & 1 deletion src/engraving/dom/engravingobject.cpp
Expand Up @@ -729,7 +729,7 @@ bool EngravingObject::isTextBase() const
|| type() == ElementType::MMREST_RANGE
|| type() == ElementType::STICKING
|| type() == ElementType::HARP_DIAGRAM
;
|| type() == ElementType::GUITAR_BEND_TEXT;
}

//---------------------------------------------------------
Expand Down