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
15 changes: 15 additions & 0 deletions src/include/mx/api/ChordData.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ enum class FrameBarre
stop
};

// Which side of the frame the first-fret label is printed on. Guitar chord diagrams conventionally
// carry it to the right of the grid.
enum class FirstFretLocation
{
unspecified,
left,
right
};

class FrameNoteData
{
public:
Expand Down Expand Up @@ -181,6 +190,10 @@ class FrameData
std::optional<std::string> unplayed;
int firstFret;
bool isFirstFretSpecified;
// The label printed beside the frame naming the fret it starts on, such as "5fr." for a frame
// whose top space is the fifth fret. Leave absent to print the fret number by itself.
std::optional<std::string> firstFretText;
FirstFretLocation firstFretLocation;
std::vector<FrameNoteData> notes;
};

Expand All @@ -190,6 +203,8 @@ MXAPI_EQUALS_MEMBER(fretCount)
MXAPI_EQUALS_MEMBER(unplayed)
MXAPI_EQUALS_MEMBER(firstFret)
MXAPI_EQUALS_MEMBER(isFirstFretSpecified)
MXAPI_EQUALS_MEMBER(firstFretText)
MXAPI_EQUALS_MEMBER(firstFretLocation)
MXAPI_EQUALS_MEMBER(notes)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(FrameData);
Expand Down
4 changes: 3 additions & 1 deletion src/private/mx/api/ChordData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ FrameNoteData::FrameNoteData()
{
}

FrameData::FrameData() : stringCount{6}, fretCount{4}, unplayed{}, firstFret{1}, isFirstFretSpecified{false}, notes{}
FrameData::FrameData()
: stringCount{6}, fretCount{4}, unplayed{}, firstFret{1}, isFirstFretSpecified{false}, firstFretText{},
firstFretLocation{FirstFretLocation::unspecified}, notes{}
{
}

Expand Down
15 changes: 15 additions & 0 deletions src/private/mx/impl/Converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,11 @@ const Converter::EnumMap<core::CancelLocation, api::CancelLocation> Converter::c
{core::CancelLocation::beforeBarline(), api::CancelLocation::beforeBarline},
};

const Converter::EnumMap<core::LeftRight, api::FirstFretLocation> Converter::firstFretLocationMap = {
{core::LeftRight::left(), api::FirstFretLocation::left},
{core::LeftRight::right(), api::FirstFretLocation::right},
};

// The standard <mode> vocabulary. api::KeyMode::unspecified and api::KeyMode::unsupported are absent
// because they have no wire spelling.
const Converter::EnumMap<core::Mode, api::KeyMode> Converter::keyModeMap = {
Expand Down Expand Up @@ -2003,6 +2008,16 @@ api::CancelLocation Converter::convert(core::CancelLocation value) const
return findApiItem(cancelLocationMap, api::CancelLocation::unspecified, value);
}

core::LeftRight Converter::convert(api::FirstFretLocation value) const
{
return findCoreItem(firstFretLocationMap, core::LeftRight::right(), value);
}

api::FirstFretLocation Converter::convert(core::LeftRight value) const
{
return findApiItem(firstFretLocationMap, api::FirstFretLocation::unspecified, value);
}

core::Mode Converter::convert(api::KeyMode value) const
{
return findCoreItem(keyModeMap, core::Mode{}, value);
Expand Down
5 changes: 5 additions & 0 deletions src/private/mx/impl/Converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "mx/core/generated/GroupSymbolValue.h"
#include "mx/core/generated/KindValue.h"
#include "mx/core/generated/LeftCenterRight.h"
#include "mx/core/generated/LeftRight.h"
#include "mx/core/generated/LineEnd.h"
#include "mx/core/generated/LineType.h"
#include "mx/core/generated/MeasureNumberingValue.h"
Expand Down Expand Up @@ -201,6 +202,9 @@ class Converter
core::CancelLocation convert(api::CancelLocation value) const;
api::CancelLocation convert(core::CancelLocation value) const;

core::LeftRight convert(api::FirstFretLocation value) const;
api::FirstFretLocation convert(core::LeftRight value) const;

// <mode> is an open vocabulary, so a core Mode holds an arbitrary string. api::KeyMode::unspecified
// and api::KeyMode::unsupported have no wire spelling and convert to an empty core::Mode; callers
// write no <mode> element for an empty Mode. A core Mode outside the standard vocabulary (including
Expand Down Expand Up @@ -294,6 +298,7 @@ class Converter
const static EnumMap<core::SoundID, api::SoundID> instrumentMap;
const static EnumMap<core::KindValue, api::ChordKind> kindMap;
const static EnumMap<core::CancelLocation, api::CancelLocation> cancelLocationMap;
const static EnumMap<core::LeftRight, api::FirstFretLocation> firstFretLocationMap;
const static EnumMap<core::Mode, api::KeyMode> keyModeMap;
const static EnumMap<core::TimeSymbol, api::TimeSignatureSymbol> simpleTimeSymbolMap;
const static EnumMap<core::TimeSymbol, api::ComplexTimeSymbol> complexTimeSymbolMap;
Expand Down
5 changes: 5 additions & 0 deletions src/private/mx/impl/DirectionReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,11 @@ void DirectionReader::parseHarmony(const core::Harmony &inHarmony, const core::H
{
chord.frameData.isFirstFretSpecified = true;
chord.frameData.firstFret = frame.firstFret()->value();
chord.frameData.firstFretText = frame.firstFret()->text();
if (frame.firstFret()->location().has_value())
{
chord.frameData.firstFretLocation = myConverter.convert(*frame.firstFret()->location());
}
}

for (const auto &frameNote : frame.frameNote())
Expand Down
8 changes: 8 additions & 0 deletions src/private/mx/impl/DirectionWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,14 @@ std::vector<core::MusicDataChoice> DirectionWriter::createHarmonyElements(int in
{
core::FirstFret firstFret{};
firstFret.setValue(chordIter->frameData.firstFret);
if (chordIter->frameData.firstFretText.has_value())
{
firstFret.setText(chordIter->frameData.firstFretText);
}
if (chordIter->frameData.firstFretLocation != api::FirstFretLocation::unspecified)
{
firstFret.setLocation(myConverter.convert(chordIter->frameData.firstFretLocation));
}
frame.setFirstFret(firstFret);
}

Expand Down
46 changes: 46 additions & 0 deletions src/private/mxtest/api/HarmonyExtrasApiTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,52 @@ TEST(harmonyFrameUnplayedRoundTrip, HarmonyExtrasApi)
std::optional<std::string>{"x"});
}

TEST(harmonyFrameFirstFretRoundTrip, HarmonyExtrasApi)
{
auto score = makeScoreWithChord();
auto &chord = chordOf(score);
chord.root = Step::c;
chord.chordKind = ChordKind::major;
chord.hasFrameData = true;
chord.frameData.isFirstFretSpecified = true;
chord.frameData.firstFret = 5;
chord.frameData.firstFretText = "5fr.";
chord.frameData.firstFretLocation = FirstFretLocation::right;

const auto xml = mxtest::toXml(score);
CHECK(xml.find("text=\"5fr.\"") != std::string::npos);
CHECK(xml.find("location=\"right\"") != std::string::npos);

const auto out = mxtest::roundTrip(score);
const auto &outFrame = firstChord(out).frameData;
CHECK(outFrame.isFirstFretSpecified);
CHECK_EQUAL(5, outFrame.firstFret);
CHECK(outFrame.firstFretText == std::optional<std::string>{"5fr."});
CHECK(FirstFretLocation::right == outFrame.firstFretLocation);
}

T_END;

TEST(harmonyFrameFirstFretBareRoundTrip, HarmonyExtrasApi)
{
auto score = makeScoreWithChord();
auto &chord = chordOf(score);
chord.root = Step::c;
chord.chordKind = ChordKind::major;
chord.hasFrameData = true;
chord.frameData.isFirstFretSpecified = true;
chord.frameData.firstFret = 3;

const auto xml = mxtest::toXml(score);
CHECK(xml.find("<first-fret>3</first-fret>") != std::string::npos);

const auto out = mxtest::roundTrip(score);
const auto &outFrame = firstChord(out).frameData;
CHECK_EQUAL(3, outFrame.firstFret);
CHECK(!outFrame.firstFretText.has_value());
CHECK(FirstFretLocation::unspecified == outFrame.firstFretLocation);
}

T_END;

TEST(harmonyFunctionRoundTrip, HarmonyExtrasApi)
Expand Down
1 change: 1 addition & 0 deletions src/private/mxtest/api/roundtrip-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ synthetic/beats.3.0.xml
synthetic/bracket.3.0.xml
synthetic/dashes.3.0.xml
synthetic/encoding-description.3.0.xml
synthetic/first-fret.3.0.xml
synthetic/first.4.0.xml
synthetic/group.3.0.xml
synthetic/instrument-abbreviation.3.0.xml
Expand Down
Loading