Skip to content

CLOSE-me: add per-staff and composite time signatures to mx::api#303

Open
webern wants to merge 1 commit into
mainfrom
claude/untitled-session-q9fg7j
Open

CLOSE-me: add per-staff and composite time signatures to mx::api#303
webern wants to merge 1 commit into
mainfrom
claude/untitled-session-q9fg7j

Conversation

@webern

@webern webern commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Human Summary

Ha, well I asked the machine to pick low hanging fruit from @rpatters1's issues that it could "just do" without my supervisions since I am working a lot for my employer right now, and for some reason it thought these two were easy and small.

I don't love the time signature change, to be honest, I might not want to merge this with that time-signature solution as is. I'd like for there to be a normal path for the 99% of time signatures that are just a X over Y, and you don't have this ugly weird vector in the 99% case. So I think this might need to be reworked.

Summary

mx::core already fully modeled the composite/staff-scoped time-signature shape (Time::number(), TimeChoiceGroup::timeSignature() as OneOrMore); the gap was entirely in mx::api/mx::impl.

  • TimeSignatureData replaces the single beats/beatType strings with std::vector<TimeSignatureComponent> (components), supporting composite/interchangeable meters (multiple beats/beat-type pairs in one <time>). A setSimple(beats, beatType) helper covers the common single-pair case.
  • TimeSignatureData gains staffIndex (same INDEX_UNSPECIFIED convention as KeyData::staffIndex) for staff-scoped <time number="...">.
  • MeasureData::timeSignature (singular) becomes timeSignatures (a vector), carried forward per staff across measures the same way the old singular field carried forward via isImplicit. An unscoped <time> declaration replaces the whole effective list; staff-scoped declarations merge into the carried-forward list by staffIndex.
  • TimeReader now parses every <time> element in a measure's <attributes> (previously only the first) and every beats/beat-type pair within each (previously only the first).

Reference corpus file data/rpatters1/timesigs_composite-ref.musicxml (added by #287) exercises the composite-meter case end to end.

Testing

  • New tests: staff-scoped and composite-meter time signature round-trips, setSimple(), equality coverage for the new fields (TimeSignatureApiTest.cpp)
  • make test: all pass (4587 assertions in 362 test cases, plus the three examples)
  • make test-api-roundtrip: 124 passed, 0 failed (of 124 pinned; no regressions)
  • make fmt / make check clean

References

@webern webern added feature new feature request breaking fixes or implementation that require breaking changes api Affects the mx::api layer impl Affects the mx::impl layer ai Issues opened by, or through, a coding agent. labels Jul 5, 2026 — with Claude
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28513 / 36640
Functions 74.3% 6352 / 8552
Branches 50.7% 22678 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 79.0% 6198 / 7846
Functions 64.3% 2093 / 3254
Branches 48.2% 5297 / 10981

Core HTML report | API HTML report

Commit 79cf5819b73214cc03d4516ebae22acbaa972e3b.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit 79cf5819b73214cc03d4516ebae22acbaa972e3b.

@rpatters1

rpatters1 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Maybe in addition to an easy setter there could be an easy getter routine that returns std::optional<std::pair<...>>. That way there would be no ugly math: it would get the simple time if it is simple and return std::nullopt if it isn't.

edit: or it could simply be

bool getSimple(std::string& beats, std::string& beatType);

Where a return value of false means it isn't simple, but either way it returns the first entry.

@rpatters1

Copy link
Copy Markdown
Contributor

Or (and this is more what I think you want): keep beats and beatType as they are and emit them first, followed by the others in the array, if any. I'm not sure what to call the array. Maybe secondaryTimeSignatures. From a data modeling perspective it is wrong, but from an api usability perspective it might be a reasonable compromise.

@webern webern force-pushed the claude/untitled-session-q9fg7j branch from b8d278f to 2b00730 Compare July 6, 2026 06:05
@webern webern changed the title feat: time signature and measure-level transposition changes feat: time signature staff-scoping and composite meter support Jul 6, 2026
@webern webern changed the title feat: time signature staff-scoping and composite meter support feat: add per-staff and composite time signatures to mx::api Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28513 / 36640
Functions 74.3% 6352 / 8552
Branches 50.7% 22678 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 79.0% 6180 / 7823
Functions 64.3% 2093 / 3254
Branches 48.2% 5283 / 10961

Core HTML report | API HTML report

Commit 582d556160e0f6050fef98091d07494565ab0775.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit 582d556160e0f6050fef98091d07494565ab0775.

Splits the time-signature half out of the original combined time-signature
and transposition PR so each can be reviewed and merged independently.

Closes #268
@webern webern force-pushed the claude/untitled-session-q9fg7j branch from 2b00730 to f1a3975 Compare July 7, 2026 09:45
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28513 / 36640
Functions 74.3% 6352 / 8552
Branches 50.7% 22678 / 44751

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 80.9% 6470 / 7994
Functions 67.8% 2221 / 3276
Branches 49.7% 5554 / 11174

Core HTML report | API HTML report

Commit c9373ffa23d78e1a7e843787d90973af06a7c62b.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

gen-quality gen/

gen-quality: 84.5 / 100   (floor 84.5, +0.0)

  structure     86.5  x0.50   [fn 90.5 / file 82.6]
  cyclomatic    88.4  x0.25
  cognitive     76.6  x0.25

  409 functions across 31 files, 7702 lines (largest file 1044)
  max cc 56  max cognitive 44  max fn loc 152

Worst offenders (top 5 per axis; full lists in score.json):
  cyclomatic gen/xsd/analyze.py:311     report                             56
  cyclomatic gen/plates/build.py:956    _validate_config_against_ir        35
  cyclomatic gen/press/context.py:145   plate_context                      34
  cyclomatic gen/__main__.py:46         _ir                                23
  cyclomatic gen/tests/test_ir.py:102   _check_references                  20
  cognitive  gen/xsd/analyze.py:311     report                             44
  cognitive  gen/ir/resolve.py:119      flat_elements                      40
  cognitive  gen/tests/test_ir.py:102   _check_references                  38
  cognitive  gen/press/context.py:145   plate_context                      37
  cognitive  gen/xsd/analyze.py:207     _sccs                              37
  size       gen/xsd/analyze.py:311     report                             152
  size       gen/press/context.py:145   plate_context                      96
  size       gen/plates/build.py:533    _value_plate                       89
  size       gen/plates/build.py:956    _validate_config_against_ir        89
  size       gen/ir/resolve.py:119      flat_elements                      78

Commit c9373ffa23d78e1a7e843787d90973af06a7c62b.

@webern webern changed the title feat: add per-staff and composite time signatures to mx::api CLOSE-me: add per-staff and composite time signatures to mx::api Jul 9, 2026
Repository owner locked and limited conversation to collaborators Jul 9, 2026
Repository owner unlocked this conversation Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai Issues opened by, or through, a coding agent. api Affects the mx::api layer breaking fixes or implementation that require breaking changes feature new feature request impl Affects the mx::impl layer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api: time signature improvements

2 participants