Tone Generation
ci / checks (push) Has been cancelled

This commit is contained in:
khodowany
2026-09-18 01:17:36 -05:00
parent 64eac2f11e
commit 80873a0896
39 changed files with 1624 additions and 52 deletions
+27 -3
View File
@@ -65,8 +65,9 @@ The pipeline is MuseScore -> uncompressed `.xml` -> musical model -> automatic a
| Harmony | Structured roots, kinds, inversions, slash basses, and degree additions/alterations/subtractions. Harmony events have their own offsets, including inside held notes. Their positions never split or shorten notes. |
| Text | Title, composer, part name, multiple lyric verses with syllabification, elisions, and extenders, rehearsal labels at measure starts, and Vamp directives. |
| Tempo | Metronome directions and positive quarter-note BPM from `<sound tempo="...">`, either within a direction or directly in a measure. Playback/navigation attributes on `sound` are rejected. |
| Repeats | Left forward and right backward bar repeats, explicit repeat counts, implicit start at the beginning, sequential/nested repeat groups, and contiguous numbered endings. |
Multiple staves/parts, repeats/navigation, slurs, dynamics, articulations, and expressive ornaments are outside the product contract and rejected. Unknown musical elements also fail. Serialized times and alterations use reduced signed-64-bit numerators and positive denominators; pitch octaves are -4 through 16, clef/octave shifts span at most three octaves, and meter counts are 1-1024 with denominator units 1-4096. These resource bounds produce diagnostics, not clamping or truncation.
Multiple staves/parts, D.C./D.S./coda/fine navigation, slurs, dynamics, articulations, and expressive ornaments are outside the product contract and rejected. Unknown musical elements also fail. Serialized times and alterations use reduced signed-64-bit numerators and positive denominators; pitch octaves are -4 through 16, clef/octave shifts span at most three octaves, and meter counts are 1-1024 with denominator units 1-4096. These resource bounds produce diagnostics, not clamping or truncation.
Layout/exporter information such as page defaults, credits, print layout, font/color/position attributes, and source pagination does not determine the small-screen layout. Engraving is regenerated. Beam groups and written note values are retained; unrelated engraving metadata does not authorize ignoring musical descendants. `defusedxml==0.7.1` forbids XML entities and external access. Ordinary MusicXML DOCTYPE declarations are allowed without fetching their DTDs.
@@ -84,13 +85,22 @@ The default is Medium Swing; `Med.`, `Med. Swing`, and `Medium` remain aliases.
Place staff text `vamp:optional-chord` at the same onset as exactly one harmony to mark a turnaround omitted from the final arrangement section. The text may be hidden. Parenthesizing an ordinary chord does not make it optional. Orphaned/duplicate markers and unknown `vamp:` properties are errors; consumed directives never enter runtime JSON.
### Repeats
`generator/repeats.py` is the shared repeat analyzer and expander. Practice keeps compact authored bars and ending brackets; the engraver draws repeat dots, combined boundaries, repeat-count labels, and brackets continued across systems. Performance sources unfold before the existing seven-section arranger. Playback and guitar-feasibility checks use that same performed order, so skipped endings do not become extra notes.
Ending numbers are comma-separated positive passes, such as `1,2` followed by `3`. The first ending terminates at the backward repeat; following endings are contiguous, each pass belongs to exactly one ending, and the final ending contains the last pass. `stop` closes the bracket; `discontinue` leaves it open. Hidden brackets still affect performance. Explicit repeat counts must agree with ending passes. Crossing or unterminated groups, overlapping endings, middle-measure repeat marks, winged repeats, and navigation attributes are rejected. Nesting is bounded to 16 levels and unfolding to 10,000 measures.
Repeated notation retains separate sounding `<tie>` and printed `<tied>` markers. `time-only` selects passes in the innermost repeat, and each active sounding endpoint must match on the performed path. Use pass-specific ties when tying across a backward jump; dangling or unreachable endpoints fail rather than sustaining the wrong pitch. Expansion assigns fresh IDs and remaps ties, tuplets, and paired tremolos. A jump that splits a paired tremolo is rejected. Backward jumps restore staff and harmony state; skipped endings do not leak key changes into later passes. See the MusicXML references for [repeats](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/repeat/) and [sounding ties](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/tie/).
### Runtime Schema
A bundle has three top-level fields:
A newly generated bundle has four top-level fields:
- `schema_version: 3`.
- `music`: metadata and a single staff containing measures, independent voice lanes, timed harmonies/attributes/tempos, instruments, tuplets, and octave spans.
- `presentation`: engraver/version, style checksum, fixed width/height, and ordered relative PNG paths with SHA-256 checksums.
- `playback`: versioned availability, exact performed beat duration/events, shared PCM sample descriptors, and a generated release envelope. Unavailable interpretations carry a reason instead of an approximate timeline.
Practice metadata includes optional `authored_key_signature`, captured before transposition and shared by all twelve variants. The menu's `SOURCE` label uses this value, not the first variant. Existing bundles without it remain supported and omit the source label. Explicit key-cancellation signatures transpose with the same interval as the music; the engraver cancels only altered pitches that actually change.
@@ -104,6 +114,20 @@ For the fretboard check, ordinary unmarked treble notation uses the project's wr
Automatic performance arrangements require a pitched, non-TAB lead. Percussion and tablature remain available in the model and melody-practice pipeline, but conversion into guitar/vocal parts fails explicitly rather than reinterpreting percussion positions or fret numbers as ordinary treble-staff notation. Chord practice requires pitched guitar notation. Practice transposition retains authored TAB strings and never invents a new fingering; chord practice additionally checks that the resulting exact integer frets lie within 0-15. Melody practice retains its existing nonnegative-fret behavior.
### Playback Boundary
All musical compilation and tone synthesis run in Python (`generator/playback.py`). Each transformed score receives a sorted timeline of independent tone events with rational quarter-note `onset` and `end` positions. Ties become one continuous attack. Rests and spacers remain silence, including trailing silence. Harmony labels are not auto-accompaniment: only pitches actually present in the selected arrangement or exercise sound. Cue notes are silent, hidden pitched notes sound, and pitchless rhythm slashes are silent.
The `soft-struck-1` profile generates shared mono 48 kHz PCM16 WAVs with a short attack/decay and a seamless sustain loop, plus a shared 50 ms release envelope. Exact sounding pitches, including supported microtones, determine samples. Explicit instrument transposition is applied once; chord practice and generated guitar parts use the existing written-guitar convention. Samples are shared across all scores and voices by content identity, not duplicated per complete performance. There are no SoundFonts, instrument sample bundles, oscillators, or MusicXML parsing at runtime.
Grace groups honor explicit steal-time-previous/following or make-time; unspecified groups share 25 percent of the following event. Conflicting grace modes, trailing grace groups, unsupported unpitched sound, tied measured tremolo, and incompatible paired tremolo make playback explicitly unavailable while retaining the notation. Measured tremolo uses written subdivision and tuplet ratios. The tone range is 20-18,000 Hz. Asset validation checks containment, SHA-256, PCM format/length, loop endpoints, envelope shape, event ordering, references, and rational timing. Generation remains transactional if validation fails.
At runtime the shared on-demand score loader serves both display and audio. A background worker validates and loads only the selected score's samples, converts absolute beat positions to rounded sample frames at the menu BPM, and prepares a Sonus sequence. Pitch does not change with BPM, and rounding absolute positions avoids cumulative timing drift. The I/O loop publishes the prepared sequence and polls completion without doing disk I/O or musical interpretation. Sonus mixes preallocated voices from immutable PCM and retires them on control threads; the callback never synthesizes tones or loads files. Limits are one million events, 512 distinct samples, 64 MiB decoded samples per selection, 64 simultaneous voices including releases, and 24 hours at the selected BPM. Exceeding them reports an error rather than dropping notes.
`audio.playback_enabled` defaults to false and replaces the old inert `track_enabled` setting. The old config name is accepted for migration, but conflicting values fail. Playback suppresses the metronome without changing its stored toggle. Authored tempo marks remain notation; the menu BPM is constant throughout a session. Selecting a practice variant or performance part starts a new session. Manual page changes never restart or seek. Natural completion, including trailing silence/release, returns to the corresponding key/part menu with its selection preserved. Leaving the score cancels playback; stale load/completion results cannot close a newer score. Errors stay in the score view and `/api/status` rather than being mistaken for completion. Existing bundles without `playback` still display but require regeneration to play.
`--once` additionally validates referenced audio without opening display/audio hardware. Runtime packages contain WAVs alongside pages/JSON; the source XML and Python dependencies remain build-only.
### Engraving Boundary
`generator/engraving/` is a custom Python engraver, not a wrapper around MuseScore or another notation engine. `layout_score(document, style, fonts)` produces immutable layout pages with exact source intervals, notation anchors and drawing primitives. `rasterize_page(page, fonts)` draws one page at four-times resolution and downsamples it. `engrave_tree(root, fonts)` retains the asset-pipeline entry point. `generator/musicxml_writer.py` remains a round-trip/export utility and is not involved in engraving.
@@ -120,7 +144,7 @@ Pages are 976x484 pixels with 12-pixel staff spacing, 28-pixel horizontal margin
### Validation And Migration
Generation validates sources and complete staged output before replacing the previous asset directory. Replacement is always complete, including when the compatibility `--clean` flag is omitted; deleted sources cannot leave stale JSON or pages. CMake tracks the input list as well as file contents, so additions and removals regenerate the catalogue. Runtime packages contain semantic JSON, PNG pages, and the OTF UI text font, not MusicXML, MuseScore, or Python dependencies. Python test discovery runs model/import/export tests; the normal asset build additionally qualifies and audits every catalogue score.
Generation validates sources and complete staged output before replacing the previous asset directory. Replacement is always complete, including when the compatibility `--clean` flag is omitted; deleted sources cannot leave stale JSON, pages, or audio. CMake tracks the input list as well as file contents, so additions and removals regenerate the catalogue. Runtime packages contain semantic JSON, PNG pages, generated PCM WAVs, and the OTF UI text font, not MusicXML, MuseScore, or Python dependencies. Python test discovery runs model/import/export tests; the normal asset build additionally qualifies and audits every catalogue score.
Diagnostics identify the file, part, measure, and unsupported element or invalid relationship. Examples include `part P1, measure 4: unsupported musical element <slur>`, `tie stop has no contiguous matching start`, and `optional-chord marker must identify exactly one harmony`.