Merge pull request #11391 from alexpavlov96/gp_remove_strings_recalculation

fix #10311: fixing the calculation of harmonic frets
This commit is contained in:
Alexander Pavlov 2022-05-06 15:05:36 +03:00 committed by GitHub
commit 269f001b10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 241 additions and 132 deletions

View file

@ -581,6 +581,8 @@ Note::Note(const Note& n, bool link)
_subchannel = n._subchannel;
_line = n._line;
_fret = n._fret;
m_harmonicFret = n.m_harmonicFret;
m_displayFret = n.m_displayFret;
_string = n._string;
_fretConflict = n._fretConflict;
_ghost = n._ghost;
@ -1312,6 +1314,9 @@ void Note::draw(mu::draw::Painter* painter) const
// tablature
if (tablature) {
if (m_displayFret == DisplayFretOption::Hide) {
return;
}
const Staff* st = staff();
const StaffType* tab = st->staffTypeForElement(this);
if (tieBack() && !tab->showBackTied()) {
@ -2169,6 +2174,10 @@ void Note::layout()
{
bool useTablature = staff() && staff()->isTabStaff(chord()->tick());
if (useTablature) {
if (m_displayFret == DisplayFretOption::Hide) {
return;
}
const Staff* st = staff();
const StaffType* tab = st->staffTypeForElement(this);
qreal mags = magS();
@ -2186,8 +2195,10 @@ void Note::layout()
_fretString = "/";
} else {
_fretString = tab->fretString(_fret, _string, _deadNote);
if (_harmonic) {
_fretString = QString("<%1>").arg(_fretString);
if (m_displayFret == DisplayFretOption::ArtificialHarmonic) {
_fretString = QString("%1 <%2>").arg(_fretString, QString::number(m_harmonicFret));
} else if (m_displayFret == DisplayFretOption::NaturalHarmonic) {
_fretString = QString("<%1>").arg(QString::number(m_harmonicFret));
}
}
if (parenthesis) {

View file

@ -152,56 +152,65 @@ public:
Note* endNote = nullptr; // note to end slide (for 2 notes slides)
bool isValid() const { return type != SlideType::Undefined; }
bool is(SlideType t) const { return t == type; }
uint32_t slideToNoteLenght{ 40 };
uint32_t slideToNoteLenght = 40;
};
enum DisplayFretOption {
Hide = -1,
NoHarmonic,
NaturalHarmonic,
ArtificialHarmonic
};
private:
bool _ghost { false }; ///< ghost note
bool _deadNote { false }; ///< dead note
bool _hidden { false }; ///< marks this note as the hidden one if there are
bool _ghost = false; ///< ghost note
bool _deadNote = false; ///< dead note
bool _hidden = false; ///< marks this note as the hidden one if there are
///< overlapping notes; hidden notes are not played
///< and heads + accidentals are not shown
bool _dotsHidden { false }; ///< dots of hidden notes are hidden too
///< except if only one note is dotted
bool _fretConflict { false }; ///< used by TAB staves to mark a fretting conflict:
///< two or more notes on the same string
bool dragMode { false };
bool _mirror { false }; ///< True if note is mirrored at stem.
bool m_isSmall { false };
bool _play { true }; // note is not played if false
mutable bool _mark { false }; // for use in sequencer
bool _fixed { false }; // for slash notation
bool _dotsHidden = false; ///< dots of hidden notes are hidden too
///< except if only one note is dotted
bool _fretConflict = false; ///< used by TAB staves to mark a fretting conflict:
///< two or more notes on the same string
bool dragMode = false;
bool _mirror = false; ///< True if note is mirrored at stem.
bool m_isSmall = false;
bool _play = true; ///< note is not played if false
mutable bool _mark = false; ///< for use in sequencer
bool _fixed = false; ///< for slash notation
DirectionH _userMirror { DirectionH::AUTO }; ///< user override of mirror
DirectionV _userDotPosition { DirectionV::AUTO }; ///< user override of dot position
DirectionH _userMirror = DirectionH::AUTO; ///< user override of mirror
DirectionV _userDotPosition = DirectionV::AUTO; ///< user override of dot position
NoteHeadScheme _headScheme { NoteHeadScheme::HEAD_AUTO };
NoteHeadGroup _headGroup { NoteHeadGroup::HEAD_NORMAL };
NoteHeadType _headType { NoteHeadType::HEAD_AUTO };
NoteHeadScheme _headScheme = NoteHeadScheme::HEAD_AUTO;
NoteHeadGroup _headGroup = NoteHeadGroup::HEAD_NORMAL;
NoteHeadType _headType = NoteHeadType::HEAD_AUTO;
VeloType _veloType { VeloType::OFFSET_VAL };
VeloType _veloType = VeloType::OFFSET_VAL;
int _offTimeType { 0 }; // compatibility only 1 - user(absolute), 2 - offset (%)
int _onTimeType { 0 }; // compatibility only 1 - user, 2 - offset
int _offTimeType = 0; ///< compatibility only 1 - user(absolute), 2 - offset (%)
int _onTimeType = 0; ///< compatibility only 1 - user, 2 - offset
int _subchannel { 0 }; ///< articulation
int _line { INVALID_LINE }; ///< y-Position; 0 - top line.
int _fret { -1 }; ///< for tablature view
int _string { -1 };
mutable int _tpc[2] { Tpc::TPC_INVALID, Tpc::TPC_INVALID }; ///< tonal pitch class (concert/transposing)
mutable int _pitch { 0 }; ///< Note pitch as midi value (0 - 127).
int _subchannel = 0; ///< articulation
int _line = INVALID_LINE; ///< y-Position; 0 - top line.
int _fret = -1; ///< for tablature view
float m_harmonicFret = -1.0;
DisplayFretOption m_displayFret = DisplayFretOption::NoHarmonic;
int _string = -1;
mutable int _tpc[2] = { Tpc::TPC_INVALID, Tpc::TPC_INVALID }; ///< tonal pitch class (concert/transposing)
mutable int _pitch = 0; ///< Note pitch as midi value (0 - 127).
int _veloOffset { 0 }; ///< velocity user offset in percent, or absolute velocity for this note
int _fixedLine { 0 }; // fixed line number if _fixed == true
qreal _tuning { 0.0 }; ///< pitch offset in cent, playable only by internal synthesizer
int _veloOffset = 0; ///< velocity user offset in percent, or absolute velocity for this note
int _fixedLine = 0; ///< fixed line number if _fixed == true
qreal _tuning = 0.0; ///< pitch offset in cent, playable only by internal synthesizer
Accidental* _accidental { 0 };
Accidental* _accidental = nullptr;
Tie* _tieFor { 0 };
Tie* _tieBack { 0 };
Tie* _tieFor = nullptr;
Tie* _tieBack = nullptr;
Slide _attachedSlide; // slide which starts from note
Slide* _relatedSlide = nullptr; // slide which goes to note
Slide _attachedSlide; ///< slide which starts from note
Slide* _relatedSlide = nullptr; ///< slide which goes to note
Symbol* _leftParenthesis = nullptr;
Symbol* _rightParenthesis = nullptr;
@ -339,6 +348,10 @@ public:
int fret() const { return _fret; }
void setFret(int val) { _fret = val; }
float harmonicFret() const { return m_harmonicFret; }
void setHarmonicFret(float val) { m_harmonicFret = val; }
DisplayFretOption displayFret() const { return m_displayFret; }
void setDisplayFret(DisplayFretOption val) { m_displayFret = val; }
int string() const { return _string; }
void setString(int val);
bool ghost() const { return _ghost; }

View file

@ -219,7 +219,7 @@ void StringData::fretChords(Chord* chord) const
maxFret = INT32_MIN;
for (auto& p : sortedNotes) {
Note* note = p.second;
if (note->string() != INVALID_STRING_INDEX) {
if (note->string() != INVALID_STRING_INDEX && note->displayFret() != Note::DisplayFretOption::Hide) {
bUsed[note->string()]++;
}
if (note->fret() != INVALID_FRET_INDEX && note->fret() < minFret) {
@ -259,7 +259,7 @@ void StringData::fretChords(Chord* chord) const
}
// if the note string (either original or newly assigned) is also used by another note
if (bUsed[nNewString] > 1) {
if (note->displayFret() == Note::DisplayFretOption::NoHarmonic && bUsed[nNewString] > 1) {
// attempt to find a suitable string, from topmost
for (nTempString=0; nTempString < static_cast<int>(strings()); nTempString++) {
if (bUsed[nTempString] < 1
@ -470,15 +470,6 @@ void StringData::sortChordNotes(std::map<int, Note*>& sortedNotes, const Chord*
for (Note* note : chord->notes()) {
int string = note->string();
int fret = note->fret();
// if note not fretted yet or current fretting no longer valid,
// use most convenient string as key
if (string <= INVALID_STRING_INDEX || fret <= INVALID_FRET_INDEX
|| getPitch(string, fret + capoFret, pitchOffset) != note->pitch()) {
note->setString(INVALID_STRING_INDEX);
note->setFret(INVALID_FRET_INDEX);
convertPitch(note->pitch(), pitchOffset, &string, &fret);
}
int key = string * 100000;
key += -(note->pitch() + pitchOffset) * 100 + *count; // disambiguate notes of equal pitch
sortedNotes.insert({ key, note });

View file

@ -1346,25 +1346,37 @@ void GPConverter::addHarmonic(const GPNote* gpnote, Note* note)
return;
}
Note* hnote = nullptr;
if (gpnote->harmonic().type != GPNote::Harmonic::Type::Natural) {
Note* hnote = mu::engraving::Factory::createNote(_score->dummy()->chord());
hnote = mu::engraving::Factory::createNote(_score->dummy()->chord());
hnote->setTrack(note->track());
hnote->setString(note->string());
hnote->setFret(note->fret());
hnote->setPitch(note->pitch());
hnote->setFret(note->fret());
note->setDisplayFret(Note::DisplayFretOption::ArtificialHarmonic);
hnote->setDisplayFret(Note::DisplayFretOption::Hide);
hnote->setTpcFromPitch();
note->chord()->add(hnote);
hnote->setPlay(false);
addTie(gpnote, hnote);
addTie(gpnote, note);
note->setHarmonicFret(note->fret() + gpnote->harmonic().fret);
} else {
note->setHarmonicFret(gpnote->harmonic().fret);
note->setDisplayFret(Note::DisplayFretOption::NaturalHarmonic);
}
Note* harmonicNote = hnote ? hnote : note;
int gproHarmonicType = static_cast<int>(gpnote->harmonic().type);
int harmonicFret = GuitarPro::harmonicOvertone(note, gpnote->harmonic().fret, gproHarmonicType);
int string = note->string();
int harmonicPitch = note->part()->instrument()->stringData()->getPitch(string, harmonicFret, nullptr);
note->setPitch(harmonicPitch);
note->setTpcFromPitch();
note->setHarmonic(true);
int string = harmonicNote->string();
int harmonicPitch = harmonicNote->part()->instrument()->stringData()->getPitch(string,
harmonicFret + harmonicNote->part()->capoFret(),
harmonicNote->staff());
harmonicNote->setPitch(harmonicPitch);
harmonicNote->setTpcFromPitch();
harmonicNote->setHarmonic(true);
auto harmonicText = [](const GPNote::Harmonic::Type& h) {
if (h == GPNote::Harmonic::Type::Artificial) {
@ -1382,7 +1394,7 @@ void GPConverter::addHarmonic(const GPNote* gpnote, Note* note)
}
};
addTextToNote(harmonicText(gpnote->harmonic().type), note);
addTextToNote(harmonicText(gpnote->harmonic().type), harmonicNote);
}
void GPConverter::configureNote(const GPNote* gpnote, Note* note)
@ -2315,7 +2327,7 @@ void GPConverter::addPickStroke(const GPBeat* beat, ChordRest* cr)
void GPConverter::addTremolo(const GPBeat* beat, ChordRest* cr)
{
if (beat->tremolo().enumerator == -1) {
if (!cr->isChord() || beat->tremolo().enumerator == -1) {
return;
}
@ -2332,7 +2344,7 @@ void GPConverter::addTremolo(const GPBeat* beat, ChordRest* cr)
Tremolo* t = Factory::createTremolo(_score->dummy()->chord());
t->setTremoloType(scoreTremolo(beat->tremolo()));
cr->add(t);
static_cast<Chord*>(cr)->add(t);
}
void GPConverter::addWah(const GPBeat* beat, ChordRest* cr)

View file

@ -1133,37 +1133,73 @@ bool GuitarPro5::readNoteEffects(Note* note)
}
if (modMask2 & EFFECT_ARTIFICIAL_HARMONIC) {
int type = readArtificialHarmonic();
if (type == 2) {
auto harmNote = readUChar();
/*auto sharp =*/ readChar();
auto octave = readUChar();
int type = readChar();
if (type == 1 || type == 3) {
int fret = type == 3 ? (readChar() - note->fret()) : note->fret();
auto harmonicNote = Factory::createNote(note->chord());
harmonicNote->setHarmonic(true);
note->chord()->add(harmonicNote);
note->setHarmonic(true);
float harmonicFret = naturalHarmonicFromFret(fret);
int harmonicOvertone = GuitarPro::harmonicOvertone(note, harmonicFret, type);
note->setDisplayFret(Note::DisplayFretOption::NaturalHarmonic);
note->setHarmonicFret(harmonicFret);
auto staff = note->staff();
int pitch = staff->part()->instrument()->stringData()->getPitch(note->string(), harmonicOvertone, staff);
note->setPitch(std::clamp(pitch, 0, 127));
note->setTpcFromPitch();
} else if (type == 2) {
int fret = note->fret();
switch (harmNote) {
case 0: fret += 24;
float midi = note->pitch() + fret;
int currentOctave = floor(midi / 12);
auto harmNote = readChar();
readChar();
auto octave = currentOctave + readChar();
Note* harmonicNote = Factory::createNote(note->chord());
harmonicNote->setHarmonic(true);
harmonicNote->setDisplayFret(Note::DisplayFretOption::ArtificialHarmonic);
note->setDisplayFret(Note::DisplayFretOption::Hide);
Staff* staff = note->staff();
float harmonicFret = 0;
switch (static_cast<int>(((octave * 12) + harmNote) - midi)) {
case 4:
harmonicFret = 12;
break;
case 2: fret += 34;
case 11:
harmonicFret = 7;
break;
case 4: fret += 38;
case 16:
harmonicFret = 5;
break;
case 5: fret += 12;
case 20:
harmonicFret = 4;
break;
case 7: fret += 32;
case 23:
harmonicFret = 3.2;
break;
case 9: fret += 28;
case 25:
harmonicFret = 2.7;
break;
case 28:
harmonicFret = 2.4;
break;
default:
harmonicFret = 12;
break;
default: fret += octave * 12;
}
int overtoneFret = GuitarPro::harmonicOvertone(note, harmonicFret, type);
harmonicNote->setString(note->string());
harmonicNote->setFret(fret);
harmonicNote->setPitch(staff->part()->instrument()->stringData()->getPitch(note->string(), fret + note->part()->capoFret(),
nullptr));
harmonicNote->setFret(note->fret());
harmonicNote->setHarmonicFret(harmonicFret + fret);
int pitch = staff->part()->instrument()->stringData()->getPitch(note->string(), overtoneFret + note->part()->capoFret(), staff);
harmonicNote->setPitch(std::clamp(pitch, 0, 127));
harmonicNote->setTpcFromPitch();
note->chord()->add(harmonicNote);
addTextToNote("A.H.", harmonicNote);
}
}
@ -1431,6 +1467,42 @@ bool GuitarPro5::readNote(int string, Note* note)
return slur;
}
float GuitarPro5::naturalHarmonicFromFret(int fret)
{
switch (fret) {
case 2:
return 2.4;
case 3:
return 3.2;
case 4:
case 5:
case 7:
case 9:
case 12:
case 16:
case 17:
case 19:
case 24:
return fret;
case 8:
return 8.2;
case 10:
return 9.6;
case 14:
return 14.7;
case 15:
return 14.7;
case 21:
return 21.7;
case 22:
return 21.7;
default:
return 12.0;
}
return 0;
}
//---------------------------------------------------------
// readArtificialHarmonic
//---------------------------------------------------------

View file

@ -2836,6 +2836,67 @@ void GuitarPro::createCrecDim(int staffIdx, int track, const Fraction& tick, boo
score->undoAddElement(hairpins[staffIdx]);
}
//---------------------------------------------------------
// addMetaInfo
//---------------------------------------------------------
static void addMetaInfo(MasterScore* score, GuitarPro* gp)
{
std::vector<QString> fieldNames = { gp->title, gp->subtitle, gp->artist,
gp->album, gp->composer };
bool createTitleField
= std::any_of(fieldNames.begin(), fieldNames.end(), [](const QString& fieldName) { return !fieldName.isEmpty(); });
if (createTitleField) {
MeasureBase* m;
if (!score->measures()->first()) {
m = Factory::createVBox(score->dummy()->system());
m->setTick(Fraction(0, 1));
score->addMeasure(m, 0);
} else {
m = score->measures()->first();
if (!m->isVBox()) {
MeasureBase* mb = Factory::createVBox(score->dummy()->system());
mb->setTick(Fraction(0, 1));
score->addMeasure(mb, m);
m = mb;
}
}
if (!gp->title.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::TITLE);
s->setPlainText(gp->title);
m->add(s);
}
if (!gp->subtitle.isEmpty() || !gp->artist.isEmpty() || !gp->album.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::SUBTITLE);
QString str;
if (!gp->subtitle.isEmpty()) {
str.append(gp->subtitle);
}
if (!gp->artist.isEmpty()) {
if (!str.isEmpty()) {
str.append("\n");
}
str.append(gp->artist);
}
if (!gp->album.isEmpty()) {
if (!str.isEmpty()) {
str.append("\n");
}
str.append(gp->album);
}
s->setPlainText(str);
m->add(s);
}
if (!gp->composer.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::COMPOSER);
s->setPlainText(gp->composer);
m->add(s);
}
}
}
//---------------------------------------------------------
// importGTP
//---------------------------------------------------------
@ -2929,59 +2990,7 @@ Score::FileError importGTP(MasterScore* score, const QString& name)
score->style().set(Sid::ArpeggioHiddenInStdIfTab, true);
std::vector<QString> fieldNames = { gp->title, gp->subtitle, gp->artist,
gp->album, gp->composer };
bool createTitleField
= std::any_of(fieldNames.begin(), fieldNames.end(), [](const QString& fieldName) { return !fieldName.isEmpty(); });
if (createTitleField) {
MeasureBase* m;
if (!score->measures()->first()) {
m = Factory::createVBox(score->dummy()->system());
m->setTick(Fraction(0, 1));
score->addMeasure(m, 0);
} else {
m = score->measures()->first();
if (!m->isVBox()) {
MeasureBase* mb = Factory::createVBox(score->dummy()->system());
mb->setTick(Fraction(0, 1));
score->addMeasure(mb, m);
m = mb;
}
}
if (!gp->title.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::TITLE);
s->setPlainText(gp->title);
m->add(s);
}
if (!gp->subtitle.isEmpty() || !gp->artist.isEmpty() || !gp->album.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::SUBTITLE);
QString str;
if (!gp->subtitle.isEmpty()) {
str.append(gp->subtitle);
}
if (!gp->artist.isEmpty()) {
if (!str.isEmpty()) {
str.append("\n");
}
str.append(gp->artist);
}
if (!gp->album.isEmpty()) {
if (!str.isEmpty()) {
str.append("\n");
}
str.append(gp->album);
}
s->setPlainText(str);
m->add(s);
}
if (!gp->composer.isEmpty()) {
Text* s = Factory::createText(m, TextStyleType::COMPOSER);
s->setPlainText(gp->composer);
m->add(s);
}
}
addMetaInfo(score, gp);
int idx = 0;

View file

@ -362,6 +362,7 @@ class GuitarPro5 : public GuitarPro
void readMeasures(int startingTempo);
Fraction readBeat(const Fraction& tick, int voice, Measure* measure, int staffIdx, Tuplet** tuplets, bool mixChange);
bool readNoteEffects(Note*);
float naturalHarmonicFromFret(int fret);
public:
GuitarPro5(MasterScore* s, int v)