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
6 changes: 6 additions & 0 deletions src/include/mx/api/ScoreData.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ class ScoreData
// The <rights> type attribute for `copyright` (above).
std::optional<std::string> copyrightType;

// Where the music came from: the edition, manuscript, or other publication this score was
// made from. This is MusicXML's <source> element. It names the origin of the music, not of
// the file. Leave it empty when the score does not name one.
std::optional<std::string> source;

EncodingData encoding;
std::vector<PageTextData> pageTextItems;

Expand Down Expand Up @@ -118,6 +123,7 @@ MXAPI_EQUALS_MEMBER(arranger)
MXAPI_EQUALS_MEMBER(publisher)
MXAPI_EQUALS_MEMBER(copyright)
MXAPI_EQUALS_MEMBER(copyrightType)
MXAPI_EQUALS_MEMBER(source)
MXAPI_EQUALS_MEMBER(encoding)
MXAPI_EQUALS_MEMBER(pageTextItems)
MXAPI_EQUALS_MEMBER(pageImageItems)
Expand Down
2 changes: 1 addition & 1 deletion src/private/mx/api/ScoreData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace api
ScoreData::ScoreData()
: musicXmlVersion{api::MusicXmlVersion::unspecified}, declaredMusicXmlVersion{}, musicXmlType{"partwise"},
workTitle{}, workNumber{}, movementTitle{}, movementNumber{}, composer{}, lyricist{}, copyright{},
copyrightType{"copyright"}, encoding{}, pageTextItems{}, defaults{}, parts{}, partGroups{},
copyrightType{"copyright"}, source{}, encoding{}, pageTextItems{}, defaults{}, parts{}, partGroups{},
ticksPerQuarter{DEFAULT_TICKS_PER_QUARTER}
{
}
Expand Down
2 changes: 2 additions & 0 deletions src/private/mx/impl/ScoreReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ api::ScoreData ScoreReader::getScoreData() const
myOutScoreData.copyrightType = std::nullopt;
}
}
myOutScoreData.source = ident.source();

api::EncodingData encodingData;

if (ident.encoding().has_value())
Expand Down
6 changes: 6 additions & 0 deletions src/private/mx/impl/ScoreWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ core::ScorePartwise ScoreWriter::getScorePartwise() const
hasIdentification = true;
}

if (myScoreData.source.has_value())
{
identification.setSource(myScoreData.source);
hasIdentification = true;
}

if (hasIdentification)
{
header.setIdentification(identification);
Expand Down
69 changes: 69 additions & 0 deletions src/private/mxtest/api/IdentificationSourceApiTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#include "mxtest/control/CompileControl.h"
#ifdef MX_COMPILE_API_TESTS

#include "cpul/cpulTestHarness.h"
#include "mx/api/DocumentManager.h"
#include "mxtest/api/RoundTrip.h"
#include "mxtest/api/TestHelpers.h"

using namespace std;
using namespace mx::api;
using namespace mxtest;

// An otherwise empty one-part score, so the only thing under test is the header.
ScoreData identificationSourceMakeScore()
{
ScoreData score;
score.parts.emplace_back();
score.parts.back().uniqueId = "P1";
score.parts.back().measures.emplace_back();
score.parts.back().measures.back().staves.emplace_back();
return score;
}

TEST(sourceSurvivesRoundTrip, IdentificationSource)
{
auto score = identificationSourceMakeScore();
score.source = "Bach-Gesellschaft Ausgabe, Band 3";

const auto out = roundTrip(score);
REQUIRE(out.source.has_value());
CHECK_EQUAL("Bach-Gesellschaft Ausgabe, Band 3", *out.source);
}

TEST(sourceWritesTheMusicXmlElement, IdentificationSource)
{
auto score = identificationSourceMakeScore();
score.source = "Urtext";

const auto xml = toXml(score);
CHECK(xml.find("<source>Urtext</source>") != std::string::npos);
}

TEST(anEmptySourceWritesNothing, IdentificationSource)
{
const auto score = identificationSourceMakeScore();
const auto xml = toXml(score);
CHECK(xml.find("<source>") == std::string::npos);

const auto out = roundTrip(score);
CHECK(!out.source.has_value());
}

// MusicXML allows <source></source>, which says the score named a source and left it blank.
// That is a different fact from saying nothing at all, so both have to survive the round trip.
TEST(aBlankSourceIsNotTheSameAsNoSource, IdentificationSource)
{
auto score = identificationSourceMakeScore();
score.source = "";

const auto out = roundTrip(score);
REQUIRE(out.source.has_value());
CHECK_EQUAL("", *out.source);
}

#endif
9 changes: 9 additions & 0 deletions src/private/mxtest/api/roundtrip-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -686,3 +686,12 @@ foundsuite/Invention_6.xml
foundsuite/Invention_7.xml
foundsuite/Invention_8.xml
foundsuite/Invention_9.xml

# Unblocked by ScoreData::source: <identification>/<source> names the edition or
# manuscript a score was made from, and the api had nowhere to keep it, so the
# element was dropped on write.
musetrainer/Canon_in_D_easy.xml
musetrainer/Carol_of_the_Bells_easy_piano.xml
musetrainer/Lacrimosa_-_Requiem.xml
musetrainer/Prelude_I_in_C_major_BWV_846_-_Well_Tempered_Clavier_First_Book.xml
synthetic/source.3.0.xml
Loading