Skip to content

feat: extend direction text formatting in mx::api and unify the enclosure enum - #374

Closed
rpatters1 wants to merge 13 commits into
webern:mainfrom
rpatters1:main
Closed

feat: extend direction text formatting in mx::api and unify the enclosure enum#374
rpatters1 wants to merge 13 commits into
webern:mainfrom
rpatters1:main

Conversation

@rpatters1

Copy link
Copy Markdown
Contributor

Human Summary

Extends direction text formatting with justify and systemRelation added where available in the musicxml spec. Also unified the Enclosure enum which resulted in a breaking change, but it seems like the best approach moving forward. (See below for more details.)

Summary

Four MusicXML attributes that mx::api could not express are now modeled, and the two duplicate enclosure enums are merged into one.

justify on words, symbol and rehearsal. All three elements carry both justify and halign, which MusicXML treats as different things: halign says which edge of the text the position refers to, justify says how the lines sit relative to each other when the text runs to more than one line. halign was already reaching the api through positionData.horizontalAlignment; justify was dropped. PageTextData and TempoData already had a justify field, so the new ones follow that precedent. Elements carrying justify without halign (lyric, part-name, group-name) are deliberately left alone -- the spec says justify doubles as alignment there, which is how the lyric reader already treats it.

systemRelation on DirectionData, mapping the system attribute of both direction and harmony. The api::SystemRelation enum already existed for measure numbering. The direction vocabulary is narrower (only-top, also-top, none), so the api-to-core conversion returns an optional and writes no attribute for the two measure-numbering-only values instead of coercing them into something wrong.

The enclosure vocabulary is now complete. RehearsalEnclosure was missing invertedBracket and pentagon through decagon, while PercussionEnclosure already had all fifteen. Since both described the same MusicXML enclosure-shape type with identical values, they are replaced by a single Enclosure in ApiCommon.h. That also collapses three parallel conversion paths -- two hand-written switches in DirectionReader and DirectionWriter plus the Converter table -- into the Converter table alone, which is where the drift came from in the first place.

Breaking change: RehearsalEnclosure and PercussionEnclosure are removed rather than aliased. The vocabularies are unchanged, so the downstream fix is a mechanical rename to Enclosure. Otherwise there is no structural API change; the additions are new fields on existing types with defaults that preserve current behavior.

Testing

  • 11 new tests across DirectionMarksRoundTripTest, DirectionDataTest and HarmonyExtrasApiTest, covering each attribute set and left unspecified, the unwritable system values, and every shape in the enclosure vocabulary
  • make api-test: 5367 assertions in 474 test cases
  • make api-roundtrip: 295 passed, 0 failed of 295 pinned
  • make core-roundtrip-test: 837 test cases
  • Unity build (CMAKE_UNITY_BUILD_BATCH_SIZE=0), since two file-local functions were removed

No corpus fixture was pinned to roundtrip-baseline.txt: the files exercising these attributes (synthetic/direction.4.0.xml, synthetic/words.3.x.xml) still fail the api round trip on unrelated gaps, directive on direction and dir on words.

rpatters1 added 13 commits July 29, 2026 06:22
- WordsData, SymbolData and RehearsalData gain justify, mapping the attribute of the same name on <words>, <symbol> and <rehearsal>.
- DirectionData gains systemRelation, mapping the system attribute on <direction> and <harmony>.
- Enclosure in ApiCommon.h replaces RehearsalEnclosure and PercussionEnclosure, completing the enclosure-shape vocabulary and
  collapsing three conversion paths into one Converter table. The two old enum names are removed; the vocabularies are unchanged.
* direction-attributes:
  Add direction and text formatting attributes to mx::api
ClefData::location was read from clef@after-barline but never written.
Adds Bool ClefData::additional and maps both attributes in both directions.
* clef-additional-attribute:
  Add clef additional attribute and fix dropped after-barline in mx::api
Expose the MusicXML kind stack-degrees and parentheses-degrees attributes,
which control how a chord symbol's degree alterations are laid out. Both are
api::Bool defaulting to unspecified, so the writer emits nothing unless the
field is set.

DirectionReader reads them off core::Kind; DirectionWriter writes them back.
Adds round-trip, negative, and file-based tests in HarmonyExtrasApiTest, and
registers data/synthetic/kind.3.0.xml with MxFileRepository.
* chord-display-options:
  Add stackDegrees and parenthesesDegrees to ChordData
DirectionWriter built each core::Degree with only degree-type, degree-value,
and degree-alter, so api::Extension::printObject was dropped on write. The read
side already populated it, meaning the field round-tripped to nothing.

MusicXML marks a degree print-object="no" when the kind text attribute already
spells it out; Finale exports <kind text="7sus4">suspended-fourth</kind> with a
hidden added seventh. Without this the seventh prints twice.

Covered by a round-trip test, a negative test, and a read of the three hidden
degrees in recsuite/BrookeWestSample.xml.
* chord-display-options:
  Write the degree print-object attribute
Expose the <notehead> element's filled and smufl attributes, which mx::core
already modeled but mx::api dropped in both directions. NoteWriter now emits
<notehead> when either is set, even for a normal notehead value. Also adds the
missing MXAPI_EQUALS_MEMBER(notehead), which had never been in the equality
block. Six tests in NoteDataTest; no roundtrip-baseline.txt additions.
* notehead-attributes:
  Add notehead filled and smufl to NoteData
The test bound a reference to firstNoteheadNote(mxtest::fromXml(xml)).
Lifetime extension does not apply when a reference binds to one returned
by a function, so the temporary ScoreData was destroyed at the end of the
statement and outNote dangled. Hold the score in a local first, as the
neighbouring notehead tests already do.

MSVC's debug CRT caught this by filling the freed block with 0xDD, which
read back as -572662307; libc++ happened not to reuse the block, so the
Linux and macOS jobs passed.
* notehead-attributes:
  Fix dangling reference in the notehead default-absent test

@webern webern left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is probably fine but it currently has the diffs from other PRs in it.

@rpatters1

Copy link
Copy Markdown
Contributor Author

Closed in favor of #378

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants