MusicXML import of unrecognized chords
A new method is provided generate a chordname directly from a MusicXML harmony object. This chordname is then parsed and rendered just as for a chordname typed directly into the score.
This commit is contained in:
parent
07962f4a5a
commit
0cd63cfe6f
9 changed files with 421 additions and 135 deletions
|
@ -434,23 +434,30 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
{
|
||||
QString tok1, tok1L, tok2, tok2L;
|
||||
QString extensionDigits = "123456789";
|
||||
QString special = "(), ";
|
||||
QString leading = "( ";
|
||||
QString trailing = "), ";
|
||||
QString special = "()[], ";
|
||||
QString leading = "([ ";
|
||||
QString trailing = ")], ";
|
||||
QString initial;
|
||||
bool take6 = false, take7 = false, take9 = false, take11 = false, take13 = false;
|
||||
int firstLeadingToken, lastLeadingToken;
|
||||
#ifdef ALLOW_PARENTHESIZED_EXTENSION
|
||||
int firstLeadingToken;
|
||||
#endif
|
||||
int lastLeadingToken;
|
||||
int len = s.size();
|
||||
int i, j;
|
||||
|
||||
configure(cl);
|
||||
_name = s;
|
||||
_parseable = true;
|
||||
_understandable = true;
|
||||
i = 0;
|
||||
|
||||
#ifdef ALLOW_PARENTHESIZED_EXTENSION
|
||||
// eat leading parens
|
||||
firstLeadingToken = _tokenList.size();
|
||||
while (i < len && leading.contains(s[i]))
|
||||
addToken(QString(s[i++]),QUALITY);
|
||||
#endif
|
||||
lastLeadingToken = _tokenList.size();
|
||||
// get quality
|
||||
for (j = 0, tok1 = "", tok1L = "", initial = ""; i < len; ++i, ++j) {
|
||||
|
@ -506,26 +513,35 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
}
|
||||
if (tok1 != "")
|
||||
addToken(tok1,QUALITY);
|
||||
#ifdef ALLOW_PARENTHESIZED_EXTENSION
|
||||
else {
|
||||
// leading tokens were not really QUALITY
|
||||
for (int t = firstLeadingToken; t < lastLeadingToken; ++t)
|
||||
_tokenList[t].tokenClass = EXTENSION;
|
||||
}
|
||||
_xmlKind = _quality;
|
||||
_xmlText = tok1;
|
||||
if (symbols.contains(tok1))
|
||||
_xmlSymbols = "yes";
|
||||
else
|
||||
_xmlSymbols = "no";
|
||||
#endif
|
||||
if (!syntaxOnly) {
|
||||
_xmlKind = _quality;
|
||||
_xmlParens = "no";
|
||||
if (symbols.contains(tok1)) {
|
||||
_xmlSymbols = "yes";
|
||||
_xmlText = "";
|
||||
}
|
||||
else {
|
||||
_xmlSymbols = "no";
|
||||
_xmlText = tok1;
|
||||
}
|
||||
}
|
||||
// eat trailing parens and commas
|
||||
while (i < len && trailing.contains(s[i]))
|
||||
addToken(QString(s[i++]),QUALITY);
|
||||
|
||||
#ifdef ALLOW_PARENTHESIZED_EXTENSION
|
||||
// eat leading parens
|
||||
firstLeadingToken = _tokenList.size();
|
||||
while (i < len && leading.contains(s[i])) {
|
||||
while (i < len && leading.contains(s[i]))
|
||||
addToken(QString(s[i++]),EXTENSION);
|
||||
}
|
||||
#endif
|
||||
lastLeadingToken = _tokenList.size();
|
||||
// get extension - up to first non-digit
|
||||
for (j = 0, tok1 = ""; i < len; ++i, ++j) {
|
||||
|
@ -537,22 +553,29 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
if (_quality == "") {
|
||||
if (_extension == "7" || _extension == "9" || _extension == "11" || _extension == "13") {
|
||||
_quality = "dominant";
|
||||
_xmlKind = "dominant";
|
||||
if (!syntaxOnly)
|
||||
_xmlKind = "dominant";
|
||||
take7 = true; take9 = true; take11 = true; take13 = true;
|
||||
}
|
||||
else {
|
||||
_quality = "major";
|
||||
_xmlKind = "major";
|
||||
if (!syntaxOnly)
|
||||
_xmlKind = "major";
|
||||
take6 = true; take7 = true; take9 = true; take11 = true; take13 = true;
|
||||
}
|
||||
}
|
||||
if (tok1 != "")
|
||||
addToken(tok1,EXTENSION);
|
||||
#ifdef ALLOW_PARENTHESIZED_EXTENSION
|
||||
else {
|
||||
// leading tokens were not really EXTENSION
|
||||
for (int t = firstLeadingToken; t < lastLeadingToken; ++t)
|
||||
for (int t = firstLeadingToken; t < lastLeadingToken; ++t) {
|
||||
_tokenList[t].tokenClass = MODIFIER;
|
||||
if (!syntaxOnly)
|
||||
_xmlParens = "yes";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!syntaxOnly) {
|
||||
QStringList extl;
|
||||
if (tok1 == "5")
|
||||
|
@ -566,7 +589,7 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
else if (tok1 == "7") {
|
||||
if (take7)
|
||||
_xmlKind += "-seventh";
|
||||
else
|
||||
else if (_xmlKind != "half-diminished")
|
||||
extl << "7";
|
||||
}
|
||||
else if (tok1 == "9") {
|
||||
|
@ -576,26 +599,32 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
_xmlKind += "-seventh";
|
||||
extl << "9";
|
||||
}
|
||||
else if (_xmlKind == "half-diminished")
|
||||
extl << "9";
|
||||
else
|
||||
extl << "7" << "9";
|
||||
}
|
||||
else if (tok1 == "11") {
|
||||
if (take11)
|
||||
_xmlKind += "-eleventh";
|
||||
_xmlKind += "-11th";
|
||||
else if (take7) {
|
||||
_xmlKind += "-seventh";
|
||||
extl << "9" << "11";
|
||||
}
|
||||
else if (_xmlKind == "half-diminished")
|
||||
extl << "9" << "11";
|
||||
else
|
||||
extl << "7" << "9" << "11";
|
||||
}
|
||||
else if (tok1 == "13") {
|
||||
if (take13)
|
||||
_xmlKind += "-thirteenth";
|
||||
_xmlKind += "-13th";
|
||||
else if (take7) {
|
||||
_xmlKind += "-seventh";
|
||||
extl << "9" << "11" << "13";
|
||||
}
|
||||
else if (_xmlKind == "half-diminished")
|
||||
extl << "9" << "11" << "13";
|
||||
else
|
||||
extl << "7" << "9" << "11" << "13";
|
||||
}
|
||||
|
@ -606,14 +635,17 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
}
|
||||
else
|
||||
extl << "6" << "9";
|
||||
}
|
||||
}
|
||||
foreach (QString e, extl) {
|
||||
QString d = "add" + e;
|
||||
_xmlDegrees += d;
|
||||
}
|
||||
if (_xmlKind == "dominant-seventh")
|
||||
_xmlKind = "dominant";
|
||||
_xmlText += tok1;
|
||||
if (tok1 == "69")
|
||||
_xmlText += "6";
|
||||
else
|
||||
_xmlText += tok1;
|
||||
}
|
||||
// eat trailing parens and commas
|
||||
while (i < len && trailing.contains(s[i]))
|
||||
|
@ -621,7 +653,6 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
|
||||
// get modifiers
|
||||
bool addPending = false;
|
||||
_xmlParens = "no";
|
||||
_modifierList.clear();
|
||||
while (i < len) {
|
||||
// eat leading parens
|
||||
|
@ -675,9 +706,9 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
else if (tok1L == "sus" && tok2L == "")
|
||||
tok2L = "4";
|
||||
else if (augmented.contains(tok1L) && tok2L == "") {
|
||||
_xmlDegrees += "alt#5";
|
||||
_quality = "augmented";
|
||||
tok1L = "";
|
||||
}
|
||||
}
|
||||
QString m = tok1L + tok2L;
|
||||
if (m != "")
|
||||
_modifierList += m;
|
||||
|
@ -693,11 +724,18 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
else if (tok1L == "no")
|
||||
degree = "sub" + tok2L;
|
||||
else if (tok1L == "sus") {
|
||||
#ifdef TREAT_SUS_AS_DEGREE
|
||||
_xmlDegrees += "sub3";
|
||||
tok1L = "add";
|
||||
#else
|
||||
// convert chords with sus into suspended "kind"
|
||||
// extension then becomes a series of degree adds
|
||||
QString seventh;
|
||||
if (tok2L == "4")
|
||||
_xmlKind = "suspended-fourth";
|
||||
else if (tok2L == "2")
|
||||
_xmlKind = "suspended-second";
|
||||
_xmlText = tok1 + tok2;
|
||||
if (_extension == "7" || _extension == "9" || _extension == "11" || _extension == "13")
|
||||
degree = (_quality == "major") ? "add#7" : "add7";
|
||||
else if (_extension != "")
|
||||
|
@ -707,12 +745,13 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
_xmlDegrees += "add11";
|
||||
_xmlDegrees += "add13";
|
||||
}
|
||||
if (_extension == "11") {
|
||||
else if (_extension == "11") {
|
||||
_xmlDegrees += "add9";
|
||||
_xmlDegrees += "add11";
|
||||
}
|
||||
if (_extension == "9")
|
||||
else if (_extension == "9")
|
||||
_xmlDegrees += "add9";
|
||||
#endif
|
||||
}
|
||||
else if (tok1L == "major") {
|
||||
if (_xmlKind.startsWith("minor")) {
|
||||
|
@ -759,10 +798,11 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
}
|
||||
else if (tok1L == "tristan") {
|
||||
_xmlKind = "Tristan";
|
||||
_xmlText = "Tristan";
|
||||
}
|
||||
else if (addPending)
|
||||
degree = "add" + tok1L + tok2L;
|
||||
else if (tok1L == "")
|
||||
else if (tok1L == "" && tok2L != "")
|
||||
degree = "add" + tok2L;
|
||||
else if (lower.contains(tok1L)) {
|
||||
tok1L = "b";
|
||||
|
@ -800,12 +840,215 @@ bool ParsedChord::parse(const QString& s, const ChordList* cl, bool syntaxOnly)
|
|||
|
||||
_handle = "<" + _quality + "><" + _extension + ">" + _modifiers;
|
||||
qDebug("parse: source = <%s>, handle = %s",qPrintable(s),qPrintable(_handle));
|
||||
qDebug("parse: xmlKind = <%s>, text = <%s>",qPrintable(_xmlKind),qPrintable(_xmlText));
|
||||
qDebug("parse: xmlSymbols = %s, xmlParens = %s",qPrintable(_xmlSymbols),qPrintable(_xmlParens));
|
||||
qDebug("parse: xmlDegrees = <%s>",qPrintable(_xmlDegrees.join(",")));
|
||||
if (!syntaxOnly) {
|
||||
qDebug("parse: xmlKind = <%s>, text = <%s>",qPrintable(_xmlKind),qPrintable(_xmlText));
|
||||
qDebug("parse: xmlSymbols = %s, xmlParens = %s",qPrintable(_xmlSymbols),qPrintable(_xmlParens));
|
||||
qDebug("parse: xmlDegrees = <%s>",qPrintable(_xmlDegrees.join(",")));
|
||||
}
|
||||
return _parseable;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// fromXml
|
||||
//---------------------------------------------------------
|
||||
|
||||
QString ParsedChord::fromXml(const QString& kind, const QString& rawKindText, const QString& useSymbols, const QString& useParens, const QList<HDegree>& dl, const ChordList* cl)
|
||||
{
|
||||
QString kindText = rawKindText;
|
||||
bool symbols = (useSymbols == "yes");
|
||||
bool parens = (useParens == "yes");
|
||||
bool implied = false;
|
||||
bool extend = false;
|
||||
int extension = 0;
|
||||
_parseable = true;
|
||||
_understandable = true;
|
||||
|
||||
// get quality info from kind
|
||||
if (kind == "major-minor") {
|
||||
_quality = "minor";
|
||||
_modifierList += "major7";
|
||||
extend = true;
|
||||
}
|
||||
else if (kind.contains("major")) {
|
||||
_quality = "major";
|
||||
if (kind == "major" || kind == "major-sixth")
|
||||
implied = true;
|
||||
}
|
||||
else if (kind.contains("minor"))
|
||||
_quality = "minor";
|
||||
else if (kind.contains("dominant")) {
|
||||
_quality = "dominant";
|
||||
implied = true;
|
||||
extension = 7;
|
||||
}
|
||||
else if (kind == "augmented-seventh") {
|
||||
_quality = "augmented";
|
||||
extension = 7;
|
||||
extend = true;
|
||||
}
|
||||
else if (kind == "augmented")
|
||||
_quality = "augmented";
|
||||
else if (kind == "half-diminished") {
|
||||
_quality = "half-diminished";
|
||||
extension = 7;
|
||||
extend = true;
|
||||
}
|
||||
else if (kind == "diminished-seventh") {
|
||||
_quality = "diminished";
|
||||
extension = 7;
|
||||
}
|
||||
else if (kind == "diminished")
|
||||
_quality = "diminished";
|
||||
else if (kind == "suspended-fourth") {
|
||||
_quality = "major";
|
||||
implied = true;
|
||||
if (kindText == "")
|
||||
_modifierList += "sus4";
|
||||
else
|
||||
_modifierList += kindText;
|
||||
}
|
||||
else if (kind == "suspended-second") {
|
||||
_quality = "major";
|
||||
implied = true;
|
||||
if (kindText == "")
|
||||
_modifierList += "sus2";
|
||||
else
|
||||
_modifierList += kindText;
|
||||
}
|
||||
else if (kind == "power") {
|
||||
_quality = "major";
|
||||
implied = true;
|
||||
extension = 5;
|
||||
}
|
||||
else
|
||||
_quality = kind;
|
||||
|
||||
// get extension info from kind
|
||||
if (kind.contains("seventh"))
|
||||
extension = 7;
|
||||
else if (kind.contains("ninth"))
|
||||
extension = 9;
|
||||
else if (kind.contains("11th"))
|
||||
extension = 11;
|
||||
else if (kind.contains("13th"))
|
||||
extension = 13;
|
||||
else if (kind.contains("sixth"))
|
||||
extension = 6;
|
||||
|
||||
// get modifier info from degree list
|
||||
foreach (const HDegree& d, dl) {
|
||||
QString mod;
|
||||
int v = d.value();
|
||||
switch (d.type()) {
|
||||
case ADD:
|
||||
case ALTER:
|
||||
switch (d.alter()) {
|
||||
case -1: mod = "b"; break;
|
||||
case 1: mod = "#"; break;
|
||||
case 0: mod = "add"; break;
|
||||
}
|
||||
break;
|
||||
case SUBTRACT:
|
||||
mod = "no";
|
||||
break;
|
||||
case UNDEF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mod += QString("%1").arg(v);
|
||||
if (mod == "add7" && kind.contains("suspended")) {
|
||||
_quality = "dominant";
|
||||
implied = true;
|
||||
extension = 7;
|
||||
extend = true;
|
||||
mod = "";
|
||||
#ifdef TREAT_SUS_AS_DEGREE
|
||||
kindText = "";
|
||||
#endif
|
||||
}
|
||||
else if (mod == "add9" && extend) {
|
||||
if (extension < 9)
|
||||
extension = 9;
|
||||
mod = "";
|
||||
}
|
||||
else if (mod == "add11" && extend) {
|
||||
if (extension < 11)
|
||||
extension = 11;
|
||||
mod = "";
|
||||
}
|
||||
else if (mod == "add13" && extend) {
|
||||
extension = 13;
|
||||
mod = "";
|
||||
}
|
||||
else if (mod == "add9" && kind.contains("sixth")) {
|
||||
extension = 69;
|
||||
mod = "";
|
||||
}
|
||||
if (mod != "")
|
||||
_modifierList += mod;
|
||||
}
|
||||
// convert no3,add[42] into sus[42]
|
||||
int no3 = _modifierList.indexOf("no3");
|
||||
if (no3 >= 0 && (_modifierList.contains("add4") || _modifierList.contains("add2"))) {
|
||||
int addn = _modifierList.indexOf("add4");
|
||||
if (addn == -1)
|
||||
addn = _modifierList.indexOf("add2");
|
||||
QString& s = _modifierList[addn];
|
||||
s.replace("add","sus");
|
||||
_modifierList.removeAt(no3);
|
||||
}
|
||||
// record extension
|
||||
if (extension)
|
||||
_extension = QString("%1").arg(extension);
|
||||
|
||||
// construct name & handle
|
||||
if (kindText != "" && !kind.contains("suspended") && kind != "major-minor") {
|
||||
_name = kindText;
|
||||
if (extension == 69)
|
||||
_name += "9";
|
||||
}
|
||||
else if (implied)
|
||||
_name = _extension;
|
||||
else {
|
||||
if (_quality == "major")
|
||||
_name = symbols ? "^" : "maj";
|
||||
else if (_quality == "minor")
|
||||
_name = symbols ? "-" : "m";
|
||||
else if (_quality == "augmented")
|
||||
_name = symbols ? "+" : "aug";
|
||||
else if (_quality == "diminished")
|
||||
_name = symbols ? "o" : "dim";
|
||||
else if (_quality == "half-diminished")
|
||||
_name = symbols ? "0" : "m7b5";
|
||||
else if (_quality == "major-minor")
|
||||
_name = symbols ? "-(^7)" : "m(maj7)";
|
||||
else
|
||||
_name = _quality;
|
||||
_name += _extension;
|
||||
}
|
||||
if (parens)
|
||||
_name += "(";
|
||||
foreach (QString mod, _modifierList) {
|
||||
mod.replace("major","maj");
|
||||
_name += mod;
|
||||
}
|
||||
if (parens)
|
||||
_name += ")";
|
||||
|
||||
// parse name to construct handle & tokenList
|
||||
parse(_name,cl,true);
|
||||
|
||||
// record original MusicXML
|
||||
_xmlKind = kind;
|
||||
_xmlText = kindText;
|
||||
_xmlSymbols = useSymbols;
|
||||
_xmlParens = useParens;
|
||||
foreach (const HDegree& d, dl)
|
||||
_xmlDegrees += d.text();
|
||||
|
||||
return _name;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// renderList
|
||||
//---------------------------------------------------------
|
||||
|
@ -913,11 +1156,13 @@ void ChordDescription::complete(ParsedChord* pc, const ChordList* cl)
|
|||
}
|
||||
if (xmlKind == "") {
|
||||
xmlKind = pc->xmlKind();
|
||||
xmlText = pc->xmlText();
|
||||
xmlSymbols = pc->xmlSymbols();
|
||||
xmlParens = pc->xmlParens();
|
||||
xmlDegrees = pc->xmlDegrees();
|
||||
}
|
||||
// these fields are not read from chord description files
|
||||
// so get them from the parsed representation in all cases
|
||||
xmlText = pc->xmlText();
|
||||
xmlSymbols = pc->xmlSymbols();
|
||||
xmlParens = pc->xmlParens();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -123,6 +123,7 @@ class ChordToken {
|
|||
class ParsedChord {
|
||||
public:
|
||||
bool parse(const QString&, const ChordList*, bool syntaxOnly = false);
|
||||
QString fromXml(const QString&, const QString&, const QString&, const QString&, const QList<HDegree>&, const ChordList*);
|
||||
const QList<RenderAction>& renderList(const ChordList*);
|
||||
bool parseable() const { return _parseable; }
|
||||
bool understandable() const { return _understandable; }
|
||||
|
@ -131,11 +132,12 @@ class ParsedChord {
|
|||
const QString& xmlSymbols() const { return _xmlSymbols; }
|
||||
const QString& xmlParens() const { return _xmlParens; }
|
||||
const QStringList& xmlDegrees() const { return _xmlDegrees; }
|
||||
bool operator==(const ParsedChord& c) { return (this->_handle == c._handle); }
|
||||
bool operator!=(const ParsedChord& c) { return !(*this == c); }
|
||||
operator QString() { return _handle; }
|
||||
operator QString() const { return _handle; }
|
||||
bool operator==(const ParsedChord& c) const { return (this->_handle == c._handle); }
|
||||
bool operator!=(const ParsedChord& c) const { return !(*this == c); }
|
||||
ParsedChord();
|
||||
private:
|
||||
QString _name;
|
||||
QString _handle;
|
||||
QString _quality;
|
||||
QString _extension;
|
||||
|
|
|
@ -130,7 +130,7 @@ Harmony::Harmony(const Harmony& h)
|
|||
_leftParen = h._leftParen;
|
||||
_rightParen = h._rightParen;
|
||||
_degreeList = h._degreeList;
|
||||
_parsedForm = h._parsedForm;
|
||||
_parsedForm = h._parsedForm ? new ParsedChord(*h._parsedForm) : 0;
|
||||
_textName = h._textName;
|
||||
_userName = h._userName;
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ static int convertRoot(const QString& s, bool germanNames)
|
|||
// return true if chord is recognized
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool Harmony::parseHarmony(const QString& ss, int* root, int* base)
|
||||
const ChordDescription* Harmony::parseHarmony(const QString& ss, int* root, int* base)
|
||||
{
|
||||
_id = -1;
|
||||
if (_parsedForm) {
|
||||
|
@ -382,12 +382,12 @@ bool Harmony::parseHarmony(const QString& ss, int* root, int* base)
|
|||
|
||||
int n = s.size();
|
||||
if (n < 1)
|
||||
return false;
|
||||
return 0;
|
||||
bool germanNames = score()->styleB(ST_useGermanNoteNames);
|
||||
int r = convertRoot(s, germanNames);
|
||||
if (r == INVALID_TPC) {
|
||||
qDebug("1:parseHarmony failed <%s>", qPrintable(ss));
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
*root = r;
|
||||
int idx = ((n > 1) && ((s[1] == 'b') || (s[1] == '#'))) ? 2 : 1;
|
||||
|
@ -402,38 +402,21 @@ bool Harmony::parseHarmony(const QString& ss, int* root, int* base)
|
|||
s = s.mid(idx).simplified();
|
||||
_userName = s;
|
||||
const ChordList* cl = score()->style()->chordList();
|
||||
_parsedForm = new ParsedChord();
|
||||
bool parseable = _parsedForm->parse(s,cl);
|
||||
// attempt to match chord using exact string match
|
||||
// but also match using parsed forms as fallback
|
||||
foreach (const ChordDescription* cd, *cl) {
|
||||
foreach (QString ss, cd->names) {
|
||||
if (s == ss) {
|
||||
_id = cd->id;
|
||||
_textName = ss;
|
||||
qDebug("exact chord match succeeded <%s>",qPrintable(ss));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (parseable && _id == -1 && !useLiteral) {
|
||||
foreach (ParsedChord ssParsed, cd->parsedChords) {
|
||||
if (*_parsedForm == ssParsed) {
|
||||
_id = cd->id;
|
||||
_textName = cd->names.front();
|
||||
qDebug("flexible chordmatch succeeded <%s> %s", qPrintable(s), qPrintable(ssParsed));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const ChordDescription* cd = 0;
|
||||
if (useLiteral)
|
||||
cd = descr(s);
|
||||
else {
|
||||
_parsedForm = new ParsedChord();
|
||||
_parsedForm->parse(s,cl);
|
||||
cd = descr(s,_parsedForm);
|
||||
}
|
||||
// exact match failed, so fall back on parsed match if one was found
|
||||
if (_id != -1)
|
||||
return true;
|
||||
|
||||
// no match found
|
||||
_textName = _userName;
|
||||
qDebug("2:parseHarmony failed <%s><%s> (%s)", qPrintable(ss), qPrintable(s), qPrintable(*_parsedForm));
|
||||
return parseable;
|
||||
if (cd) {
|
||||
_id = cd->id;
|
||||
_textName = cd->names.front();
|
||||
}
|
||||
else
|
||||
_textName = _userName;
|
||||
return cd;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -484,9 +467,11 @@ void Harmony::endEdit()
|
|||
void Harmony::setHarmony(const QString& s)
|
||||
{
|
||||
int r, b;
|
||||
const ChordDescription* cd = 0;
|
||||
if (parseHarmony(s, &r, &b))
|
||||
cd = getDescription();
|
||||
const ChordDescription* cd = parseHarmony(s, &r, &b);
|
||||
if (!cd && _parsedForm && _parsedForm->parseable()) {
|
||||
cd = generateDescription();
|
||||
_id = cd->id;
|
||||
}
|
||||
if (cd) {
|
||||
setRootTpc(r);
|
||||
setBaseTpc(b);
|
||||
|
@ -533,11 +518,9 @@ QString HDegree::text() const
|
|||
case 1: degree += "#"; break;
|
||||
default: break;
|
||||
}
|
||||
/* QString s = QString("%1").arg(_value);
|
||||
QString ss; // = degree + s;
|
||||
QString s = QString("%1").arg(_value);
|
||||
QString ss = degree + s;
|
||||
return ss;
|
||||
*/
|
||||
return QString();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -546,7 +529,7 @@ QString HDegree::text() const
|
|||
// using musicXml "kind" string and degree list
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::fromXml(const QString& kind, const QList<HDegree>& dl)
|
||||
const ChordDescription* Harmony::fromXml(const QString& kind, const QList<HDegree>& dl)
|
||||
{
|
||||
QStringList degrees;
|
||||
|
||||
|
@ -570,7 +553,7 @@ const ChordDescription* Harmony::fromXml(const QString& kind, const QList<HDegr
|
|||
//---------------------------------------------------------
|
||||
// fromXml
|
||||
// lookup harmony in harmony data base
|
||||
// using musicXml "kind" string and degree list
|
||||
// using musicXml "kind" string only
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::fromXml(const QString& kind)
|
||||
|
@ -584,6 +567,22 @@ const ChordDescription* Harmony::fromXml(const QString& kind)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// fromXml
|
||||
// construct harmony directly from XML
|
||||
// build name first
|
||||
// then generate chord description from that
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::fromXml(const QString& kind, const QString& kindText, const QString& symbols, const QString& parens, const QList<HDegree>& dl)
|
||||
{
|
||||
ParsedChord* pc = new ParsedChord;
|
||||
_textName = pc->fromXml(kind, kindText, symbols, parens, dl, score()->style()->chordList());
|
||||
_parsedForm = pc;
|
||||
const ChordDescription* cd = getDescription(_textName,pc);
|
||||
return cd;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// descr
|
||||
// look up id in chord list
|
||||
|
@ -595,6 +594,33 @@ const ChordDescription* Harmony::descr() const
|
|||
return score()->style()->chordDescription(_id);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// descr
|
||||
// look up name in chord list
|
||||
// optionally look up by parsed chord as fallback
|
||||
// return chord description if found, or null
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::descr(const QString& name, const ParsedChord* pc) const
|
||||
{
|
||||
const ChordList* cl = score()->style()->chordList();
|
||||
const ChordDescription* match = 0;
|
||||
foreach (const ChordDescription* cd, *cl) {
|
||||
foreach (const QString& s, cd->names) {
|
||||
if (s == name)
|
||||
return cd;
|
||||
else if (pc) {
|
||||
foreach (const ParsedChord& sParsed, cd->parsedChords) {
|
||||
if (sParsed == *pc)
|
||||
match = cd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// exact match failed, so fall back on parsed match if one was found
|
||||
return match;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// getDescription
|
||||
// look up id in chord list
|
||||
|
@ -618,29 +644,25 @@ const ChordDescription* Harmony::getDescription()
|
|||
|
||||
//---------------------------------------------------------
|
||||
// getDescription
|
||||
// same but lookup by name
|
||||
// same but lookup by name and optionally parsed chord
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::getDescription(const QString& name)
|
||||
const ChordDescription* Harmony::getDescription(const QString& name, const ParsedChord* pc)
|
||||
{
|
||||
const ChordDescription* cd = 0;
|
||||
foreach (cd, *score()->style()->chordList()) {
|
||||
foreach (const QString& n, cd->names) {
|
||||
if (name == n) {
|
||||
_id = cd->id;
|
||||
_textName = cd->names.front();
|
||||
return cd;
|
||||
}
|
||||
}
|
||||
const ChordDescription* cd = descr(name,pc);
|
||||
if (cd)
|
||||
_id = cd->id;
|
||||
else {
|
||||
cd = generateDescription();
|
||||
_id = cd->id;
|
||||
}
|
||||
cd = generateDescription();
|
||||
return cd;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// generateDescription
|
||||
// generate new chord description from _textName
|
||||
// add top chord list using private id
|
||||
// add to chord list using private id
|
||||
//---------------------------------------------------------
|
||||
|
||||
const ChordDescription* Harmony::generateDescription()
|
||||
|
|
|
@ -105,8 +105,9 @@ class Harmony : public Text {
|
|||
bool rightParen() const { return _rightParen; }
|
||||
|
||||
const ChordDescription* descr() const;
|
||||
const ChordDescription* descr(const QString&, const ParsedChord* pc = 0) const;
|
||||
const ChordDescription* getDescription();
|
||||
const ChordDescription* getDescription(const QString&);
|
||||
const ChordDescription* getDescription(const QString&, const ParsedChord* pc = 0);
|
||||
const ChordDescription* generateDescription();
|
||||
|
||||
virtual void layout();
|
||||
|
@ -138,7 +139,7 @@ class Harmony : public Text {
|
|||
QString harmonyName() const;
|
||||
void render(const TextStyle* ts = 0);
|
||||
|
||||
bool parseHarmony(const QString& s, int* root, int* base);
|
||||
const ChordDescription* parseHarmony(const QString& s, int* root, int* base);
|
||||
|
||||
const QString& extensionName() const;
|
||||
|
||||
|
@ -153,7 +154,8 @@ class Harmony : public Text {
|
|||
virtual bool isEmpty() const;
|
||||
virtual qreal baseLine() const;
|
||||
|
||||
const ChordDescription* fromXml(const QString& s, const QList<HDegree>&);
|
||||
const ChordDescription* fromXml(const QString&, const QString&, const QString&, const QString&, const QList<HDegree>&);
|
||||
const ChordDescription* fromXml(const QString& s, const QList<HDegree>&);
|
||||
const ChordDescription* fromXml(const QString& s);
|
||||
virtual void spatiumChanged(qreal oldValue, qreal newValue);
|
||||
virtual QLineF dragAnchor() const;
|
||||
|
|
|
@ -5055,7 +5055,7 @@ void MusicXml::xmlHarmony(QDomElement e, int tick, Measure* measure, int staff)
|
|||
QString printFrame(e.attribute("print-frame"));
|
||||
QString printStyle(e.attribute("print-style"));
|
||||
|
||||
QString kind, kindText;
|
||||
QString kind, kindText, symbols, parens;
|
||||
QList<HDegree> degreeList;
|
||||
|
||||
if (harmony) {
|
||||
|
@ -5096,6 +5096,8 @@ void MusicXml::xmlHarmony(QDomElement e, int tick, Measure* measure, int staff)
|
|||
// print-style, halign, valign
|
||||
|
||||
kindText = e.attribute("text");
|
||||
symbols = e.attribute("use-symbols");
|
||||
parens = e.attribute("parentheses-degrees");
|
||||
kind = e.text();
|
||||
}
|
||||
else if (tag == "inversion") {
|
||||
|
@ -5172,39 +5174,52 @@ void MusicXml::xmlHarmony(QDomElement e, int tick, Measure* measure, int staff)
|
|||
|
||||
//TODO-WS ha->setTick(tick);
|
||||
|
||||
const ChordDescription* d = ha->fromXml(kind, degreeList);
|
||||
if (d == 0) {
|
||||
QString degrees;
|
||||
foreach(const HDegree &d, degreeList) {
|
||||
if (!degrees.isEmpty())
|
||||
degrees += " ";
|
||||
degrees += d.text();
|
||||
}
|
||||
qDebug("unknown chord txt: <%s> kind: <%s> degrees: %s",
|
||||
qPrintable(kindText), qPrintable(kind), qPrintable(degrees));
|
||||
|
||||
// Strategy II: lookup "kind", merge in degree list and try to find
|
||||
// harmony in list
|
||||
|
||||
d = ha->fromXml(kind);
|
||||
if (d) {
|
||||
ha->setId(d->id);
|
||||
foreach(const HDegree &d, degreeList)
|
||||
ha->addDegree(d);
|
||||
ha->resolveDegreeList();
|
||||
ha->render();
|
||||
}
|
||||
else {
|
||||
qDebug("'kind: <%s> not found in harmony data base", qPrintable(kind));
|
||||
QString s = tpc2name(ha->rootTpc(), score->style(ST_useGermanNoteNames).toBool()) + kindText;
|
||||
ha->setText(s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const ChordDescription* d = ha->fromXml(kind, kindText, symbols, parens, degreeList);
|
||||
if (d) {
|
||||
ha->setId(d->id);
|
||||
// ha->resolveDegreeList();
|
||||
ha->setTextName(d->names.front());
|
||||
ha->render();
|
||||
}
|
||||
#if 0
|
||||
else {
|
||||
// This code won't be hit if MusicXML was at all straightforward,
|
||||
// as fromXml() is normally able to construct a chord description by itself.
|
||||
// The following is retained from previous revisions as a fallback,
|
||||
// in case fromXml fails but there is a match to be found in the tags from the chord list.
|
||||
d = ha->fromXml(kind, degreeList);
|
||||
if (d == 0) {
|
||||
QString degrees;
|
||||
foreach(const HDegree &d, degreeList) {
|
||||
if (!degrees.isEmpty())
|
||||
degrees += " ";
|
||||
degrees += d.text();
|
||||
}
|
||||
qDebug("unknown chord txt: <%s> kind: <%s> degrees: %s",
|
||||
qPrintable(kindText), qPrintable(kind), qPrintable(degrees));
|
||||
// Strategy II: lookup "kind", merge in degree list and try to find
|
||||
// harmony in list
|
||||
|
||||
d = ha->fromXml(kind);
|
||||
if (d) {
|
||||
ha->setId(d->id);
|
||||
foreach(const HDegree &d, degreeList)
|
||||
ha->addDegree(d);
|
||||
ha->resolveDegreeList();
|
||||
ha->render();
|
||||
}
|
||||
else {
|
||||
qDebug("'kind: <%s> not found in harmony data base", qPrintable(kind));
|
||||
QString s = tpc2name(ha->rootTpc(), score->style(ST_useGermanNoteNames).toBool()) + kindText;
|
||||
ha->setText(s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ha->setId(d->id);
|
||||
// ha->resolveDegreeList();
|
||||
ha->render();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ha->setVisible(printObject == "yes");
|
||||
|
||||
// TODO-LV: do this only if ha points to a valid harmony
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
<root>
|
||||
<root-step>G</root-step>
|
||||
</root>
|
||||
<kind text="">major</kind>
|
||||
<kind>major</kind>
|
||||
</harmony>
|
||||
<note>
|
||||
<pitch>
|
||||
|
@ -68,7 +68,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="">major</kind>
|
||||
<kind>major</kind>
|
||||
<frame>
|
||||
<frame-strings>6</frame-strings>
|
||||
<frame-frets>5</frame-frets>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="">major</kind>
|
||||
<kind>major</kind>
|
||||
</harmony>
|
||||
<note>
|
||||
<pitch>
|
||||
|
@ -102,7 +102,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="+">augmented</kind>
|
||||
<kind use-symbols="yes">augmented</kind>
|
||||
</harmony>
|
||||
<note>
|
||||
<pitch>
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="omit5">major</kind>
|
||||
<kind>major</kind>
|
||||
<degree>
|
||||
<degree-value>5</degree-value>
|
||||
<degree-alter>0</degree-alter>
|
||||
|
@ -112,7 +112,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="2">major</kind>
|
||||
<kind>major</kind>
|
||||
<degree>
|
||||
<degree-value>2</degree-value>
|
||||
<degree-alter>0</degree-alter>
|
||||
|
@ -150,7 +150,7 @@
|
|||
<root>
|
||||
<root-step>C</root-step>
|
||||
</root>
|
||||
<kind text="Maj7#5">major-seventh</kind>
|
||||
<kind text="Maj7">major-seventh</kind>
|
||||
<degree>
|
||||
<degree-value>5</degree-value>
|
||||
<degree-alter>1</degree-alter>
|
||||
|
@ -189,7 +189,7 @@
|
|||
<root-step>E</root-step>
|
||||
<root-alter>-1</root-alter>
|
||||
</root>
|
||||
<kind text="9sus">suspended-fourth</kind>
|
||||
<kind text="sus">suspended-fourth</kind>
|
||||
<degree>
|
||||
<degree-value>7</degree-value>
|
||||
<degree-alter>0</degree-alter>
|
||||
|
|
|
@ -1129,7 +1129,7 @@
|
|||
<chord id="213">
|
||||
<name>add2</name>
|
||||
<xml>major</xml>
|
||||
<degree>add9</degree>
|
||||
<degree>add2</degree>
|
||||
<voicing>C D E G</voicing>
|
||||
</chord>
|
||||
<chord id="214">
|
||||
|
|
Loading…
Reference in a new issue