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

[MU3 Backend] ENG-23: Infer transpositions for guitar staves #8358

Merged
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
31 changes: 27 additions & 4 deletions importexport/musicxml/importmxmlpass1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1101,8 +1101,11 @@ void MusicXMLParserPass1::identification()
else if (_e.name() == "encoding") {
// TODO
while (_e.readNextStartElement()) {
if (_e.name() == "supports" && _e.attributes().value("element") == "beam" && _e.attributes().value("type") == "yes")
_hasBeamingInfo = true;
if (_e.name() == "supports" )
if (_e.attributes().value("element") == "beam" && _e.attributes().value("type") == "yes")
_hasBeamingInfo = true;
else if (_e.attributes().value("element") == "transpose")
iveshenry18 marked this conversation as resolved.
Show resolved Hide resolved
_supportsTranspose = _e.attributes().value("type").toString();
_e.skipCurrentElement();
}
// _score->setMetaTag("encoding", _e.readElementText()); works with DOM but not with pull parser
Expand Down Expand Up @@ -1798,6 +1801,23 @@ static const InstrumentTemplate* findInstrument(const QString& instrSound)
}
#endif

//---------------------------------------------------------
// addInferredTranspose
//---------------------------------------------------------
/**
In the case that transposition information is missing,
instrument-level transpositions are inferred here.
This changes the *written* pitch, but retains the sounding pitch.
*/
void MusicXMLParserPass1::addInferredTranspose(const QString& partId)
{
if (_parts[partId].getName().contains("guitar", Qt::CaseInsensitive)
&& !_parts[partId].hasTab()) {
_parts[partId]._inferredTranspose = Interval(12);
_parts[partId]._intervals[Fraction(0, 1)] = Interval(-12);
}
}

//---------------------------------------------------------
// scorePart
//---------------------------------------------------------
Expand Down Expand Up @@ -2374,7 +2394,7 @@ void MusicXMLParserPass1::attributes(const QString& partId, const Fraction cTime
TODO: Store the clef type, to simplify staff type setting in pass 2.
*/

void MusicXMLParserPass1::clef(const QString& /* partId */)
void MusicXMLParserPass1::clef(const QString& partId)
{
Q_ASSERT(_e.isStartElement() && _e.name() == "clef");
_logger->logDebugTrace("MusicXMLParserPass1::clef", &_e);
Expand All @@ -2394,8 +2414,11 @@ void MusicXMLParserPass1::clef(const QString& /* partId */)
while (_e.readNextStartElement()) {
if (_e.name() == "line")
_e.skipCurrentElement(); // skip but don't log
else if (_e.name() == "sign")
else if (_e.name() == "sign") {
QString sign = _e.readElementText();
if (sign == "TAB")
_parts[partId].hasTab(true);
}
else
skipLogCurrElem();
}
Expand Down
3 changes: 3 additions & 0 deletions importexport/musicxml/importmxmlpass1.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class MusicXMLParserPass1 {
const CreditWordsList& credits() const { return _credits; }
bool hasBeamingInfo() const { return _hasBeamingInfo; }
static VBox* createAndAddVBoxForCreditWords(Score* const score, const int miny = 0, const int maxy = 75);
QString supportsTranspose() const { return _supportsTranspose; }
void addInferredTranspose(const QString& partId);

private:
// functions
Expand All @@ -186,6 +188,7 @@ class MusicXMLParserPass1 {
Score* _score; ///< MuseScore score
MxmlLogger* _logger; ///< Error logger
bool _hasBeamingInfo; ///< Whether the score supports or contains beaming info
QString _supportsTranspose; ///< Whether the score supports transposition info

// part specific data (TODO: move to part-specific class)
Fraction _timeSigDura; ///< Measure duration according to last timesig read
Expand Down
13 changes: 9 additions & 4 deletions importexport/musicxml/importmxmlpass2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ static int MusicXMLStepAltOct2Pitch(int step, int alter, int octave)
Note that n's staff and track have not been set yet
*/

static void xmlSetPitch(Note* n, int step, int alter, int octave, const int octaveShift, const Instrument* const instr)
static void xmlSetPitch(Note* n, int step, int alter, int octave, const int octaveShift, const Instrument* const instr, Interval inferredTranspose = Interval(0))
{
//qDebug("xmlSetPitch(n=%p, step=%d, alter=%d, octave=%d, octaveShift=%d)",
// n, step, alter, octave, octaveShift);
Expand All @@ -225,18 +225,20 @@ static void xmlSetPitch(Note* n, int step, int alter, int octave, const int octa
//const Instrument* instr = staff->part()->instr();

const Interval intval = instr->transpose();

const Interval combinedIntval(intval.diatonic + inferredTranspose.diatonic, intval.chromatic + inferredTranspose.chromatic);
//qDebug(" staff=%p instr=%p dia=%d chro=%d",
// staff, instr, static_cast<int>(intval.diatonic), static_cast<int>(intval.chromatic));

int pitch = MusicXMLStepAltOct2Pitch(step, alter, octave);
pitch += intval.chromatic; // assume not in concert pitch
pitch += 12 * octaveShift; // correct for octave shift
pitch += inferredTranspose.chromatic;
// ensure sane values
pitch = limit(pitch, 0, 127);

int tpc2 = step2tpc(step, AccidentalVal(alter));
int tpc1 = Ms::transposeTpc(tpc2, intval, true);
tpc2 = Ms::transposeTpc(tpc2, inferredTranspose, true);
int tpc1 = Ms::transposeTpc(tpc2, combinedIntval, true);
n->setPitch(pitch, tpc1, tpc2);
//qDebug(" pitch=%d tpc1=%d tpc2=%d", n->pitch(), n->tpc1(), n->tpc2());
}
Expand Down Expand Up @@ -1663,6 +1665,9 @@ void MusicXMLParserPass2::part()
_hasDrumset = hasDrumset(instruments);

// set the parts first instrument

if (_pass1.supportsTranspose() == "no")
_pass1.addInferredTranspose(id);
setPartInstruments(_logger, &_e, _pass1.getPart(id), id, _score, _pass1.getInstrList(id), _pass1.getIntervals(id), instruments);

// set the part name
Expand Down Expand Up @@ -4530,7 +4535,7 @@ static void setPitch(Note* note, MusicXMLParserPass1& pass1, const QString& part
}
}
else {
xmlSetPitch(note, mnp.step(), mnp.alter(), mnp.octave(), octaveShift, instrument);
xmlSetPitch(note, mnp.step(), mnp.alter(), mnp.octave(), octaveShift, instrument, pass1.getMusicXmlPart(partId)._inferredTranspose);
}
}

Expand Down
4 changes: 4 additions & 0 deletions importexport/musicxml/importxmlfirstpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MusicXmlPart {
int nMeasures() const { return measureDurations.size(); }
MusicXmlInstrList _instrList; // TODO: make private
MusicXmlIntervalList _intervals; ///< Transpositions
Interval _inferredTranspose;
Interval interval(const Fraction f) const;
int octaveShift(const int staff, const Fraction f) const;
void addOctaveShift(const int staff, const int shift, const Fraction f);
Expand All @@ -77,6 +78,8 @@ class MusicXmlPart {
QString getAbbr() const { return abbr; }
void setPrintAbbr(bool b) { printAbbr = b; }
bool getPrintAbbr() const { return printAbbr; }
bool hasTab() const { return _hasTab; }
void hasTab(const bool b) { _hasTab = b; }
LyricNumberHandler& lyricNumberHandler() { return _lyricNumberHandler; }
const LyricNumberHandler& lyricNumberHandler() const { return _lyricNumberHandler; }
void setMaxStaff(const int staff);
Expand All @@ -87,6 +90,7 @@ class MusicXmlPart {
bool printName = true;
QString abbr;
bool printAbbr = true;
bool _hasTab = false;
QStringList measureNumbers; // MusicXML measure number attribute
QList<Fraction> measureDurations; // duration in fraction for every measure
QVector<MusicXmlOctaveShiftList> octaveShifts; // octave shift list for every staff
Expand Down