Skip to content

feat: round-trip harmony extras and figured-bass#201

Open
webern wants to merge 3 commits into
mainfrom
issue-2-harmony
Open

feat: round-trip harmony extras and figured-bass#201
webern wants to merge 3 commits into
mainfrom
issue-2-harmony

Conversation

@webern

@webern webern commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

Two independent commits addressing audit sections 2.6 and 2.5.

Harmony extras (#191): `ChordData` previously only modeled ``; ``,
``, and the MusicXML 4.0 `` were dropped on read and could not be written.
`ChordData` gains a `HarmonyChordSource` discriminator (root/numeral/function),
`functionText`, numeral fields (`numeralRoot`, `numeralRootText`, `numeralAlter`,
`numeralKeyFifths`, `NumeralMode`), and `inversion`/`hasInversion`.
`DirectionReader::parseHarmony` now branches on `HarmonyChordGroupChoice::Kind` instead of
calling `asRoot()` unconditionally, and reads ``. Also fixes a latent issue: the
harmony-chord dispatch loop filtered to `isRoot()`, silently skipping numeral/function chords.
`DirectionWriter` rebuilds all three variants symmetrically.

Figured-bass (#190): `MeasureReader::parseFiguredBass` emitted a lossy plain-text
`WordsData` stub; the structured `` XML was never written back. New
`FiguredBassData` (ordered `FigureData` list with prefix/figure-number/suffix, parentheses,
`durationTimeTicks`) replaces the stub. `DirectionWriter` emits `` via
`createFiguredBassElements`. Scoped out: `` and print/position formatting attributes.

Testing

  • New `HarmonyExtrasApiTest.cpp` (inversion, function, numeral with alter and numeral-key)
  • New `FiguredBassApiTest.cpp` (multi-figure with prefix/number/suffix; parentheses + duration)
  • `make test` passes (227 cases), `make check` passes, `make test-api-roundtrip` baseline unchanged

References

@github-actions

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 3ca01633b076d3e3bbcea4103681cc93ef356f41.

@github-actions

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.9% 28539 / 36623
Functions 74.4% 6360 / 8549
Branches 50.7% 22672 / 44725

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

Metric Coverage Covered / Total
Lines 69.5% 4880 / 7020
Functions 54.9% 1479 / 2692
Branches 40.9% 3973 / 9720

Core HTML report | API HTML report

Commit 3ca01633b076d3e3bbcea4103681cc93ef356f41.

webern added 2 commits June 17, 2026 08:44
Summary:
The harmony-chord choice in the api ChordData only modeled the <root>
alternative; <inversion>, the deprecated <function>, and the MusicXML 4.0
<numeral> were dropped on read and could not be written. This adds those
fields to ChordData and wires read + write through DirectionReader and
DirectionWriter.

- ChordData gains: a HarmonyChordSource discriminator (root | numeral |
  function), functionText, the numeral fields (numeralRoot, numeralRootText,
  numeralAlter/hasNumeralAlter, numeralKey fifths + NumeralMode), and
  inversion/hasInversion.
- DirectionReader::parseHarmony now branches on the HarmonyChordGroupChoice
  kind (it previously called asRoot() unconditionally) and reads inversion.
  The harmony-chord dispatch loop no longer filters to isRoot(), so numeral
  and function chords are no longer silently skipped.
- DirectionWriter rebuilds root / numeral / function and inversion symmetrically.

Test plan:
- New src/private/mxtest/api/HarmonyExtrasApiTest.cpp: round-trip tests for
  inversion, function, and numeral (with numeral-alter and numeral-key).
- make test (225 cases), make check, make test-api-roundtrip all pass.
Summary:
<figured-bass> was read as flattened plain text into a WordsData direction
(MeasureReader::parseFiguredBass) and never written back as <figured-bass>, so
it was lost on round-trip. This adds an api type that models the element
structurally and wires symmetric read + write.

- New mx::api FiguredBassData (an ordered list of FigureData with optional
  prefix / figure-number / suffix, plus parentheses and an optional duration in
  ticks) and FigureData. Attached to DirectionData via a figuredBasses vector,
  so it is positioned and flushed at the right tick like harmony.
- MeasureReader::parseFiguredBass now builds a FiguredBassData (figures,
  parentheses, duration) instead of the WordsData text stub.
- DirectionWriter::createFiguredBassElements rebuilds <figured-bass> as a
  core::MusicDataChoice, emitted alongside harmony in getDirectionLikeThings.

Scope: <figure> child <extend> and the figured-bass print/position formatting
attributes are intentionally not modeled (kept lean); the round-tripped data is
the figures' prefix/number/suffix text plus parentheses and duration.

Test plan:
- New src/private/mxtest/api/FiguredBassApiTest.cpp: round-trips multiple
  figures with prefix/number/suffix, and parentheses + duration.
- make test (227 cases), make check, make test-api-roundtrip all pass.
@webern webern force-pushed the issue-2-harmony branch from ac8eb9e to 91b75d2 Compare June 17, 2026 06:44
@github-actions

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 2b2518f69de1e94ab22e4f5ce959bdb0daacdff5.

@webern webern added feature new feature request non-breaking fixes or implementation that do not require breaking changes area/mx::api area/mx::impl ai Issues opened by, or through, a coding agent. labels Jun 17, 2026
@webern webern changed the title Harmony inversion/function/numeral + figured-bass round-trip (#191, #190) feat: round-trip harmony extras and figured-bass Jun 17, 2026
@github-actions

Copy link
Copy Markdown

Coverage report

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

Metric Coverage Covered / Total
Lines 77.9% 28539 / 36624
Functions 74.4% 6360 / 8550
Branches 50.7% 22672 / 44725

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

Metric Coverage Covered / Total
Lines 71.2% 5144 / 7227
Functions 56.5% 1607 / 2843
Branches 42.4% 4252 / 10037

Core HTML report | API HTML report

Commit 2b2518f69de1e94ab22e4f5ce959bdb0daacdff5.

@webern

webern commented Jun 17, 2026

Copy link
Copy Markdown
Owner Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown

@copilot resolve the merge conflicts in this pull request

Resolved in a49fb77.

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. area/mx::api area/mx::impl feature new feature request non-breaking fixes or implementation that do not require breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add harmony inversion, function, and numeral to ChordData round-trip <figured-bass>

2 participants