+20
-6
@@ -126,13 +126,13 @@ Measure durations, event onsets/durations, harmony offsets, span times, and pitc
|
||||
|
||||
### Shared Part Generation
|
||||
|
||||
`parts.py` owns shared source validation, melody-copying, measure construction, rests, and solo slashes. `rhythm.py` loads snippets; `accompaniment.py` plans phrase cycles, realizes independent bass/upper voices or electric chord attacks, and owns shared tie-free duration spelling. Before guitar placement, `acoustic.py` shortens generated bass/upper attacks so they cannot overlap and groups them into continuous harmony spans; afterward it combines them into one notated voice. `guitar.py` selects one complete grip per acoustic span using its shared candidate generation and placement search. `melody.py` identifies long tied melody runs and splits them at harmony changes before guitar placement. `arrange/generate.py` supplies the existing seven-section song form. `pitch.py` resolves source instrument transposition and the shared written-octave convention. There is no generic twelve-key transposer.
|
||||
`parts.py` owns shared source validation, melody-copying, measure construction, rests, and temporary solo placeholders. `rhythm.py` loads snippets; `accompaniment.py` plans phrase cycles, realizes independent bass/upper voices or electric chord attacks, and owns shared tie-free duration spelling. Before guitar placement, `acoustic.py` shortens generated bass/upper attacks so they cannot overlap and groups them into continuous harmony spans; afterward it combines them into one notated voice. `acoustic_voicing.py` selects one complete grip per acoustic span. `melody.py` identifies long tied melody runs and splits them at harmony changes before electric guitar placement. `arrange/generate.py` supplies the existing seven-section song form, then `solo/arrangement.py` replaces solo placeholders and backing after regular placement. `pitch.py` resolves source instrument transposition and the shared written-octave convention. There is no generic twelve-key transposer.
|
||||
|
||||
Generated accompaniment has no ties. Acoustic attacks release at the earliest of their planned end, the next combined bass/upper attack, or the barline. Simultaneous bass/upper attacks use the shorter planned duration for the entire grip. If a binary duration would need ties, it shortens to the longest conventional single note that fits, followed by silence; no extra attack or artificial tuplet is introduced. Rhythm-snippet continuations are silent, and the planner does not count them as acoustic coverage. After placement, consolidation preserves the prepared timing and TAB assignments, combines simultaneous tones under one stem, and writes rests only for complete silence. A silent full bar has one measure rest. Old lane stem directions and beam groups are removed in favor of single-voice engraving. Authored melody ties, solo slashes, and electric melody harmonization remain separate from this build-time accompaniment policy.
|
||||
Generated accompaniment has no ties. Acoustic attacks release at the earliest of their planned end, the next combined bass/upper attack, or the barline. Simultaneous bass/upper attacks use the shorter planned duration for the entire grip. If a binary duration would need ties, it shortens to the longest conventional single note that fits, followed by silence; no extra attack or artificial tuplet is introduced. Rhythm-snippet continuations are silent, and the planner does not count them as acoustic coverage. After placement, consolidation preserves the prepared timing and TAB assignments, combines simultaneous tones under one stem, and writes rests only for complete silence. A silent full bar has one measure rest. Old lane stem directions and beam groups are removed in favor of single-voice engraving. Authored melody ties, composed solo sustains, and electric melody harmonization remain separate from this build-time accompaniment policy.
|
||||
|
||||
Acoustic accompaniment uses the planned foundation. Electric accompaniment sections in songs use the response plan; song electric melody sections are raised one octave before harmonization. Song sections remain Rhythm Lead-in, Vocal Melody, Guitar Melody, Acoustic Solo, Electric Solo, Vocal Melody, and Ending, with each part retaining its melody, accompaniment, rest, or solo-slash role. Optional turnarounds are removed before planning the final ending. The generated phrase cycles contain explicit events in reading order, not runtime loops or repeat signs.
|
||||
Acoustic accompaniment uses the planned foundation. Electric accompaniment sections in songs use the response plan; song electric melody sections are raised one octave before harmonization. Song sections remain Rhythm Lead-in, Vocal Melody, Guitar Melody, Acoustic Solo, Electric Solo, Vocal Melody, and Ending. Solo sections now contain composed single-note guitar lines supported by the other guitar. Optional turnarounds are removed before planning the final ending. The generated phrase cycles contain explicit events in reading order, not runtime loops or repeat signs.
|
||||
|
||||
Each source is parsed, validated, and normalized once for all three parts. The batch generator computes shared accompaniment plans before building those parts. Public `arrange_document` and `generate_performance_scores` calls require a keyword `rhythms` argument holding a `generator.rhythm.Library`; internal part builders receive precomputed plans. Builders treat the prepared source and plans as read-only and own their generated notes and spans. Source-path checks reject colliding song slugs before writing parts: for example, `one-two.xml` and `one_two.xml` would both produce `one_two_acoustic.json` and cannot coexist.
|
||||
Each source is parsed, validated, and normalized once for all three parts. The batch generator computes shared accompaniment plans before building those parts. `arrange_document` constructs the regular form with a keyword `rhythms` library. The complete `generate_performance_scores` pipeline additionally requires `scales` and `sequences`; it always composes both solos before writing the song's three parts. Internal part builders receive precomputed plans. Builders treat the prepared source and plans as read-only and own their generated notes and spans. Source-path checks reject colliding song slugs before writing parts: for example, `one-two.xml` and `one_two.xml` would both produce `one_two_acoustic.json` and cannot coexist.
|
||||
|
||||
All three parts use octave-transposing notation: written notes sound one octave lower. This includes the vocal part for the singer's range. Unmarked source notation uses that convention by default. Vocal melodies retain their written pitches and octave numbers. Song electric melody sections add twelve semitones to both written and sounding pitch; the guitar's octave-transposing convention still applies. Written C4 sounds C3 (A-string fret 3), and written C#6 sounds C#5, within the 1-20 fret range. Generated accompaniment is notated using the same convention.
|
||||
|
||||
@@ -184,10 +184,24 @@ The analyzer requires a declared tonic and mode in the song's MusicXML key. A on
|
||||
|
||||
Selection first preserves every required pitch. Recognized ii-V pairs, resolving dominants, and leading-tone diminished resolutions establish local destinations; the declared key provides the default context elsewhere. Minor cadences prefer harmonic minor and major cadences prefer the destination's major collection when compatible. The arrival chord is evaluated afresh, so a plain D-minor tonic need not retain a preceding cadence's raised seventh. Among equally appropriate choices, a deterministic dynamic program minimizes collection changes, then contextual pitch differences. Equivalent modes share a pitch collection rather than forcing new boundaries. This is a conservative harmonic heuristic, not a claim that ambiguous harmony has only one interpretation.
|
||||
|
||||
Only the soloing part receives compact `scale_spans`, with absolute rational start/end offsets, labels, and matched status. `N.C.` and solo boundaries stop annotations. Accompaniment, melody, pitches, TAB, and playback are unchanged; no solo notes are synthesized. Original melody notes are not constraints on these slash-only solo passages. Unmatched chords print `No exact scale match` and emit a source/measure/beat warning without changing the harmony. Invalid scale data or missing tonal metadata instead fail generation before publication.
|
||||
Only the soloing part receives compact `scale_spans`, with absolute rational start/end offsets, labels, and matched status. `N.C.` and solo boundaries stop annotations. The composer consumes the structured scale choices directly and checks every sounding note across its entire duration. It may select another permitted collection to preserve an exact melody quotation, but never silently changes that quotation's notes. A harmony with no exact compatible collection, invalid scale data, or missing tonal metadata fails generation before publication. The standalone scale-analysis utility can still report unmatched chords without composing them.
|
||||
|
||||
Scale labels appear above chord symbols at collection changes and system starts. Detailed parent collections, source and arranged positions, chord-root modes, degrees, chord-tone targets, and selection reasons are written to `generated_assets/analysis/<song>.txt`. These reports are published transactionally with the build output but excluded from both installation and Docker runtime assets. The C++ runtime only displays the generated pages; it does not load the scale library or run harmonic analysis.
|
||||
|
||||
### Phrase Composition
|
||||
|
||||
`generator/solo/` is build-only. `analysis.py` extracts one- and two-bar monophonic motifs from the normalized melody, collapsing ties while retaining repeated attacks, leading rests, source event IDs, and harmonic context. `planning.py` selects at most three motif identities and plans statements, returns, variations, answers, a register peak, and a final callback around two- to four-bar phrase boundaries. Melody breathing points and harmonic destinations influence those boundaries. `types.py` owns exact-time motif, phrase, target, and sounding-pitch models. No annotations are added to source XML.
|
||||
|
||||
`sequences.py` validates the C-major interval paths in `data/sequences/2_4`, `3_4`, and `4_4`, then indexes them by attack count and ranks their range, largest leap, and contour turns. Steps accumulate through octaves, including negative scale indices. Sequence meter describes the source container, not the realized duration. `realization.py` matches those paths to rhythm attacks, not rests or tied continuations. It also supports single targets, explicit repeated notes, holds, and silence. Rhythm snippets retain their meter and exact durations; shorter fitting cells and melody rhythms are not stretched. Catalog entries are ranked by phrase function rather than sampled uniformly or cycled to fill time.
|
||||
|
||||
Phrase search retains at most 64 distinct endings and considers at most 32 ranked extensions per state. Ordered preferences protect motif identity, register/activity, contour, connection, and playability. Arrival windows require actual defining chord tones, respecting suspensions and explicit alterations. Quotes retain all pitches and attack/release timing, with a whole-fragment octave relocation reported explicitly. Other treatments include harmonic adaptation, changed endings, fragmentation, and rhythmic displacement. A scale change need not end the phrase; held pitches must remain permitted throughout. Missing harmony means silence. Chromatic ornaments, swing, dynamics, and automatic solo chords are not added.
|
||||
|
||||
Acoustic starts with melody-related material and a softer activity/register preference; electric introduces a contrasting sequence-derived motif and returns to the melody before the vocal pickup. These are compositional preferences, not fixed notes-per-bar quotas. Each phrase reserves release space. Sounding registers start around G3-D5 and B3-G5 respectively; written guitar notation is one octave above sound, exactly once. `placement.py` chooses frets 1-20 with no open strings, retaining tied positions. Movement cost accounts for the time between attacks at the first declared source tempo, or 120 quarter notes/minute when absent. Runtime tempo remains adjustable; this check is not a guarantee of ease at every tempo.
|
||||
|
||||
`arrangement.py` analyzes each song once and replaces only the two solo-section windows after normal head/accompaniment placement. Electric backs Acoustic Solo; acoustic backs Electric Solo. Neither guitarist accompanies itself. Backing rhythms use the generated solo's foreground and the reserved vocal return, not the absent original melody. Electric may reuse the corresponding placed head grip only when its pitches fit the active solo collection; otherwise ordinary chord-tone placement applies. Existing chord constraints remain unchanged. All non-solo measures and the complete vocal part are regression boundaries. The vocal pickup cutoff is reserved before composing the electric ending.
|
||||
|
||||
`notation.py` converts placed attack spans into ordinary score events, rests, and tied continuations across notation/bar boundaries. Existing engraving, playback compilation, and C++ consumption require no new runtime representation. `report.py` appends phrase/motif/source IDs, transformations, target windows, sounding pitches, and TAB decisions to the build analysis report. A bounded-search failure identifies the passage and search limits; it does not claim mathematical impossibility. Generation still stages and validates the complete output before publication. Listening and playing remain necessary to accept the musical result.
|
||||
|
||||
### Engraving Boundary
|
||||
|
||||
`generator/engraving/` is a custom Python engraver, not a wrapper around MuseScore or another notation engine. `generator.engraving.layout.layout_score(document, style, fonts)` produces immutable layout pages with exact source intervals, notation anchors and drawing primitives. `generator.engraving.raster.rasterize_page(page, fonts)` draws one page at four-times resolution and downsamples it. `generator.engraving.pipeline.engrave_tree(root, fonts)` is the asset-pipeline entry point. `fonts.Fonts`, `types.Style`, and `types.EngravingError` live in their respective engraving modules; page constants and `check_page` live in `validation`. Package initializers do not re-export these APIs. `generator/musicxml_writer.py` remains a round-trip/export utility and is not involved in engraving.
|
||||
@@ -226,7 +240,7 @@ The six compact source files were replaced, not retained as a second authoring f
|
||||
|
||||
References: [MusicXML durations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/duration/), [tuplets](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/tuplet/), [degree alterations](https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/degree-alter/), and [SMuFL accidentals](https://smufl.formats.music/latest/tables/standard-accidentals-12-edo.html).
|
||||
|
||||
Generator checks compare the generated performance summary against `data/expected/performance_summary.json`. The summary stores compatibility invariants such as section layout, chord-label placement, slash-section placement, and vocal timing without committing the generated score files themselves. Refresh that file only when an intentional arrangement-logic change should become the new baseline.
|
||||
Generator checks compare the generated performance summary against `data/expected/performance_summary.json`. The summary stores compatibility invariants such as section layout, chord-label placement, absence of uncomposed solo slashes, and vocal timing without committing the generated score files themselves. Refresh that file only when an intentional arrangement-logic change should become the new baseline.
|
||||
|
||||
## Pico Runtime
|
||||
|
||||
|
||||
Reference in New Issue
Block a user