Fix Playability
ci / checks (push) Failing after 23m26s

This commit is contained in:
khodowany
2026-10-08 19:04:06 -05:00
parent bc7baea7c4
commit 3f63b255ac
23 changed files with 633 additions and 374 deletions
+29 -4
View File
@@ -133,11 +133,11 @@ Measure durations, event onsets/durations, harmony offsets, span times, and pitc
`parts.py` retains exact source notes, rests, lyrics and ties. `melody.py` harmonizes eligible sustained electric source-melody notes using their actual chord, preserving source pitches while splitting and reattacking held notes at harmony changes. Guitar notation applies its octave transposition once. `comping.Canonical` independently chooses compact chord voicings and rhythm from the source form and shared policy, then reuses that part at the same source-form position throughout the arrangement. There is no generated-solo response or reuse of a generated melody's grips. Sparse bars and deliberate releases are legal.
Electric accompaniment shares the electric melody's `guitar.chord_positions(..., profile='electric')` families: strings 1–3 or 1–4, with a preference for four-note grips. `chord_families.py` classifies actual shapes as drop-two, close-position, compact-open, compact-doubled or three-note shells, ranked by versioned policy. Melody adaptation also permits strings 2–4 when the exact source pitch cannot sit above the chord on string 1. Within the selected span tier, its lowest feasible string number and foreground position precede chord size. A primary grip with the melody on string 2 beats a four-fret grip with it on string 1. Short monophonic melody notes retain their movement-based placement; Electric Solo is unchanged. These electric roles use the same written chord requirements without taking responsibility for the slash bass. Acoustic accompaniment uses `acoustic_voicing.candidates`: exactly one bass on string 5/6 and one upper note each on strings 4/3/2. Without an explicit slash bass or inversion, its bass is a root or written fifth (including an altered fifth); plain major/minor triads also permit the third. Plain triads retain all three chord members. Explicit bass/inversion requirements override this choice.
Electric accompaniment shares the electric melody's `guitar.chord_positions(..., profile='electric')` families: strings 1–3 or 1–4, with a preference for four-note grips. `chord_families.py` classifies actual shapes as drop-two, close-position, compact-open, compact-doubled or three-note shells, ranked by versioned policy. Electric melody chords must also use strings 1–3 or 1–4, always including string 1. The exact source pitch is retained even when a legal supporting chord tone must sound above it. Within the selected span tier, the melody's lowest feasible string number and foreground position precede chord size. A primary grip with the melody on string 2 beats a four-fret grip with it on string 1. Short monophonic melody notes and solos use the shared hand-window placement objective. A harmonization with no legal chord retains the existing reported single-note fallback. These electric roles use the same written chord requirements without taking responsibility for the slash bass. Acoustic accompaniment uses `acoustic_voicing.candidates`: exactly one bass on string 5/6 and one upper note each on strings 4/3/2. Without an explicit slash bass or inversion, its bass is a root or written fifth (including an altered fifth); plain major/minor triads also permit the third. Plain triads retain all three chord members. Explicit bass/inversion requirements override this choice.
Acoustic families are transposable degree orders for triads, tetrads and extended chords, with root/fifth alternatives on both bass strings. Slots resolve to the written third/suspension, sixth/seventh, fifth and extension, including alterations and omissions. They are not per-song grip tables. Only harmonically and physically valid realizations enter selection; unavailable bass/inversion variants are discarded. Coverage checks exercise 35 common qualities in all 12 keys. Four-fret fallbacks support previously unavailable acoustic Bb9, BbM9, Bb7#9 and Bb6add9; for example, Bb9 uses fifth-bass frets 8/10/7/9 on strings 5/4/3/2. Acoustic Bbm9 and Bm9 still have no grip meeting all constraints. Unsupported chords fail with source measure, beat and chord rather than silently omitting defining tones.
The active voicing selector compares complete paths with dynamic programming over concrete grips. The leading cost minimizes four-fret grip selections: every span of one, two or three positions has zero cost in this tier. Existing criteria then apply within the tier. Electric accompaniment prioritizes chord size and shared family rank, then combined grip effort, hand-center travel, register and position costs, followed by missing optional tones and register tie-breaking. Acoustic prefers declared degree-order families on either bass string, then a combined travel/register/position cost, optional-tone coverage and register tie-breaking. Stable role-specific keys resolve equal costs. Since accompaniment transitions have travel costs but no hard travel cutoff, four-fret candidates are safely removed from a span when any primary grip exists; this exact dominance reduction keeps the repeat search small. Electric placement retains distinct alternatives where future held/tied notes can constrain feasibility, and does not charge tied continuations as fresh grip choices. The remaining acoustic placement helper uses the same leading fallback tier.
The active voicing selector compares complete paths with dynamic programming over concrete grips. The leading cost minimizes four-fret grip selections: every span of one, two or three positions has zero cost in this tier. Existing criteria then apply within the tier. Electric accompaniment prioritizes chord size and shared family rank; acoustic prefers declared degree-order families on either bass string. Both then minimize hand-window travel, the passage maximum hand position, secondary finger/grip effort, missing optional tones and sounding-register distance, in that order. Stable role-specific keys resolve equal costs. Since accompaniment transitions have travel costs but no hard travel cutoff, four-fret candidates are safely removed from a span when any primary grip exists; this exact dominance reduction keeps the repeat search small. Electric placement retains distinct alternatives where future held/tied notes can constrain feasibility, and does not charge tied continuations as fresh grip choices. The remaining acoustic placement helper uses the same leading fallback tier.
Silent spans retain the preceding hand position. A continuous accompaniment repeat includes the last-to-first transition. Source-harmony variants, such as relocated pickup harmony, are solved between fixed canonical neighbors without mutating a shared previous-grip state; request order cannot change them. Per-span composition reports include candidate counts, selected family, bass/string family availability, selected positions, bass degree and movement, plus `fret_span`, `span_group` and `span_candidates` (primary/fallback counts before dominance reduction). These are diagnostic sidecar fields; the score/runtime format is unchanged.
@@ -169,9 +169,34 @@ Acoustic sequential-release verification on October 7, 2026: `python3 modulus.py
Monophonic solos use strings 1–6 and frets 1–12, with no open strings or acoustic/electric string restrictions. Placement compares complete paths through legal positions, accounting for hand-region changes, travel time and string crossings. Ties retain their position. Chord placement retains its separate policy: frets 1–20, a sounding anchor on frets 1–12, primary spans of up to three consecutive positions and fallback spans of exactly four. The span is inclusive: 7–9 is three positions, 7–10 is four. Five positions are always invalid. The anchor must remain sounding, including after shorter notes release. Acoustic accompaniment additionally keeps its bass within frets 1–12 and requires an independent anchor in its upper three-note cluster, preserving the option of separate bass/upper attacks. These costs approximate physical feasibility; they do not assign fingers or guarantee comfortable playing.
Both accompaniment roles add `register_weight * abs(mean sounding MIDI - center)` and `high_position_weight * max(0, mean fret - comfortable_max_fret)` to their existing grip effort and movement tier, once per active harmonic span. The three role policies (`acoustic_accompaniment`, `electric_accompaniment`, `electric_melody`) use weights 1 and 1 and threshold 9; their register centers are 53, 60 and 60 respectively. All arithmetic is rational. Electric melody adds the same costs only to harmonized attacks, below compact-span, melody-string, foreground, chord-size and family priorities. Exact melody pitches are never transposed to meet the register center; monophonic placement is unchanged. Scoring and scored-pruning caches explicitly include the normalized preference tuple. Tied continuations incur no second chord cost. Existing travel costs and source-form joins remain in place; high positions can still win when avoiding a costly shift. The ending uses its own accompaniment role's local cost and incoming grip.
Guitar placement shares the `hand-window-2` model in `generator/hand_position.py`. **Both chords and single notes** may occupy any legal four-consecutive-fret window. Position 7 covers frets 7–10; a chord occupying only frets 7–8 can use positions 5, 6 or 7, according to its neighbors. Position is the bottom of the selected window, not necessarily an occupied fret. This is an approximate positioning model, not an anatomical finger assignment. Sounded-note limits remain binding: a solo note cannot exceed fret 12 merely because its hand window extends farther.
Each selected accompaniment span reports `voicing_cost` contributions for movement, base grip effort, weighted register, weighted position and their combined effort. These describe the balanced tier, not the higher span/family priorities; repeat-join travel is assigned to the first canonical span. Both accompaniment roles keep `voicing` settings in concrete cache dependencies but exclude them from rhythmic choice seeds, including settings overrides. Acoustic rhythm selection can change under its new bass-first policy, but changing voicing weights alone never reshuffles rhythm. Each role still composes independently.
After the existing compact-span, chord-family and electric-melody foreground priorities, selection minimizes **total absolute hand-position travel**, then the **maximum hand position across the complete path**. Finger changes and string crossings within the same window contribute zero travel. Only after those two criteria tie do relative finger adjustment, string crossings, grip complexity and time pressure decide between paths. Acoustic bass is one voice even across strings 5 and 6; upper voices follow their strings. Added/removed chord voices contribute one secondary adjustment each. Solo secondary effort uses elapsed time from the preceding attack, not a tied notation fragment. A genuine continuation retains pitch, TAB and hand position without another movement charge.
A low fret is not rewarded independently at every event. All three voicing policies retain only `register_weight` (default 1), weighting distance from their sounding-register center after travel, peak position, secondary effort and optional-tone coverage. Centers remain 53 for acoustic accompaniment and 60 for electric accompaniment/melody. Exact melody and solo pitches are never transposed to meet these preferences. Canonical accompaniment selection includes source-form repeat joins and solves source-derived variations between actual selected neighboring grips **and hand positions**. Unchanged intervals retain both. The instrumental ending receives the last accompaniment grip and selected window; it does not infer the hand from the lowest occupied fret.
The dynamic programs retain concrete grips/held strings, selected hand position and distinct earlier peaks. A later high position can equalize two earlier peaks and expose a subordinate tie-break, so retaining only one lowest-peak history is unsafe. Strictly worse primary travel at an identical endpoint/hand can still be discarded. Equal fret averages do not imply equal placement states. Stable ordering resolves equal complete costs.
Role reports expose `total_travel`, `shift_count`, `largest_shift`, `peak_position` and actual selected hand positions. Accompaniment costs separate travel from secondary `effort`; individual spans separate hand shift, finger adjustment, string changes, grip effort and register tie-breaking. Repeat travel is assigned to the first canonical span. Solo reports call the time-weighted subordinate value `secondary_effort`. Cache fingerprints include placement code while accompaniment rhythm seeds remain unchanged. Every role remains independently composed; the solo model, pitches and rhythms are unchanged.
The pre-window baseline is preserved outside production inputs under `/tmp/vamp-hand-window-baseline-dwh4anzh`. Verification compares sounded content and rhythms separately from TAB, and checks all electric melody chords for strings 1–3 or 1–4. Independent exhaustive test oracles cover complete grip/window paths, fixed neighbors, cyclic repeats and single-note paths; tests also cover a greedy-position trap, future peak equalization, zero-cost within-window string changes and tied-event invariance.
Hand-window verification on October 8, 2026: `python3 modulus.py python -m unittest discover -s tests -p 'test_*.py'` passed 472 tests in 100.928 seconds. The subsequent asset build exposed a print-footer overlap: Letter pagination had reserved a nominal font height instead of the actual page-number outline. The new tight-page regression reproduced that failure, and `python3 modulus.py python -m unittest tests.test_printing` passed all eight tests after measured footer bounds replaced the estimate. Content and collision audits remain enabled.
`python3 modulus.py build --target x86_64_gcc12_debug --target arm_64_gcc12_release` then passed both asset checks and produced six complete PDFs (42 pages). Native regeneration took 33 seconds with cached roles and a cold engraving namespace; ARM reused the assets in six seconds. Before the footer repair, uncached composition completed in five seconds for all twelve roles, with solos taking about 0.5 seconds each. `python3 modulus.py test --target x86_64_gcc12_debug` passed in 13.27 seconds. All 400 score JSON, normal/playback PNG, report and PDF files match between native and ARM.
Against the saved pre-window baseline, all four solos retain exactly the same pitches, timing and ties; vocal documents and accompaniment rhythm reports remain identical. Electric melody retains its exact source notes and timing; supporting chord tones may change to enforce string 1. All 46 All of Me and 33 Autumn Leaves electric melody chord fragments now use strings 1–3 or 1–4; previously 13 and three fragments respectively omitted string 1. Short monophonic melody remains permissible.
| Solo | Total hand travel, before → after | Number of shifts, before → after | Peak hand position, before → after |
|---|---:|---:|---:|
| All of Me acoustic | 131 → 12 | 87 → 10 | 9 → 7 |
| All of Me electric | 128 → 9 | 87 → 7 | 9 → 7 |
| Autumn Leaves acoustic | 141 → 2 | 85 → 2 | 9 → 3 |
| Autumn Leaves electric | 106 → 6 | 77 → 6 | 9 → 5 |
Travel is the sum of absolute window-position changes, measured in frets. An independent dynamic program over every legal string/fret/window confirmed that each new solo achieves the minimum raw travel for its unchanged sounding notes. This verifies the placement objective, not anatomical comfort or musical preference. The canonical accompaniment cycles now travel 10/14 frets for All of Me/Autumn Leaves acoustic, and 0/6 for electric; they retain source-based repeat consistency. No listening assessment was performed.
The sibling Modulus Conan helper was repaired to call `modulus.build_context.build_context`, replacing the removed `modulus.cli.build_context` API. Its Python suite passed 132 tests with four existing skips. A locally exported and built Modulus package passed `python3 modulus.py configure --target x86_64_gcc12_release`, followed by the release build and runtime test above. The offline attempt first identified missing cached Ligo sources; allowing ordinary Conan source retrieval completed configuration. An unwritable generated engraving cache was also restored to the build user's ownership. The Modulus source fix must be published through that repository's normal release process before remote CI using `modulus/latest` receives it; no remote package was published during this repair.
Recurring-groove verification on October 6, 2026: the full Python regression command passed 423 tests in 149.85 seconds. After the final partial-entry grip fix and one additional real-evaluator fill test, the focused accompaniment/arrangement command passed 59 tests in 21.64 seconds. The native test target passed in 30.58 seconds. The initial native rebuild took 42 seconds, including 7 seconds for independently generated roles and 33.62 seconds of engraving; accompaniment roles took 0.8–1.6 seconds each. The following ARM build reused all role/score caches and took 11 seconds. All four solo documents, both electric melodies and both vocals exactly matched the prechange baseline; native/ARM score assets matched byte for byte. The chosen base cycles differed by song and retained all two/four requested slots. Autumn Leaves selected the authored overlapping-quarter acoustic pattern. No optional fill improved these production winners, so no fills were forced; independently authored tests verify real source-fit selection of a fill. An acoustic score/TAB page was visually checked for overlap ties in a single part. Recordings were unchanged and no listening assessment was performed. Baseline snapshots stayed under `/tmp/`.