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
11 changes: 0 additions & 11 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
{
"permissions": {
"allow": [
"Agent",
"Edit",
"WebSearch",
"WebFetch",
"Write",
"Read",
"Bash"
]
},
"hooks":
{
"SessionStart":
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
build/
build-*/
cmake-build-*
.cache/
src/private/mxtest/file/PathRoot.h
*.DS_Store
compile_commands.json
Expand Down
116 changes: 116 additions & 0 deletions data/rpatters1/timesigs_composite-ref-modified.musicxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="4.0">
<identification>
<encoding>
<software>Finale v27.4 for Mac</software>
<encoding-date>2026-06-30</encoding-date>
<supports attribute="new-system" element="print" type="yes" value="yes"/>
<supports attribute="new-page" element="print" type="yes" value="yes"/>
<supports element="accidental" type="yes"/>
<supports element="beam" type="yes"/>
<supports element="stem" type="yes"/>
</encoding>
</identification>
<defaults>
<scaling>
<millimeters>7.2319</millimeters>
<tenths>40</tenths>
</scaling>
<page-layout>
<page-height>1545</page-height>
<page-width>1194</page-width>
<page-margins type="both">
<left-margin>70</left-margin>
<right-margin>70</right-margin>
<top-margin>88</top-margin>
<bottom-margin>88</bottom-margin>
</page-margins>
</page-layout>
<system-layout>
<system-margins>
<left-margin>0</left-margin>
<right-margin>0</right-margin>
</system-margins>
<system-distance>121</system-distance>
<top-system-distance>70</top-system-distance>
</system-layout>
<appearance>
<line-width type="stem">0.7487</line-width>
<line-width type="beam">5</line-width>
<line-width type="staff">0.7487</line-width>
<line-width type="light barline">0.7487</line-width>
<line-width type="heavy barline">5</line-width>
<line-width type="leger">0.7487</line-width>
<line-width type="ending">0.7487</line-width>
<line-width type="wedge">0.7487</line-width>
<line-width type="enclosure">0.7487</line-width>
<line-width type="tuplet bracket">0.7487</line-width>
<note-size type="grace">60</note-size>
<note-size type="cue">60</note-size>
<distance type="hyphen">60</distance>
<distance type="beam">7.5</distance>
</appearance>
<music-font font-family="Maestro, engraved" font-size="20.5"/>
<word-font font-family="Times New Roman" font-size="10.25"/>
</defaults>
<part-list>
<score-part id="P1">
<part-name print-object="no">MusicXML Part</part-name>
<score-instrument id="P1-I1">
<instrument-name>SmartMusic SoftSynth 1</instrument-name>
</score-instrument>
<midi-instrument id="P1-I1">
<midi-channel>1</midi-channel>
<midi-bank>15489</midi-bank>
<midi-program>1</midi-program>
<volume>80</volume>
<pan>0</pan>
</midi-instrument>
</score-part>
</part-list>
<!--=========================================================-->
<part id="P1">
<measure number="1" width="983">
<print>
<system-layout>
<system-margins>
<left-margin>70</left-margin>
<right-margin>0</right-margin>
</system-margins>
<top-system-distance>211</top-system-distance>
</system-layout>
<measure-numbering>system</measure-numbering>
</print>
<attributes>
<divisions>2</divisions>
<key>
<fifths>0</fifths>
<mode>major</mode>
</key>
<time>
<beats>1 + 2.5</beats>
<beat-type>2 + 4</beat-type>
<beats>3 + 2</beats>
<beat-type>16</beat-type>
<beats>1.5 + 5</beats>
<beat-type>8 + 16</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<sound tempo="96"/>
<note>
<rest measure="yes"/>
<duration>33</duration>
<voice>1</voice>
</note>
<barline location="right">
<bar-style>light-heavy</bar-style>
</barline>
</measure>
</part>
<!--=========================================================-->
</score-partwise>
160 changes: 160 additions & 0 deletions src/include/mx/api/ComplexTimeSignature.h
Original file line number Diff line number Diff line change
@@ -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 <optional>
#include <string>
#include <variant>
#include <vector>

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<TimeFraction> 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<TimeFraction> fractions{TimeFraction{"4", "4"}};

ComplexTimeSymbol symbol{ComplexTimeSymbol::unspecified};
TimeSeparator separator{TimeSeparator::unspecified};
std::optional<InterchangeableTimeSignature> 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<MeteredTimeSignature, std::string> myValue;
};

MXAPI_NOT_EQUALS_AND_VECTORS(ComplexTimeSignature);

} // namespace api
} // namespace mx
Loading
Loading