Skip to content

feat: add read/write support for the sound element#200

Merged
webern merged 1 commit into
mainfrom
issue-188
Jun 17, 2026
Merged

feat: add read/write support for the sound element#200
webern merged 1 commit into
mainfrom
issue-188

Conversation

@webern

@webern webern commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses audit section 2.3. `MeasureReader::parseSound` was a no-op; any tempo, dynamics, or
navigation encoded on a standalone `` was dropped on read.

New `api::SoundData` type (carried on `DirectionData` via `isSoundDataSpecified` +
`soundData`) round-trips the high-value scalar attributes: `tempo`, `dynamics` (doubles,
<0 = unspecified), `dacapo`/`forward-repeat`/`pizzicato` (Bool), and
`segno`/`dalsegno`/`coda`/`tocoda`/`fine` (string, empty = unspecified).

A standalone `` within a measure reads into a sound-only `DirectionData` on staff 0;
a `` child `` reads via `DirectionReader`. `DirectionWriter` emits the
right form in each case. Shared `readSoundData`/`writeSoundData` helpers in
`SoundFunctions.{h,cpp}`.

Scoped out: nested children (``, ``, ``, ``) and
less-common scalars (`pan`, `elevation`, `divisions`, pedals, `id`).

Testing

  • New `SoundApiTest.cpp` (5 cases): standalone tempo, standalone dynamics, navigation
    attributes, serialized XML spot-check (``), direction-child sound + words
  • `make test` passes (227 cases, 3944 assertions)
  • `make check` (fmt-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 65f90d0ccb07108fa4617d5dbf91f403af20ae57.

@github-actions

Copy link
Copy Markdown

Coverage report

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

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

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

Metric Coverage Covered / Total
Lines 68.9% 4808 / 6976
Functions 54.8% 1478 / 2695
Branches 40.6% 3943 / 9702

Core HTML report | API HTML report

Commit 65f90d0ccb07108fa4617d5dbf91f403af20ae57.

@webern webern changed the title Add read+write support for the &lt;sound&gt; element (#188) api: support the sound element Jun 17, 2026
Summary:
Model the commonly-used scalar attributes of the MusicXML <sound> element
in the api layer and wire them through impl so they round-trip. Previously
MeasureReader::parseSound was a no-op and DirectionData carried only a
"TODO - sound element", so any tempo/dynamics/segno/coda/etc. encoded on a
<sound> (standalone or as a direction child) was dropped on read.

- New api type api::SoundData (src/include/mx/api/SoundData.h) holding:
  tempo, dynamics (doubles, <0 = unspecified); dacapo, forward-repeat,
  pizzicato (Bool); segno, dalsegno, coda, tocoda, fine (strings, empty =
  unspecified). Carried on DirectionData via isSoundDataSpecified + soundData.
- impl read: shared impl::readSoundData helper (SoundFunctions). A standalone
  <sound> is read by MeasureReader::parseSound into a sound-only DirectionData
  on staff 0 (isStaffValueSpecified=false); a <direction> child <sound> is read
  by DirectionReader into the same DirectionData.
- impl write: impl::writeSoundData rebuilds core::Sound. DirectionWriter emits
  a standalone <sound> MusicDataChoice when the direction is sound-only, or
  attaches the <sound> as a child of the <direction> when other content exists.

Test plan:
- New src/private/mxtest/api/SoundApiTest.cpp: round-trips standalone tempo,
  standalone dynamics, the navigation booleans/strings, asserts the serialized
  XML emits a standalone <sound tempo="120"/>, and round-trips a direction-child
  <sound> alongside <words>.
- MX_RUNNING_IN_DOCKER=1 make test (3944 assertions, 227 cases all pass),
  make fmt, make check (fmt-check passed), make test-api-roundtrip (1/1 pinned).

Scope note:
Nested child elements of <sound> (<midi-instrument>, <midi-device>, <play>,
<swing>, <offset>) and the less-common scalar attributes (divisions, time-only,
pan, elevation, damper/soft/sostenuto-pedal, id) are intentionally not modeled;
the focus is the high-value playback/navigation attributes. No new corpus
roundtrip-baseline entry: discovery shows the ~141 wild <sound> files still fail
the strict DOM compare on unrelated subset gaps, so unit tests are the coverage.
@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 e60a63c3534a44ccd15291e0b661233e9a629013.

@webern webern changed the title api: support the sound element feat: add read/write support for the sound element Jun 17, 2026
@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
@github-actions

Copy link
Copy Markdown

Coverage report

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

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

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

Metric Coverage Covered / Total
Lines 70.6% 5072 / 7183
Functions 56.4% 1606 / 2846
Branches 42.1% 4222 / 10019

Core HTML report | API HTML report

Commit e60a63c3534a44ccd15291e0b661233e9a629013.

@webern webern merged commit 0c183a0 into main Jun 17, 2026
7 checks passed
@webern webern deleted the issue-188 branch June 17, 2026 07:14
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.

read and write the <sound> element

1 participant