Skip to content

Commit

Permalink
Merge pull request #2164 from notengrafik/pr/musicxml-harmony-degree
Browse files Browse the repository at this point in the history
Improve MusicXML import of chord symbol <degree> elements
  • Loading branch information
lpugin authored Apr 30, 2021
2 parents 2e3419b + 183e472 commit e9b7095
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 10 deletions.
3 changes: 2 additions & 1 deletion include/vrv/iomusxml.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,10 @@ class MusicXmlInput : public Input {
static pedalLog_DIR ConvertPedalTypeToDir(const std::string &value);
static tupletVis_NUMFORMAT ConvertTupletNumberValue(const std::string &value);
static std::wstring ConvertTypeToVerovioText(const std::string &value);
static std::string ConvertAlterToSymbol(const std::string &value);
static std::string ConvertAlterToSymbol(const std::string &value, bool plusMinus = false);
static std::string ConvertKindToSymbol(const std::string &value);
static std::string ConvertKindToText(const std::string &value);
static std::string ConvertDegreeToText(const pugi::xml_node harmony);
static std::string ConvertFigureGlyph(const std::string &value);
///@}

Expand Down
125 changes: 116 additions & 9 deletions src/iomusxml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2338,11 +2338,7 @@ void MusicXmlInput::ReadMusicXmlHarmony(pugi::xml_node node, Measure *measure, c
harmText = harmText + ConvertKindToText(GetContent(kind));
}
}
pugi::xml_node degree = node.child("degree");
if (degree) {
harmText += ConvertAlterToSymbol(degree.child("degree-alter").text().as_string())
+ degree.child("degree-value").text().as_string();
}
harmText += ConvertDegreeToText(node);
pugi::xml_node bass = node.child("bass");
if (bass) {
harmText += "/";
Expand Down Expand Up @@ -3814,7 +3810,7 @@ tupletVis_NUMFORMAT MusicXmlInput::ConvertTupletNumberValue(const std::string &v
return tupletVis_NUMFORMAT_NONE;
}

std::string MusicXmlInput::ConvertAlterToSymbol(const std::string &value)
std::string MusicXmlInput::ConvertAlterToSymbol(const std::string &value, bool plusMinus)
{
static const std::map<std::string, std::string> Alter2Symbol{
{ "-2", "𝄫" }, //
Expand All @@ -3824,9 +3820,25 @@ std::string MusicXmlInput::ConvertAlterToSymbol(const std::string &value)
{ "2", "𝄪" } //
};

const auto result = Alter2Symbol.find(value);
if (result != Alter2Symbol.end()) {
return result->second;
static const std::map<std::string, std::string> Alter2PlusMinus{
{ "-2", "--" }, //
{ "-1", "-" }, //
{ "0", "" }, //
{ "1", "+" }, //
{ "2", "++" } //
};

if (plusMinus) {
const auto result = Alter2PlusMinus.find(value);
if (result != Alter2PlusMinus.end()) {
return result->second;
}
}
else {
const auto result = Alter2Symbol.find(value);
if (result != Alter2Symbol.end()) {
return result->second;
}
}

return std::string();
Expand Down Expand Up @@ -3914,6 +3926,101 @@ std::string MusicXmlInput::ConvertKindToText(const std::string &value)
return std::string();
}

std::string MusicXmlInput::ConvertDegreeToText(pugi::xml_node harmony)
{
// Maps <kind> values to the first interval that can get an "add" prefix
static const std::map<std::string, int> Kind2FirstAddable{
{ "major", 9 }, //
{ "minor", 9 }, //
{ "augmented", 9 }, //
{ "diminished", 9 }, //
{ "dominant", 11 }, //
{ "major-seventh", 11 }, //
{ "minor-seventh", 11 }, //
{ "diminished-seventh", 11 }, //
{ "augmented-seventh", 11 }, //
{ "half-diminished", 11 }, //
{ "major-minor", 11 }, //
{ "major-sixth", 11 }, //
{ "minor-sixth", 11 },

// Skipping "dominant-ninth", "major-ninth" and "minor-ninth". An
// additional 13 would not get an "add", implying to omit the 11, as the
// 11 is regularly omitted anyway. Compare:
// https://music.stackexchange.com/questions/3732/

// Skipping "dominant-11th", "major-11th" and "minor-11th".
// 13 would no longer get an "add".

// Skipping "dominant-13th", "major-13th" and "minor-13th". Nothing to
// add anyway.

{ "suspended-second", 11 }, //
{ "suspended-fourth", 9 }, //

// Skipping "functional sixths": Neapolitan, Italian, French, German.
// Skipping pedal (pedal-point bass)

{ "power", 7 } //

// Skipping Tristan
};

std::string degreeText = "";

for (pugi::xml_node degree : harmony.children("degree")) {
if (degreeText == "") {
degreeText = "(";
}

pugi::xml_node typeNode = degree.child("degree-type");
const std::string type = typeNode.text().as_string();
pugi::xml_node valueNode = degree.child("degree-value");
if (!valueNode) {
// <degree-value> is required. Signal something is missing.
degreeText += "?";
continue;
}
const std::string degreeValue = valueNode.text().as_string();

if (typeNode.attribute("text")) {
degreeText += typeNode.attribute("text").as_string();
}
else {
if (type == "subtract") {
degreeText += "no";
}
else if (type == "add") {
const std::string kind = harmony.child("kind").text().as_string();
const auto result = Kind2FirstAddable.find(kind);

if (result != Kind2FirstAddable.end()) {
int firstAddable = result->second;
if (std::stoi(degreeValue) >= firstAddable) {
degreeText += "add";
}
}
}
}

pugi::xml_node alterNode = degree.child("degree-alter");
const std::string alter = alterNode.text().as_string();
// degree-alter value of 0 is not rendered as natural, it's omitted.
// (<degree-alter> is a required element, so assume it's there.)
if (alter != "0") {
const std::string plusMinus = alterNode.attribute("plus-minus").as_string();
degreeText += ConvertAlterToSymbol(alter, plusMinus == "yes");
}
degreeText += degreeValue;
}

if (degreeText != "") {
degreeText += ")";
}

return degreeText;
}

std::string MusicXmlInput::ConvertFigureGlyph(const std::string &value)
{
static const std::map<std::string, std::string> FigureGlyphMap{
Expand Down

0 comments on commit e9b7095

Please sign in to comment.