Skip to content
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
46 changes: 46 additions & 0 deletions src/engraving/dom/accidental.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,44 @@ double Accidental::subtype2centOffset(AccidentalType st)
return ACC_LIST[int(st)].centOffset;
}

AccidentalType Accidental::centOffset2Subtype(double centOffset)
{
for (int i = 0; i < static_cast<int>(AccidentalType::END); ++i) {
if (muse::RealIsEqual(ACC_LIST[i].centOffset, centOffset)) {
return static_cast<AccidentalType>(i);
}
}

return AccidentalType::NONE;
}

AccidentalType Accidental::value2MicrotonalSubtype(AccidentalVal val, int quarterOff)
{
if (quarterOff == 0) {
return value2subtype(val);
}

if (quarterOff < -1 || quarterOff > 1) {
// TODO: more general implementation
return value2subtype(val);
}

switch (val) {
case AccidentalVal::NATURAL:
return quarterOff == 1 ? AccidentalType::SHARP_ARROW_DOWN : AccidentalType::FLAT_ARROW_UP;
case AccidentalVal::FLAT:
return quarterOff == 1 ? AccidentalType::FLAT_ARROW_UP : AccidentalType::FLAT_ARROW_DOWN;
case AccidentalVal::SHARP:
return quarterOff == 1 ? AccidentalType::SHARP_ARROW_UP : AccidentalType::SHARP_ARROW_DOWN;
case AccidentalVal::FLAT2:
return quarterOff == 1 ? AccidentalType::FLAT2_ARROW_UP : AccidentalType::FLAT2_ARROW_DOWN;
case AccidentalVal::SHARP2:
return quarterOff == 1 ? AccidentalType::SHARP2_ARROW_UP : AccidentalType::SHARP2_ARROW_DOWN;
default:
return value2subtype(val);
}
}

int Accidental::line() const
{
Note* n = note();
Expand Down Expand Up @@ -342,6 +380,14 @@ void Accidental::setSubtype(const AsciiStringView& tag)
setAccidentalType(name2subtype(tag));
}

void Accidental::setAccidentalType(AccidentalType t)
{
m_accidentalType = t;
if (note()) {
note()->setCentOffset(Accidental::subtype2centOffset(t));
}
}

void Accidental::computeMag()
{
double m = explicitParent() ? parentItem()->mag() : 1.0;
Expand Down
4 changes: 3 additions & 1 deletion src/engraving/dom/accidental.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class Accidental final : public EngravingItem
void setSubtype(const AsciiStringView& s);
int subtype() const override { return (int)m_accidentalType; }

void setAccidentalType(AccidentalType t) { m_accidentalType = t; }
void setAccidentalType(AccidentalType t);
AccidentalType accidentalType() const { return m_accidentalType; }

void setRole(AccidentalRole r) { m_role = r; }
Expand Down Expand Up @@ -272,9 +272,11 @@ class Accidental final : public EngravingItem
static SymId subtype2symbol(AccidentalType);
static AsciiStringView subtype2name(AccidentalType);
static AccidentalType value2subtype(AccidentalVal);
static AccidentalType value2MicrotonalSubtype(AccidentalVal val, int quarterOff);
static AccidentalType name2subtype(const AsciiStringView&);
static bool isMicrotonal(AccidentalType t) { return t > AccidentalType::FLAT3; }
static double subtype2centOffset(AccidentalType);
static AccidentalType centOffset2Subtype(double centOffset);

int stackingOrder() const { return ldata()->stackingNumber + m_stackingOrderOffset; }

Expand Down
64 changes: 18 additions & 46 deletions src/engraving/dom/guitarbend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,18 +134,17 @@ void GuitarBend::changeBendAmount(int endBendAmount, int startBendAmount)

// All other bends: set bend amount by transposing end note appropriately
int pitch = endBendAmount / 2 + startNoteOfChain()->pitch();
QuarterOffset quarterOff = endBendAmount % 2 == 1 ? QuarterOffset::QUARTER_SHARP
: endBendAmount % 2 == -1 ? QuarterOffset::QUARTER_FLAT : QuarterOffset::NONE;
if (pitch == startNote()->pitch() && quarterOff == QuarterOffset::QUARTER_SHARP) {
int quarterOff = endBendAmount % 2;
if (pitch == startNote()->pitch() && quarterOff == 1) {
// Because a flat second is more readable than a sharp unison
pitch += 1;
quarterOff = QuarterOffset::QUARTER_FLAT;
quarterOff = -1;
}

setEndNotePitch(pitch, quarterOff);
}

void GuitarBend::setEndNotePitch(int pitch, QuarterOffset quarterOff)
void GuitarBend::setEndNotePitch(int pitch, int quarterToneOffset)
{
Note* note = endNote();
IF_ASSERT_FAILED(note) {
Expand All @@ -170,44 +169,22 @@ void GuitarBend::setEndNotePitch(int pitch, QuarterOffset quarterOff)
tiedNote = tiedNote->tieFor() ? tiedNote->tieFor()->endNote() : nullptr;
}

AccidentalType accidentalType = Accidental::value2subtype(tpc2alter(targetTpc1));
if (quarterOff == QuarterOffset::QUARTER_SHARP) {
switch (accidentalType) {
case AccidentalType::NONE:
case AccidentalType::NATURAL:
accidentalType = AccidentalType::SHARP_ARROW_DOWN;
break;
case AccidentalType::FLAT:
accidentalType = AccidentalType::FLAT_ARROW_UP;
break;
case AccidentalType::SHARP:
accidentalType = AccidentalType::SHARP_ARROW_UP;
break;
default:
break;
}
} else if (quarterOff == QuarterOffset::QUARTER_FLAT) {
switch (accidentalType) {
case AccidentalType::NONE:
case AccidentalType::NATURAL:
accidentalType = AccidentalType::FLAT_ARROW_UP;
break;
case AccidentalType::FLAT:
accidentalType = AccidentalType::FLAT_ARROW_DOWN;
break;
case AccidentalType::SHARP:
accidentalType = AccidentalType::SHARP_ARROW_DOWN;
break;
default:
Note* linkedNoteOnNotationStaff = nullptr;
for (EngravingObject* linked : note->linkList()) {
if (!toNote(linked)->staffType()->isTabStaff()) {
linkedNoteOnNotationStaff = toNote(linked);
break;
}
}

if (accidentalType != note->accidentalType()) {
for (EngravingObject* linked : note->linkList()) {
toNote(linked)->updateLine();
score()->changeAccidental(toNote(linked), accidentalType);
}
if (linkedNoteOnNotationStaff) {
// Manage microtonal by setting appropriate microtonal accidentals, which will propagate to TAB staff too
AccidentalType accidentalType = Accidental::value2MicrotonalSubtype(tpc2alter(targetTpc1), quarterToneOffset);
linkedNoteOnNotationStaff->updateLine();
score()->changeAccidental(linkedNoteOnNotationStaff, accidentalType);
} else {
// Accidental logic doesn't work on TAB, so set cents offset directly
note->undoChangeProperty(Pid::CENT_OFFSET, quarterToneOffset * 50.0);
}

computeBendAmount();
Expand Down Expand Up @@ -489,13 +466,8 @@ void GuitarBend::computeBendAmount()

int pitchDiffInQuarterTones = 2 * (endPitch - startPitch);

Accidental* startAcc = startN->accidental();
Accidental* endAcc = endN->accidental();
int startCentsOff = startAcc ? Accidental::subtype2centOffset(startAcc->accidentalType()) : 0;
int endCentsOff = endAcc ? Accidental::subtype2centOffset(endAcc->accidentalType()) : 0;

int startOffInQuarterTones = round(double(startCentsOff) / 50);
int endOffInQuarterTones = round(double(endCentsOff) / 50);
int startOffInQuarterTones = round(startN->centOffset() / 50);
int endOffInQuarterTones = round(endN->centOffset() / 50);

pitchDiffInQuarterTones += endOffInQuarterTones - startOffInQuarterTones;

Expand Down
8 changes: 1 addition & 7 deletions src/engraving/dom/guitarbend.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@ enum class GuitarBendShowHoldLine : unsigned char {
HIDE,
};

enum class QuarterOffset : unsigned char {
QUARTER_FLAT,
NONE,
QUARTER_SHARP
};

enum class ActionIconType : signed char;

class GuitarBend final : public SLine
Expand Down Expand Up @@ -84,7 +78,7 @@ class GuitarBend final : public SLine

Note* endNote() const;
void changeBendAmount(int bendAmount, int startBendAmount);
void setEndNotePitch(int pitch, QuarterOffset quarterOff = QuarterOffset::NONE);
void setEndNotePitch(int pitch, int quarterToneOffset);

bool isReleaseBend() const;
bool isFullRelease() const;
Expand Down
34 changes: 29 additions & 5 deletions src/engraving/dom/note.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ Note::Note(const Note& n, bool link)
m_ghost = n.m_ghost;
m_deadNote = n.m_deadNote;
m_pitch = n.m_pitch;
m_centOffset = n.m_centOffset;
m_tpc[0] = n.m_tpc[0];
m_tpc[1] = n.m_tpc[1];
m_dotsHidden = n.m_dotsHidden;
Expand Down Expand Up @@ -1289,6 +1290,7 @@ void Note::add(EngravingItem* e)
break;
case ElementType::ACCIDENTAL:
m_accidental = toAccidental(e);
m_centOffset = Accidental::subtype2centOffset(toAccidental(e)->accidentalType());
break;
case ElementType::TEXTLINE:
case ElementType::NOTELINE:
Expand Down Expand Up @@ -1354,6 +1356,7 @@ void Note::remove(EngravingItem* e)

case ElementType::ACCIDENTAL:
m_accidental = 0;
m_centOffset = 0;
break;

case ElementType::TEXTLINE:
Expand Down Expand Up @@ -2132,6 +2135,24 @@ void Note::updateAccidental(AccidentalState* as)
{
int absLine = absStep(tpc(), epitch());

// Ensure m_centOffset and microtonal accidental match (they can mismatch when switching from TAB)
if (muse::RealIsNull(m_centOffset)) {
if (m_accidental && !muse::RealIsNull(Accidental::subtype2centOffset(m_accidental->accidentalType()))) {
score()->undoRemoveElement(m_accidental);
}
} else {
if (m_accidental) {
bool correct = muse::RealIsEqual(Accidental::subtype2centOffset(m_accidental->accidentalType()), m_centOffset);
if (!correct) {
m_accidental->undoChangeProperty(Pid::ACCIDENTAL_TYPE, static_cast<int>(Accidental::centOffset2Subtype(m_centOffset)));
}
} else {
AccidentalType accType = Accidental::value2MicrotonalSubtype(tpc2alter(tpc()), quarterToneOffset());
updateLine();
score()->changeAccidental(this, accType);
}
}

// don't touch accidentals that don't concern tpc such as
// quarter tones
if (!(m_accidental && Accidental::isMicrotonal(m_accidental->accidentalType()))) {
Expand Down Expand Up @@ -2633,11 +2654,7 @@ int Note::playingOctave() const

double Note::playingTuning() const
{
if (!m_accidental) {
return m_tuning;
}

return m_tuning + Accidental::subtype2centOffset(m_accidental->accidentalType());
return m_tuning + m_centOffset;
}

//---------------------------------------------------------
Expand Down Expand Up @@ -2993,6 +3010,8 @@ PropertyValue Note::getProperty(Pid propertyId) const
switch (propertyId) {
case Pid::PITCH:
return pitch();
case Pid::CENT_OFFSET:
return centOffset();
case Pid::TPC1:
return m_tpc[0];
case Pid::TPC2:
Expand Down Expand Up @@ -3058,6 +3077,9 @@ bool Note::setProperty(Pid propertyId, const PropertyValue& v)
setPitch(v.toInt());
score()->setPlaylistDirty();
break;
case Pid::CENT_OFFSET:
setCentOffset(v.toDouble());
break;
case Pid::TPC1:
m_tpc[0] = v.toInt();
break;
Expand Down Expand Up @@ -3174,6 +3196,8 @@ bool Note::setProperty(Pid propertyId, const PropertyValue& v)
PropertyValue Note::propertyDefault(Pid propertyId) const
{
switch (propertyId) {
case Pid::CENT_OFFSET:
return 0.0;
case Pid::GHOST:
case Pid::DEAD:
case Pid::SMALL:
Expand Down
6 changes: 6 additions & 0 deletions src/engraving/dom/note.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ class Note final : public EngravingItem
void setPitch(int val, bool notifyAboutChanged = true);
void setPitch(int pitch, int tpc1, int tpc2);
int pitch() const { return m_pitch; }

double centOffset() const { return m_centOffset; }
void setCentOffset(double v) { m_centOffset = v; }
int quarterToneOffset() const { return std::round(m_centOffset / 50); }

int ottaveCapoFret() const;
int linkedOttavaPitchOffset() const;
int ppitch() const; // playback pitch
Expand Down Expand Up @@ -528,6 +533,7 @@ class Note final : public EngravingItem
int m_string = -1;
mutable int m_tpc[2] = { Tpc::TPC_INVALID, Tpc::TPC_INVALID }; // tonal pitch class (concert/transposing)
mutable int m_pitch = 0; // Note pitch as midi value (0 - 127).
mutable double m_centOffset = 0.0; // Pitch offset in cents (100 cents = 1 semitone)

int m_userVelocity = 0; // velocity user offset in percent, or absolute velocity for this note
int m_fixedLine = 0; // fixed line number if _fixed == true
Expand Down
1 change: 1 addition & 0 deletions src/engraving/dom/property.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static constexpr PropertyMetaData propertyList[] = {
{ Pid::KEYSIG_MODE, P_TYPE::KEY_MODE, PropertyGroup::APPEARANCE, false, "keysig_mode", QT_TRANSLATE_NOOP("engraving/propertyName", "key signature mode") },
{ Pid::SLUR_STYLE_TYPE, P_TYPE::SLUR_STYLE_TYPE, PropertyGroup::APPEARANCE, false, "lineType", QT_TRANSLATE_NOOP("engraving/propertyName", "line type") },
{ Pid::PITCH, P_TYPE::INT, PropertyGroup::NONE, true, "pitch", QT_TRANSLATE_NOOP("engraving/propertyName", "pitch") },
{ Pid::CENT_OFFSET, P_TYPE::REAL, PropertyGroup::NONE, true, "centOffset", QT_TRANSLATE_NOOP("engraving/propertyName", "cent offset") },

{ Pid::TPC1, P_TYPE::INT, PropertyGroup::NONE, true, "tpc", QT_TRANSLATE_NOOP("engraving/propertyName", "tonal pitch class") },
{ Pid::TPC2, P_TYPE::INT, PropertyGroup::NONE, true, "tpc2", QT_TRANSLATE_NOOP("engraving/propertyName", "transposed tonal pitch class") },
Expand Down
1 change: 1 addition & 0 deletions src/engraving/dom/property.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ enum class Pid : short {
KEYSIG_MODE,
SLUR_STYLE_TYPE,
PITCH,
CENT_OFFSET,

TPC1,
TPC2,
Expand Down
1 change: 1 addition & 0 deletions src/engraving/rw/read460/tread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3303,6 +3303,7 @@ bool TRead::readProperties(Note* n, XmlReader& e, ReadContext& ctx)

if (tag == "pitch") {
n->setPitch(clampPitch(e.readInt()), false);
} else if (TRead::readProperty(n, tag, e, ctx, Pid::CENT_OFFSET)) {
} else if (tag == "tpc") {
int tpc = e.readInt();
n->setTpc1(tpc);
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/rw/write/twrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2370,7 +2370,7 @@ void TWrite::write(const Note* item, XmlWriter& xml, WriteContext& ctx)
}
xml.endElement();
}
for (Pid id : { Pid::PITCH, Pid::TPC1, Pid::TPC2, Pid::SMALL, Pid::MIRROR_HEAD, Pid::DOT_POSITION,
for (Pid id : { Pid::PITCH, Pid::CENT_OFFSET, Pid::TPC1, Pid::TPC2, Pid::SMALL, Pid::MIRROR_HEAD, Pid::DOT_POSITION,
Pid::HEAD_SCHEME, Pid::HEAD_GROUP, Pid::USER_VELOCITY, Pid::PLAY, Pid::TUNING, Pid::FRET, Pid::STRING,
Pid::GHOST, Pid::DEAD, Pid::HEAD_TYPE, Pid::FIXED, Pid::FIXED_LINE }) {
writeProperty(item, xml, id);
Expand Down
Loading
Loading