diff --git a/.claude/settings.json b/.claude/settings.json index ef7f1c63f..89f051566 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,15 +1,4 @@ { - "permissions": { - "allow": [ - "Agent", - "Edit", - "WebSearch", - "WebFetch", - "Write", - "Read", - "Bash" - ] - }, "hooks": { "SessionStart": diff --git a/.gitignore b/.gitignore index c1dc32919..3c9c99c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ build/ build-*/ cmake-build-* +.cache/ src/private/mxtest/file/PathRoot.h *.DS_Store compile_commands.json diff --git a/data/rpatters1/timesigs_composite-ref-modified.musicxml b/data/rpatters1/timesigs_composite-ref-modified.musicxml new file mode 100644 index 000000000..04e35fedd --- /dev/null +++ b/data/rpatters1/timesigs_composite-ref-modified.musicxml @@ -0,0 +1,116 @@ + + + + + + Finale v27.4 for Mac + 2026-06-30 + + + + + + + + + + 7.2319 + 40 + + + 1545 + 1194 + + 70 + 70 + 88 + 88 + + + + + 0 + 0 + + 121 + 70 + + + 0.7487 + 5 + 0.7487 + 0.7487 + 5 + 0.7487 + 0.7487 + 0.7487 + 0.7487 + 0.7487 + 60 + 60 + 60 + 7.5 + + + + + + + MusicXML Part + + SmartMusic SoftSynth 1 + + + 1 + 15489 + 1 + 80 + 0 + + + + + + + + + + 70 + 0 + + 211 + + system + + + 2 + + 0 + major + + + + G + 2 + + + + + + 33 + 1 + + + light-heavy + + + + + diff --git a/src/include/mx/api/ComplexTimeSignature.h b/src/include/mx/api/ComplexTimeSignature.h new file mode 100644 index 000000000..8fb681339 --- /dev/null +++ b/src/include/mx/api/ComplexTimeSignature.h @@ -0,0 +1,160 @@ +// MusicXML Class Library +// Copyright (c) by Matthew James Briggs +// Distributed under the MIT License + +#pragma once + +#include "mx/api/ApiCommon.h" +#include "mx/api/TimeSignatureData.h" + +#include +#include +#include +#include + +namespace mx +{ +namespace api +{ + +// The full MusicXML time-symbol vocabulary. Used for a complex meter's own symbol and for an +// interchangeable pair's symbol. Unlike the narrow TimeSignatureSymbol (common/cut only), this +// includes the unusual display modes that force a signature into the complex arena. +enum class ComplexTimeSymbol +{ + unspecified, // ordinary fractional display + common, // the C glyph + cut, // the slashed-C glyph + singleNumber, // the meter drawn as a single number + note, // the beat-type drawn as a downstem note glyph + dottedNote // the beat-type drawn as a dotted downstem note glyph +}; + +// The line drawn between beats and beat-type (MusicXML time-separator). 'unspecified' writes no +// attribute (the spec default is "none"). +enum class TimeSeparator +{ + unspecified, + none, + horizontal, + diagonal, + vertical, + adjacent +}; + +// How a dual/interchangeable pair is visually joined (MusicXML time-relation). +enum class TimeRelation +{ + unspecified, + parentheses, + bracket, + equals, + slash, + space, + hyphen +}; + +// The alternate meter of an interchangeable dual pair, e.g. the "(6/8)" of "3/4 (6/8)". It decorates +// a primary metered signature and carries its own relation, symbol, and separator, independent of +// the primary's. +struct InterchangeableTimeSignature +{ + // The alternate meter's fractions. One = simple, more than one = composite. Nonempty when used. + std::vector fractions; + + TimeRelation relation{TimeRelation::unspecified}; + ComplexTimeSymbol symbol{ComplexTimeSymbol::unspecified}; + TimeSeparator separator{TimeSeparator::unspecified}; +}; + +MXAPI_EQUALS_BEGIN(InterchangeableTimeSignature) +MXAPI_EQUALS_MEMBER(fractions) +MXAPI_EQUALS_MEMBER(relation) +MXAPI_EQUALS_MEMBER(symbol) +MXAPI_EQUALS_MEMBER(separator) +MXAPI_EQUALS_END; +MXAPI_NOT_EQUALS_AND_VECTORS(InterchangeableTimeSignature); + +// A metered (as opposed to senza-misura) complex time signature: a primary meter of one or more +// fractions, plus its decorations. +// +// Note: A single fraction with no symbol, no separator, and no interchangeable is simple-equivalent +// and the TimeChoice constructor collapses it back to the simple case; +struct MeteredTimeSignature +{ + // The primary meter's fractions. One = a single meter (decorated); more than one = composite + // (additive), e.g. 2/4 + 3/8. Nonempty in a valid state. + std::vector fractions{TimeFraction{"4", "4"}}; + + ComplexTimeSymbol symbol{ComplexTimeSymbol::unspecified}; + TimeSeparator separator{TimeSeparator::unspecified}; + std::optional interchangeable; +}; + +MXAPI_EQUALS_BEGIN(MeteredTimeSignature) +MXAPI_EQUALS_MEMBER(fractions) +MXAPI_EQUALS_MEMBER(symbol) +MXAPI_EQUALS_MEMBER(separator) +MXAPI_EQUALS_MEMBER(interchangeable) +MXAPI_EQUALS_END; +MXAPI_NOT_EQUALS_AND_VECTORS(MeteredTimeSignature); + +// Models the full MusicXML time element, except for the most simple cases which are modeled by +// TimeSignatureData. +// +// Included: +// - composite meters +// - symbols +// - separator selection +// - interchangeable meters +// - senza-misura +// +// Include a ComplexTimeSignature in your score using TimeChoice{complex}; +class ComplexTimeSignature +{ + public: + enum class Kind + { + metered, + senzaMisura + }; + + // Defaults to a metered 4/4. + // + // Note: if supplied to a TimeChoice, this would collapse to a simple time signature. + ComplexTimeSignature(); + + // Construct a metered time signature. + ComplexTimeSignature(MeteredTimeSignature value); + + // Construct a senza-misura time signature. + // + // glyph is free text; MusicXML defines no valid/invalid values beyond xs:string (e.g. "X"). + ComplexTimeSignature(std::string glyph); + + Kind kind() const; + bool isMetered() const; + bool isSenzaMisura() const; + + // Returns a copy of the internally held MeteredTimeSignature. + // + // Check isMetered() first. If this is not a metered time signature, a default constructed + // MeteredTimeSignature is returned. + const MeteredTimeSignature metered() const; + + // Returns a copy of the senza-misura glyph (which could be an empty string). + // + // Check isSenzaMisura() first. If this is not a senza-misura time signature, a default + // constructed (empty) string is returned. + const std::string senzaMisura() const; + + bool operator==(const ComplexTimeSignature &other) const; + + private: + std::variant myValue; +}; + +MXAPI_NOT_EQUALS_AND_VECTORS(ComplexTimeSignature); + +} // namespace api +} // namespace mx diff --git a/src/include/mx/api/MeasureData.h b/src/include/mx/api/MeasureData.h index 659796157..62b09aafc 100644 --- a/src/include/mx/api/MeasureData.h +++ b/src/include/mx/api/MeasureData.h @@ -6,13 +6,10 @@ #include "mx/api/ApiCommon.h" #include "mx/api/BarlineData.h" -#include "mx/api/ClefData.h" -#include "mx/api/DirectionData.h" #include "mx/api/KeyData.h" #include "mx/api/PartSymbolData.h" #include "mx/api/StaffData.h" -#include "mx/api/TempoData.h" -#include "mx/api/TimeSignatureData.h" +#include "mx/api/TimeChoice.h" #include "mx/api/TransposeData.h" #include @@ -22,7 +19,7 @@ namespace mx { namespace api { -/// This typedef help readability in a few places such as the layout map in ScoreData. +// This typedef helps readability in a few places such as the layout map in ScoreData. using MeasureIndex = int; class MeasureData @@ -31,59 +28,46 @@ class MeasureData // this is the notes and other music data std::vector staves; - TimeSignatureData timeSignature; + // The measure's time signature (applies to all staves). + // + // Breaking change: this used to be TimeSignatureData but was changed to TimeChoice + TimeChoice timeSignature; + + // Only use this if you need different time signatures on different staves. Normally empty; + // keyed by staff index, one entry per staff-scoped