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
174 changes: 103 additions & 71 deletions data/corpus.xml

Large diffs are not rendered by default.

121 changes: 121 additions & 0 deletions data/synthetic/tuplet-nested.3.0.features.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by `python3 -m audit files`. The element/attribute surface of the
sibling corpus file. Do not edit by hand; regenerate with the audit tool. -->
<feature-audit>
<audited>synthetic/tuplet-nested.3.0.xml</audited>
<audited-version>3.0</audited-version>
<features>
<feature>
<name>actual-notes</name>
</feature>
<feature>
<name>attributes</name>
</feature>
<feature>
<name>beat-type</name>
</feature>
<feature>
<name>beats</name>
</feature>
<feature>
<name>clef</name>
</feature>
<feature>
<name>divisions</name>
</feature>
<feature>
<name>duration</name>
</feature>
<feature>
<name>fifths</name>
</feature>
<feature>
<name>key</name>
</feature>
<feature>
<name>line</name>
</feature>
<feature>
<name>measure</name>
<attributes>
<attribute>number</attribute>
</attributes>
</feature>
<feature>
<name>normal-notes</name>
</feature>
<feature>
<name>notations</name>
</feature>
<feature>
<name>note</name>
</feature>
<feature>
<name>octave</name>
</feature>
<feature>
<name>part</name>
<attributes>
<attribute>id</attribute>
</attributes>
</feature>
<feature>
<name>part-list</name>
</feature>
<feature>
<name>part-name</name>
</feature>
<feature>
<name>pitch</name>
</feature>
<feature>
<name>score-part</name>
<attributes>
<attribute>id</attribute>
</attributes>
</feature>
<feature>
<name>score-partwise</name>
<attributes>
<attribute>version</attribute>
</attributes>
</feature>
<feature>
<name>sign</name>
</feature>
<feature>
<name>step</name>
</feature>
<feature>
<name>time</name>
</feature>
<feature>
<name>time-modification</name>
</feature>
<feature>
<name>tuplet</name>
<attributes>
<attribute>number</attribute>
<attribute>type</attribute>
</attributes>
</feature>
<feature>
<name>tuplet-actual</name>
</feature>
<feature>
<name>tuplet-normal</name>
</feature>
<feature>
<name>tuplet-number</name>
</feature>
<feature>
<name>tuplet-type</name>
</feature>
<feature>
<name>type</name>
</feature>
<feature>
<name>voice</name>
</feature>
</features>
</feature-audit>
110 changes: 110 additions & 0 deletions data/synthetic/tuplet-nested.3.0.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<score-partwise version="3.0">
<part-list>
<score-part id="P1">
<part-name>Nested Tuplets</part-name>
</score-part>
</part-list>
<part id="P1">
<measure number="1">
<attributes>
<divisions>36</divisions>
<key>
<fifths>0</fifths>
</key>
<time>
<beats>2</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>C</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>eighth</type>
<time-modification>
<actual-notes>9</actual-notes>
<normal-notes>4</normal-notes>
</time-modification>
<notations>
<tuplet type="start" number="1"/>
<tuplet type="start" number="2">
<tuplet-actual>
<tuplet-number>3</tuplet-number>
<tuplet-type>eighth</tuplet-type>
</tuplet-actual>
<tuplet-normal>
<tuplet-number>2</tuplet-number>
<tuplet-type>eighth</tuplet-type>
</tuplet-normal>
</tuplet>
</notations>
</note>
<note>
<pitch>
<step>D</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>eighth</type>
<time-modification>
<actual-notes>9</actual-notes>
<normal-notes>4</normal-notes>
</time-modification>
</note>
<note>
<pitch>
<step>E</step>
<octave>5</octave>
</pitch>
<duration>8</duration>
<voice>1</voice>
<type>eighth</type>
<time-modification>
<actual-notes>9</actual-notes>
<normal-notes>4</normal-notes>
</time-modification>
<notations>
<tuplet type="stop" number="2"/>
</notations>
</note>
<note>
<pitch>
<step>F</step>
<octave>5</octave>
</pitch>
<duration>24</duration>
<voice>1</voice>
<type>quarter</type>
<time-modification>
<actual-notes>3</actual-notes>
<normal-notes>2</normal-notes>
</time-modification>
</note>
<note>
<pitch>
<step>G</step>
<octave>5</octave>
</pitch>
<duration>24</duration>
<voice>1</voice>
<type>quarter</type>
<time-modification>
<actual-notes>3</actual-notes>
<normal-notes>2</normal-notes>
</time-modification>
<notations>
<tuplet type="stop" number="1"/>
</notations>
</note>
</measure>
</part>
</score-partwise>
2 changes: 1 addition & 1 deletion src/include/mx/api/ApiCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ constexpr int TICK_TIME_INFINITY = std::numeric_limits<int>::max();
// "unspecified" or "absent". Do not use these for new fields; new absent-able fields use
// std::optional instead (see "mx::api conventions" in AGENTS.md and issue #249).
constexpr int INDEX_UNSPECIFIED = -1; // staff/part indices and numbers, e.g. KeyData::staffIndex
constexpr int NUMBER_LEVEL_UNSPECIFIED = -1; // MusicXML 'number' attributes, e.g. TupletStart::numberLevel
constexpr int NUMBER_LEVEL_UNSPECIFIED = -1; // MusicXML 'number' attributes, e.g. SpannerNumber::level
constexpr int VALUE_UNSPECIFIED = -1; // other absent-able ints, e.g. DirectionData::voice
constexpr Double DOUBLE_UNSPECIFIED = -1.0; // absent-able doubles, e.g. StaffData::staffSize

Expand Down
78 changes: 64 additions & 14 deletions src/include/mx/api/TupletData.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "mx/api/Id.h"
#include "mx/api/LineData.h"
#include "mx/api/PositionData.h"
#include "mx/api/SpannerNumber.h"

#include <optional>
#include <string>
Expand All @@ -22,12 +23,22 @@ namespace api
// is the '2' and the 'normal' part of the
// ratio is the '3'

// The shape of a tuplet's bracket. MusicXML calls the older, slur-like shape 'curved'.
enum class TupletLineShape
{
unspecified,
straight,
curved
};

class TupletStart
{
public:
// used to id when nested
// NUMBER_LEVEL_UNSPECIFIED means unspecified
int numberLevel;
// Tells this tuplet apart from another one that is open at the same time, which happens when
// one tuplet is written inside another. Leave it alone for a tuplet that stands on its own.
// Give both ends of a tuplet the same identity when tuplets are nested, and mx numbers them
// for you; an explicit level is written out as it stands.
SpannerNumber number;

PositionData positionData;

Expand All @@ -48,40 +59,73 @@ class TupletStart
// a triplet or the 4 in 5:4 for quintuplet
Bool showNormalNumber;

// Whether the note value of the tuplet's own notes is shown beside the bracket, for example the
// eighth note in "3 eighths". This is optional. Left unspecified, the file does not say, and a
// reader leaves the note values off, which is what MusicXML asks for. Set it to yes to show
// them or to no to keep them off.
Bool showActualType;

// Whether the note value of the 'normal' notes is shown as well, for example the eighth note in
// "in the time of 2 eighths". Also optional. MusicXML states both sides in a single attribute,
// so this field only takes effect when showActualType is yes.
Bool showNormalType;

Bool bracket;

// Whether the bracket is drawn as a straight line or in the older curved style.
TupletLineShape lineShape;

// Whether the actual and normal figures above are spelled out in the file next to the notes.
// mx works this out on its own, so leave both unspecified; they are here for the rare case
// where you want to decide instead.
//
// Unspecified spells out a figure when it says something the notes do not already say. Nested
// tuplets are where that happens. When one tuplet is written inside another, each note counts
// against both tuplets at once, so the note by itself cannot tell you the inner tuplet's own
// ratio, and the tuplet has to state it. A tuplet standing on its own agrees with its notes and
// is left plain. Setting yes spells the figure out anyway, and no leaves it out.
//
// Reading a file sets yes for each figure the file spelled out, so that a score read and
// written back keeps what it had.
Bool writeActual;
Bool writeNormal;

// The <tuplet> element's id attribute (see Id.h).
std::optional<Id> id;

TupletStart()
: numberLevel{NUMBER_LEVEL_UNSPECIFIED}, positionData{}, actualNumber{VALUE_UNSPECIFIED},
actualDurationName{api::DurationName::unspecified}, actualDots{VALUE_UNSPECIFIED},
normalNumber{VALUE_UNSPECIFIED}, normalDurationName{api::DurationName::unspecified},
normalDots{VALUE_UNSPECIFIED}, showActualNumber{Bool::unspecified}, showNormalNumber{Bool::unspecified},
bracket{Bool::unspecified}, id{}
: number{}, positionData{}, actualNumber{VALUE_UNSPECIFIED}, actualDurationName{api::DurationName::unspecified},
actualDots{VALUE_UNSPECIFIED}, normalNumber{VALUE_UNSPECIFIED},
normalDurationName{api::DurationName::unspecified}, normalDots{VALUE_UNSPECIFIED},
showActualNumber{Bool::unspecified}, showNormalNumber{Bool::unspecified}, showActualType{Bool::unspecified},
showNormalType{Bool::unspecified}, bracket{Bool::unspecified}, lineShape{TupletLineShape::unspecified},
writeActual{Bool::unspecified}, writeNormal{Bool::unspecified}, id{}
{
}
};

class TupletStop
{
public:
// used to id when nested
// NUMBER_LEVEL_UNSPECIFIED means unspecified
int numberLevel;
// Matches the number of the TupletStart this ends. See TupletStart::number.
SpannerNumber number;

PositionData positionData;

// Whether a bracket is drawn. MusicXML allows the attribute on both ends of a tuplet, and
// some programs state it only on the stop.
Bool bracket;

// The <tuplet> element's id attribute (see Id.h).
std::optional<Id> id;

TupletStop() : numberLevel{NUMBER_LEVEL_UNSPECIFIED}, positionData{}, id{}
TupletStop() : number{}, positionData{}, bracket{Bool::unspecified}, id{}
{
}
};

MXAPI_EQUALS_BEGIN(TupletStart)
MXAPI_EQUALS_MEMBER(numberLevel)
MXAPI_EQUALS_MEMBER(number)
MXAPI_EQUALS_MEMBER(positionData)
MXAPI_EQUALS_MEMBER(actualNumber)
MXAPI_EQUALS_MEMBER(actualDurationName)
Expand All @@ -91,14 +135,20 @@ MXAPI_EQUALS_MEMBER(normalDurationName)
MXAPI_EQUALS_MEMBER(normalDots)
MXAPI_EQUALS_MEMBER(showActualNumber)
MXAPI_EQUALS_MEMBER(showNormalNumber)
MXAPI_EQUALS_MEMBER(showActualType)
MXAPI_EQUALS_MEMBER(showNormalType)
MXAPI_EQUALS_MEMBER(bracket)
MXAPI_EQUALS_MEMBER(lineShape)
MXAPI_EQUALS_MEMBER(writeActual)
MXAPI_EQUALS_MEMBER(writeNormal)
MXAPI_EQUALS_MEMBER(id)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TupletStart);

MXAPI_EQUALS_BEGIN(TupletStop)
MXAPI_EQUALS_MEMBER(numberLevel)
MXAPI_EQUALS_MEMBER(number)
MXAPI_EQUALS_MEMBER(positionData)
MXAPI_EQUALS_MEMBER(bracket)
MXAPI_EQUALS_MEMBER(id)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TupletStop);
Expand Down
Loading
Loading