Skip to content
Closed
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
7 changes: 7 additions & 0 deletions src/include/mx/api/DirectionData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"
#include "mx/api/ChordData.h"
#include "mx/api/CodaData.h"
#include "mx/api/EditorialData.h"
#include "mx/api/FiguredBassData.h"
#include "mx/api/MarkData.h"
#include "mx/api/OttavaData.h"
Expand Down Expand Up @@ -102,6 +103,10 @@ struct DirectionData
bool isSoundDataSpecified;
SoundData soundData;

// Editorial (<footnote>/<level>) carried on the <direction>. The editorial-voice <voice> is
// already represented by the `voice` member above.
EditorialData editorial;

std::vector<TempoData> tempos;
std::vector<MarkData> marks;
std::vector<WedgeStart> wedgeStarts;
Expand Down Expand Up @@ -142,6 +147,7 @@ inline bool isDirectionDataEmpty(const DirectionData &directionData)
directionData.ottavaStops.size() == 0 && directionData.words.size() == 0 &&
directionData.segnos.size() == 0 && directionData.codas.size() == 0 &&
directionData.figuredBasses.size() == 0 && !directionData.isSoundDataSpecified &&
!directionData.editorial.isFootnoteSpecified && !directionData.editorial.isLevelSpecified &&
directionData.orderedComponents.size() == 0;
}

Expand All @@ -152,6 +158,7 @@ MXAPI_EQUALS_MEMBER(voice)
MXAPI_EQUALS_MEMBER(isStaffValueSpecified)
MXAPI_EQUALS_MEMBER(isSoundDataSpecified)
MXAPI_EQUALS_MEMBER(soundData)
MXAPI_EQUALS_MEMBER(editorial)
MXAPI_EQUALS_MEMBER(tempos)
MXAPI_EQUALS_MEMBER(marks)
MXAPI_EQUALS_MEMBER(wedgeStarts)
Expand Down
40 changes: 40 additions & 0 deletions src/include/mx/api/EditorialData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/FootnoteData.h"
#include "mx/api/LevelData.h"

namespace mx
{
namespace api
{
// The MusicXML editorial group (<footnote> + <level>) carries editorial information for a parent
// element. It appears on several elements (e.g. <part-group>, <direction>); this reusable type
// captures it once. The is...Specified flags distinguish "absent" (write nothing back) from a
// present-but-empty child.
class EditorialData
{
public:
bool isFootnoteSpecified;
FootnoteData footnote;
bool isLevelSpecified;
LevelData level;

EditorialData() : isFootnoteSpecified{false}, footnote{}, isLevelSpecified{false}, level{}
{
}
};

MXAPI_EQUALS_BEGIN(EditorialData)
MXAPI_EQUALS_MEMBER(isFootnoteSpecified)
MXAPI_EQUALS_MEMBER(footnote)
MXAPI_EQUALS_MEMBER(isLevelSpecified)
MXAPI_EQUALS_MEMBER(level)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(EditorialData);
} // namespace api
} // namespace mx
43 changes: 43 additions & 0 deletions src/include/mx/api/FootnoteData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#pragma once

#include "mx/api/ApiCommon.h"
#include "mx/api/ColorData.h"
#include "mx/api/FontData.h"
#include "mx/api/PositionData.h"

#include <string>

namespace mx
{
namespace api
{
// The MusicXML <footnote> element is a formatted-text: text content plus the position, font, and
// color attribute groups. Modeled to match WordsData (the api's other formatted-text surface).
class FootnoteData
{
public:
std::string text;
PositionData positionData;
FontData fontData;
bool isColorSpecified;
ColorData colorData;

FootnoteData() : text{}, positionData{}, fontData{}, isColorSpecified{false}, colorData{}
{
}
};

MXAPI_EQUALS_BEGIN(FootnoteData)
MXAPI_EQUALS_MEMBER(text)
MXAPI_EQUALS_MEMBER(positionData)
MXAPI_EQUALS_MEMBER(fontData)
MXAPI_EQUALS_MEMBER(isColorSpecified)
MXAPI_EQUALS_MEMBER(colorData)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(FootnoteData);
} // namespace api
} // namespace mx
66 changes: 66 additions & 0 deletions src/include/mx/api/LevelData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// MusicXML Class Library
// Copyright (c) by Matthew James Briggs
// Distributed under the MIT License

#pragma once

#include "mx/api/ApiCommon.h"

#include <string>

namespace mx
{
namespace api
{
// Mirrors the MusicXML <level> 'type' attribute (start-stop-single): whether the editorial
// information applies to the start of a series of symbols, the end, or a single symbol.
enum class StartStopSingle
{
unspecified,
start,
stop,
single
};

// Mirrors the MusicXML <level> 'size' attribute (symbol-size).
Comment on lines +17 to +25

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This strikes me as one of things we should try to simplify in the mx::api model instead of mirroring the way MusicXML models it. In general, we are trying to get away from MusicXML's procedural "stop"/"start" semantics (i.e. requiring stateful parsing) and represent data more statically whenever we can.

enum class SymbolSize
{
unspecified,
full,
cue,
graceCue,
large
};

// The MusicXML <level> element specifies editorial information for the parent element. Its text
// content is descriptive; the attributes control how the editorial marking is rendered. An
// `unspecified` enum (or empty `value`) means the source carried no such attribute and none is
// written back.
class LevelData
{
public:
std::string value;
Bool reference;
StartStopSingle type;
Bool parentheses;
Bool bracket;
SymbolSize size;

LevelData()
: value{}, reference{Bool::unspecified}, type{StartStopSingle::unspecified}, parentheses{Bool::unspecified},
bracket{Bool::unspecified}, size{SymbolSize::unspecified}
{
}
};

MXAPI_EQUALS_BEGIN(LevelData)
MXAPI_EQUALS_MEMBER(value)
MXAPI_EQUALS_MEMBER(reference)
MXAPI_EQUALS_MEMBER(type)
MXAPI_EQUALS_MEMBER(parentheses)
MXAPI_EQUALS_MEMBER(bracket)
MXAPI_EQUALS_MEMBER(size)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(LevelData);
} // namespace api
} // namespace mx
8 changes: 7 additions & 1 deletion src/include/mx/api/PartGroupData.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#pragma once

#include "mx/api/EditorialData.h"

#include <string>
#include <vector>

Expand Down Expand Up @@ -59,7 +61,10 @@ class PartGroupData
BracketType bracketType;
GroupBarline groupBarline;
// TODO - group time
// TODO - group editorial

// Editorial (<footnote>/<level>) carried on the part-group *start*. MusicXML ignores child
// values at the group stop, so stop-group editorial is intentionally not modeled.
EditorialData editorial;

// The number attribute is used to distinguish overlapping
// and nested part-groups, not the sequence of groups.
Expand All @@ -82,6 +87,7 @@ MXAPI_EQUALS_MEMBER(abbreviation)
MXAPI_EQUALS_MEMBER(displayAbbreviation)
MXAPI_EQUALS_MEMBER(bracketType)
MXAPI_EQUALS_MEMBER(groupBarline)
MXAPI_EQUALS_MEMBER(editorial)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(PartGroupData);
} // namespace api
Expand Down
33 changes: 33 additions & 0 deletions src/private/mx/impl/Converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ const Converter::EnumMap<core::YesNo, api::Bool> Converter::boolMap = {
{core::YesNo::no(), api::Bool::no},
};

const Converter::EnumMap<core::StartStopSingle, api::StartStopSingle> Converter::startStopSingleMap = {
{core::StartStopSingle::start(), api::StartStopSingle::start},
{core::StartStopSingle::stop(), api::StartStopSingle::stop},
{core::StartStopSingle::single(), api::StartStopSingle::single},
};

const Converter::EnumMap<core::SymbolSize, api::SymbolSize> Converter::symbolSizeMap = {
{core::SymbolSize::full(), api::SymbolSize::full},
{core::SymbolSize::cue(), api::SymbolSize::cue},
{core::SymbolSize::graceCue(), api::SymbolSize::graceCue},
{core::SymbolSize::large(), api::SymbolSize::large},
};

const Converter::EnumMap<core::Valign, api::VerticalAlignment> Converter::valignMap = {
{core::Valign::baseline(), api::VerticalAlignment::baseline},
{core::Valign::bottom(), api::VerticalAlignment::bottom},
Expand Down Expand Up @@ -1412,6 +1425,26 @@ core::YesNo Converter::convert(api::Bool value) const
return findCoreItem(boolMap, core::YesNo::no(), value);
}

core::StartStopSingle Converter::convert(api::StartStopSingle value) const
{
return findCoreItem(startStopSingleMap, core::StartStopSingle::single(), value);
}

api::StartStopSingle Converter::convert(core::StartStopSingle value) const
{
return findApiItem(startStopSingleMap, api::StartStopSingle::unspecified, value);
}

core::SymbolSize Converter::convert(api::SymbolSize value) const
{
return findCoreItem(symbolSizeMap, core::SymbolSize::full(), value);
}

api::SymbolSize Converter::convert(core::SymbolSize value) const
{
return findApiItem(symbolSizeMap, api::SymbolSize::unspecified, value);
}

api::VerticalAlignment Converter::convert(core::Valign value) const
{
return findApiItem(valignMap, api::VerticalAlignment::unspecified, value);
Expand Down
10 changes: 10 additions & 0 deletions src/private/mx/impl/Converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@
#include "mx/core/generated/RightLeftMiddle.h"
#include "mx/core/generated/SoundID.h"
#include "mx/core/generated/StartStopDiscontinue.h"
#include "mx/core/generated/StartStopSingle.h"
#include "mx/core/generated/StemValue.h"
#include "mx/core/generated/Step.h"
#include "mx/core/generated/SymbolSize.h"
#include "mx/core/generated/TechnicalChoice.h"
#include "mx/core/generated/Transpose.h"
#include "mx/core/generated/Valign.h"
Expand Down Expand Up @@ -86,6 +88,12 @@ class Converter
core::YesNo convert(api::Bool value) const;
api::Bool convert(core::YesNo value) const;

core::StartStopSingle convert(api::StartStopSingle value) const;
api::StartStopSingle convert(core::StartStopSingle value) const;

core::SymbolSize convert(api::SymbolSize value) const;
api::SymbolSize convert(core::SymbolSize value) const;

core::Valign convert(api::VerticalAlignment value) const;
api::VerticalAlignment convert(core::Valign value) const;

Expand Down Expand Up @@ -169,6 +177,8 @@ class Converter
const static EnumMap<core::ClefSign, api::ClefSymbol> clefMap;
const static EnumMap<core::AboveBelow, api::Placement> placementMap;
const static EnumMap<core::YesNo, api::Bool> boolMap;
const static EnumMap<core::StartStopSingle, api::StartStopSingle> startStopSingleMap;
const static EnumMap<core::SymbolSize, api::SymbolSize> symbolSizeMap;
const static EnumMap<core::Valign, api::VerticalAlignment> valignMap;
const static EnumMap<core::LeftCenterRight, api::HorizontalAlignment> halignMap;
const static EnumMap<core::CSSFontSize, api::CssSize> cssMap;
Expand Down
3 changes: 3 additions & 0 deletions src/private/mx/impl/DirectionReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include "mx/core/generated/WedgeType.h"
#include "mx/core/generated/YesNo.h"
#include "mx/impl/DynamicsReader.h"
#include "mx/impl/EditorialFunctions.h"
#include "mx/impl/MarkDataFunctions.h"
#include "mx/impl/MetronomeReader.h"
#include "mx/impl/PrintFunctions.h"
Expand Down Expand Up @@ -171,6 +172,8 @@ void DirectionReader::parseValues()
myOutDirectionData.soundData = std::move(soundData);
}
}

myOutDirectionData.editorial = getEditorialData(myDirection->editorialVoiceDirection());
}
else if (myHarmony)
{
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 @@ -66,6 +66,7 @@
#include "mx/core/generated/WedgeType.h"
#include "mx/core/generated/YesNo.h"
#include "mx/impl/DynamicsWriter.h"
#include "mx/impl/EditorialFunctions.h"
#include "mx/impl/FontFunctions.h"
#include "mx/impl/LineFunctions.h"
#include "mx/impl/MarkDataFunctions.h"
Expand Down Expand Up @@ -112,6 +113,13 @@ std::vector<core::MusicDataChoice> DirectionWriter::getDirectionLikeThings()
direction.setOffset(coreOffset);
}

if (myDirectionData.editorial.isFootnoteSpecified || myDirectionData.editorial.isLevelSpecified)
{
core::EditorialVoiceDirectionGroup editorial{};
setEditorial(myDirectionData.editorial, editorial);
direction.setEditorialVoiceDirection(std::move(editorial));
}

for (auto mark : myDirectionData.marks)
{
mark.tickTimePosition = myDirectionData.tickTimePosition;
Expand Down
Loading
Loading