Skip to content

feat: round-trip metronome attributes and non-numeric per-minute - #362

Merged
webern merged 1 commit into
mainfrom
claude/direction-types-ordering-9l60vv-e
Jul 19, 2026
Merged

feat: round-trip metronome attributes and non-numeric per-minute#362
webern merged 1 commit into
mainfrom
claude/direction-types-ordering-9l60vv-e

Conversation

@webern

@webern webern commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Human Summary

I am reluctantly making the interface more and more complicated as we hill climb against the test corpus. This one makes metronome beats per minute a string (it was an int) and adds support for its attributes.

Summary

The <metronome> element only round-tripped its parentheses attribute and a numeric per-minute; every other attribute was silently dropped, and a non-numeric per-minute discarded the beat-unit and was never written back.

This models the metronome's full attribute surface on TempoData — the print-style-align group (position, font, color, halign/valign) plus justify, print-object, and id — populated by MetronomeReader and emitted by DirectionWriter.

per-minute is xs:string in MusicXML ("120", "ca. 76", a range). BeatsPerMinute::beatsPerMinute changes from int to std::string and is kept verbatim, so "quarter = ca. 76" now round-trips. The unreachable TempoType::tempoText enumerator and the TempoText struct are removed. A numeric playback tempo is available separately on SoundData::tempo (quarter notes per minute).

Breaking: BeatsPerMinute::beatsPerMinute is now a string, and TempoType::tempoText / TempoText are gone. The metronome-note form and per-minute's own font are still not modeled (the note form is the next slice; per-minute's font is a documented drop).

Testing

  • New roundTripMetronomeAttributes and nonNumericPerMinuteRoundTrips unit tests
  • Full unit suite passes (5104 assertions in 448 test cases)
  • Round-trip regression 275/275 pinned, no regressions
  • Round-trip baseline 272 -> 275 (synthetic metronome kitchen-sink fixtures); real-world files that reported an attribute mismatch at the metronome now progress past it

References

The <metronome> element carried only its parentheses attribute and a
numeric per-minute; every other attribute was silently dropped. Model
the full print-style-align set (position, font, color, halign/valign)
plus justify, print-object, and id on TempoData, populated by the reader
and emitted by the writer.

per-minute is xs:string in MusicXML ("120", "ca. 76", a range), but the
api held it as an int with a lossy tempoText fallback that discarded the
beat-unit and was never written back. BeatsPerMinute::beatsPerMinute is
now a std::string kept verbatim, so a mark like "quarter = ca. 76"
round-trips. The now-unreachable TempoType::tempoText enumerator and the
TempoText struct are removed. A player needing a numeric playback tempo
should read SoundData::tempo, which is numeric and expressed in quarter
notes per minute.

Round-trip baseline 272 -> 275 (the synthetic metronome kitchen-sink
fixtures). Real-world files that reported an attribute mismatch at the
metronome now progress past it to their next divergence.

First metronome slice of #324.
@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 19, 2026 — with Claude
@webern
webern merged commit 85484da into main Jul 19, 2026
7 checks passed
@webern
webern deleted the claude/direction-types-ordering-9l60vv-e branch July 19, 2026 13:42
webern added a commit that referenced this pull request Jul 19, 2026
## Human Summary

Unfortunately this is quite complicated, but it appears to correctly
model the various forms of metronome markings that MusicXML supports.

## Summary

Second and final metronome slice of #324. `TempoData`'s
enum-plus-parallel-payload shape is replaced by a `TempoChoice` choice
class (the `TimeChoice` / `PercussionDataChoice` pattern) over the three
`<metronome>` bodies: beats-per-minute, metric modulation, and the
note-relation form. The unreachable `unspecified` tempo sentinel is gone
— a default/empty tempo is skipped by the writer on a content check, so
nothing throws.

The note-relation form — metric relationships drawn with note figures
(`metronome-note` and `metronome-relation`, plus per-figure
`metronome-beam`, `metronome-dot`, `metronome-tied`, and
`metronome-tuplet`, and the `metronome-arrows` flag) — is modeled in a
new `NoteRelationData.h` with dedicated mini-structs. They reuse only
the leaf enums (`DurationName`, `Beam`); a metronome note figure has
none of the pitch/stem/staff/voice machinery of a real note, so
`NoteData` is not reused. `beat-unit-tied` (a beat-unit tied to the
preceding one, e.g. "quarter + eighth = 120") is modeled on both the
beats-per-minute and metric-modulation forms.

Every part of `<metronome>` now round-trips except per-minute's own font
override, which is a documented drop — the font belongs on the metronome
element, and no real-world file uses the per-minute font.

Breaking: `TempoData::tempoType` and the parallel `beatsPerMinute` /
`metricModulation` fields are replaced by `TempoData::choice`.

## Testing

- [x] New `roundTripNoteRelation` (strong whole-body equality check),
`roundTripBeatUnitTied`, plus
`roundTripMetricModulation`/`roundTripBpm`/attributes migrated to the
choice API
- [x] Full unit suite passes (5105 assertions in 449 test cases)
- [x] Round-trip regression 284/284 pinned, no regressions
- [x] Round-trip baseline 275 -> 284 (all eight metronome-note fixtures
plus `beat-unit-tied`)

## References

- Progresses #324
- Stacked on #362
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.

1 participant