Skip to content
Open
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
10 changes: 7 additions & 3 deletions src/include/mx/api/MeasureData.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ class MeasureData
// this is the notes and other music data
std::vector<StaffData> staves;

TimeSignatureData timeSignature;
// resolved, carried-forward time signature(s) in effect for this measure. Normally a single
// entry with staffIndex == INDEX_UNSPECIFIED (applies to all staves). When staff-scoped time
// signatures are used, there is one entry per staff. Mirrors the isImplicit carry-forward
// semantics of the old singular field.
std::vector<TimeSignatureData> timeSignatures;

// an empty measureNumber indicates a normal
// measure number (i.e. the measure's
Expand Down Expand Up @@ -100,7 +104,7 @@ class MeasureData
std::vector<TransposeData> transpositions;

MeasureData()
: staves{}, timeSignature{}, number{}, measureNumbering{MeasureNumbering::unspecified},
: staves{}, timeSignatures{TimeSignatureData{}}, number{}, measureNumbering{MeasureNumbering::unspecified},
multiMeasureRest{VALUE_UNSPECIFIED}, implicit{Bool::unspecified}, nonControlling{Bool::unspecified},
width{DOUBLE_UNSPECIFIED}
{
Expand All @@ -109,7 +113,7 @@ class MeasureData

MXAPI_EQUALS_BEGIN(MeasureData)
MXAPI_EQUALS_MEMBER(staves)
MXAPI_EQUALS_MEMBER(timeSignature)
MXAPI_EQUALS_MEMBER(timeSignatures)
MXAPI_EQUALS_MEMBER(number)
MXAPI_EQUALS_MEMBER(measureNumbering)
MXAPI_EQUALS_MEMBER(multiMeasureRest)
Expand Down
50 changes: 40 additions & 10 deletions src/include/mx/api/TimeSignatureData.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "mx/api/ApiCommon.h"

#include <string>
#include <vector>

namespace mx
{
Expand All @@ -20,17 +21,35 @@ enum class TimeSignatureSymbol
singleNumber
};

class TimeSignatureData
// TimeSignatureComponent represents a single beats/beat-type pair. Most time signatures have
// exactly one component (e.g. "4/4"). Composite time signatures (e.g. "3+2/8" notated with
// multiple <time> children, or an additive meter written as separate beats/beat-type pairs)
// are represented by multiple components.
struct TimeSignatureComponent
{
public:
// common, cut
TimeSignatureSymbol symbol;

// the top number of the time signature, e.g. "5" in a "5/4" or "3+2" in a "(3+2)/8"
std::string beats;

// the bottom number of the time signature, e.g. "4" in a "5/4"
std::string beatType;
};

MXAPI_EQUALS_BEGIN(TimeSignatureComponent)
MXAPI_EQUALS_MEMBER(beats)
MXAPI_EQUALS_MEMBER(beatType)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TimeSignatureComponent);

class TimeSignatureData
{
public:
// common, cut
TimeSignatureSymbol symbol;

// one beats/beat-type pair per component. most time signatures have exactly one entry; a
// composite/additive time signature (e.g. multiple <time> beats/beat-type pairs) has more
// than one. use setSimple() to populate the common single-component case.
std::vector<TimeSignatureComponent> components;

// a time signature is implicit when it is not specified by the musicxml
bool isImplicit;
Expand All @@ -39,24 +58,35 @@ class TimeSignatureData
// implicit, the 'display' field will be ignored
Bool display;

// this value is optional. INDEX_UNSPECIFIED means unspecified. when value is
// unspecified it means that the time signature applies to all staves within the part
int staffIndex;

// convenience helper for the common case of a single beats/beat-type pair.
void setSimple(const std::string &beats, const std::string &beatType)
{
components.clear();
components.push_back(TimeSignatureComponent{beats, beatType});
}

inline bool isEqualTo(const TimeSignatureData &other) const
{
return (beats == other.beats) && (beatType == other.beatType) && (symbol == other.symbol);
return (components == other.components) && (symbol == other.symbol) && (staffIndex == other.staffIndex);
}

TimeSignatureData()
: symbol{TimeSignatureSymbol::unspecified}, beats{"4"}, beatType{"4"}, isImplicit{true},
display{Bool::unspecified}
: symbol{TimeSignatureSymbol::unspecified}, components{TimeSignatureComponent{"4", "4"}}, isImplicit{true},
display{Bool::unspecified}, staffIndex{INDEX_UNSPECIFIED}
{
}
};

MXAPI_EQUALS_BEGIN(TimeSignatureData)
MXAPI_EQUALS_MEMBER(symbol)
MXAPI_EQUALS_MEMBER(beats)
MXAPI_EQUALS_MEMBER(beatType)
MXAPI_EQUALS_MEMBER(components)
MXAPI_EQUALS_MEMBER(isImplicit)
MXAPI_EQUALS_MEMBER(display)
MXAPI_EQUALS_MEMBER(staffIndex)
MXAPI_EQUALS_END;
MXAPI_NOT_EQUALS_AND_VECTORS(TimeSignatureData);
} // namespace api
Expand Down
5 changes: 2 additions & 3 deletions src/private/mx/examples/Write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ int main(int argc, const char *argv[])
// add a measure
part.measures.emplace_back(MeasureData{});
auto &measure = part.measures.back();
measure.timeSignature.beats = "4";
measure.timeSignature.beatType = "4";
measure.timeSignature.isImplicit = false;
measure.timeSignatures.front().setSimple("4", "4");
measure.timeSignatures.front().isImplicit = false;

// add a staff
measure.staves.emplace_back(StaffData{});
Expand Down
2 changes: 1 addition & 1 deletion src/private/mx/impl/Cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace mx
namespace impl
{
Cursor::Cursor(int numStaves, int globalTicksPerQuarter)
: timeSignature{}, ticksPerQuarter(globalTicksPerQuarter), tickTimePosition(0), voiceIndex(0), staffIndex(0),
: timeSignatures{}, ticksPerQuarter(globalTicksPerQuarter), tickTimePosition(0), voiceIndex(0), staffIndex(0),
isBackupInProgress(false), isFirstMeasureInPart(true), isChordActive(false), myNumStaves(numStaves),
myGlobalTicksPerQuarter(globalTicksPerQuarter)
{
Expand Down
2 changes: 1 addition & 1 deletion src/private/mx/impl/Cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace impl
class Cursor
{
public:
api::TimeSignatureData timeSignature;
std::vector<api::TimeSignatureData> timeSignatures;
int ticksPerQuarter;
int tickTimePosition;
int voiceIndex;
Expand Down
78 changes: 68 additions & 10 deletions src/private/mx/impl/MeasureReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,24 +216,82 @@ impl::MeasureCursor MeasureReader::getCursor() const
void MeasureReader::parseTimeSignature() const
{
TimeReader timeReader{myPartwiseMeasure.musicData()};
api::TimeSignatureData timeSignature;

if (timeReader.getIsTimeFound())
std::vector<api::TimeSignatureData> carriedForward;
if (myCurrentCursor.measureIndex > 0)
{
timeSignature = timeReader.getTimeSignatureData();
timeSignature.isImplicit = false;
carriedForward = myPreviousMeasureCursor.timeSignatures;
}
else // no time signature was found

std::vector<api::TimeSignatureData> effective;

if (!timeReader.getIsTimeFound())
{
// no <time> this measure -> copy carried-forward list verbatim, force implicit. at
// measure 0 there is nothing to carry forward yet, so fall back to a single default
// (matches the pre-vector behavior of a default-constructed TimeSignatureData).
effective = carriedForward;
if (effective.empty())
{
effective.push_back(api::TimeSignatureData{});
}
for (auto &ts : effective)
{
ts.isImplicit = true;
}
}
else
{
if (myCurrentCursor.measureIndex > 0)
auto parsed = timeReader.getTimeSignatures();
bool anyUnscoped = false;
for (auto &ts : parsed)
{
timeSignature = myPreviousMeasureCursor.timeSignature;
ts.isImplicit = false;
if (ts.staffIndex != api::INDEX_UNSPECIFIED && ts.staffIndex > myCurrentCursor.getNumStaves() - 1)
{
ts.staffIndex = api::INDEX_UNSPECIFIED;
}
if (ts.staffIndex == api::INDEX_UNSPECIFIED)
{
anyUnscoped = true;
}
}

if (anyUnscoped)
{
// one or more unscoped entries -> the new effective list replaces everything
effective = std::move(parsed);
}
else
{
// all entries are staff-scoped -> merge into the carried-forward list by staffIndex
effective = carriedForward;
for (auto &e : effective)
{
e.isImplicit = true;
}
for (const auto &p : parsed)
{
bool replaced = false;
for (auto &e : effective)
{
if (e.staffIndex == p.staffIndex)
{
e = p;
replaced = true;
break;
}
}
if (!replaced)
{
effective.push_back(p);
}
}
}
timeSignature.isImplicit = true;
}

myOutMeasureData.timeSignature = timeSignature;
myCurrentCursor.timeSignature = timeSignature;
myOutMeasureData.timeSignatures = effective;
myCurrentCursor.timeSignatures = effective;
advanceTickTimePosition(0, "parseTimeSignature");
}

Expand Down
7 changes: 5 additions & 2 deletions src/private/mx/impl/MeasureWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,12 @@ void MeasureWriter::writeMeasureGlobals()
myPropertiesWriter->writePartSymbol(*myMeasureData.partSymbol);
}

if (!myMeasureData.timeSignature.isImplicit)
for (const auto &timeSignature : myMeasureData.timeSignatures)
{
myPropertiesWriter->writeTime(myMeasureData.timeSignature);
if (!timeSignature.isImplicit)
{
myPropertiesWriter->writeTime(timeSignature.staffIndex, timeSignature);
}
}

int localStaffCounter = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/private/mx/impl/PartReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ api::PartData PartReader::getPartData()
{
myOutPartData.transposition = transpositionData;
}
myCurrentCursor.timeSignature = measureData.timeSignature;
myCurrentCursor.timeSignatures = measureData.timeSignatures;
myCurrentCursor.ticksPerQuarter = reader.getCursor().ticksPerQuarter;
myOutPartData.measures.emplace_back(std::move(measureData));
++myCurrentCursor.measureIndex;
Expand Down
24 changes: 19 additions & 5 deletions src/private/mx/impl/PropertiesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,30 @@ void PropertiesWriter::writeTraditionalKey(const api::KeyData &inKeyData, core::
ioKey.setChoice(core::KeyChoice::traditionalKey(tkg));
}

void PropertiesWriter::writeTime(const api::TimeSignatureData &value)
void PropertiesWriter::writeTime(int staffIndex, const api::TimeSignatureData &value)
{
core::Time time{};

core::TimeSignatureGroup tsg{};
tsg.setBeats(value.beats);
tsg.setBeatType(value.beatType);
if (staffIndex >= 0)
{
time.setNumber(core::StaffNumber{staffIndex + 1});
}

std::vector<core::TimeSignatureGroup> groups;
groups.reserve(value.components.size());
for (const auto &component : value.components)
{
core::TimeSignatureGroup tsg{};
tsg.setBeats(component.beats);
tsg.setBeatType(component.beatType);
groups.push_back(std::move(tsg));
}

core::OneOrMore<core::TimeSignatureGroup> timeSigGroups{};
timeSigGroups.setItems(std::move(groups));

core::TimeChoiceGroup tcg{};
tcg.setTimeSignature(core::OneOrMore<core::TimeSignatureGroup>{tsg});
tcg.setTimeSignature(std::move(timeSigGroups));

time.setChoice(core::TimeChoice::group(tcg));

Expand Down
2 changes: 1 addition & 1 deletion src/private/mx/impl/PropertiesWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class PropertiesWriter
void writeKey(int staffIndex, const api::KeyData &inKeyData);
static void writeTraditionalKey(const api::KeyData &inKeyData, core::Key &ioKey);
static void writeNonTraditionalKey(const api::KeyData &inKeyData, core::Key &ioKey);
void writeTime(const api::TimeSignatureData &value);
void writeTime(int staffIndex, const api::TimeSignatureData &value);
void writeNumStaves(int value);
void writeStaffDetails(int staffIndex, int staffLines);
void writeStaffDetails(int staffIndex, int staffLines, double staffSize);
Expand Down
Loading
Loading