Skip to content

Commit a38677b

Browse files
committed
Simplify MIDI Studio V2 Song Sheet with populated sections and sequence builder - PR_26146_071-midi-studio-v2-song-sheet-sequence-builder
1 parent 6bf1621 commit a38677b

10 files changed

Lines changed: 895 additions & 137 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# PR_26146_071 MIDI Studio V2 Song Sheet Sequence Builder Validation
2+
3+
Status: PASS
4+
5+
## Scope Verified
6+
7+
- Replaced the freeform Song Sheet section workflow with simple named section inputs for Intro, Verse, Chorus, Bridge, Outro, plus Custom sections.
8+
- Added an Available Sections list that only shows populated musical sections.
9+
- Added a Song Sequence builder with Add, Move Up, Move Down, and Remove actions.
10+
- Removed the visible Loop sections Song Sheet field.
11+
- Added Apply Song Sheet To controls for Chords/Pad, Bass, Drums, and Lead with requested defaults.
12+
- Parse Guided Song Sheet now uses populated sections, Song Sequence order, and selected apply targets.
13+
- Canonical song model, Octave Timeline colors, diagnostics, and JSON Details refresh from the new sequence workflow.
14+
15+
## Validation Commands
16+
17+
PASS - Project instructions read:
18+
19+
- `Get-Content docs/dev/PROJECT_INSTRUCTIONS.md`
20+
21+
PASS - Changed-file syntax checks:
22+
23+
- `node --check tools/midi-studio-v2/js/controls/SongSheetControl.js`
24+
- `node --check src/engine/audio/SongSheetParser.js`
25+
- `node --check tools/midi-studio-v2/js/MidiStudioV2App.js`
26+
- `node --check tools/midi-studio-v2/js/bootstrap.js`
27+
- `node --check tools/midi-studio-v2/js/services/MidiStudioStateSerializer.js`
28+
- `node --check tests/playwright/tools/MidiStudioV2.spec.mjs`
29+
30+
PASS - External JS/CSS only check:
31+
32+
- `Select-String -Path tools/midi-studio-v2/index.html -Pattern '<script(?![^>]*\bsrc=)|<style|\son[a-z]+='`
33+
- Result: no inline script, style, or event handler matches.
34+
35+
PASS - Targeted MIDI Studio V2 Playwright tests:
36+
37+
- `npx playwright test tests/playwright/tools/MidiStudioV2.spec.mjs --grep "PR071" --reporter=list`
38+
- Result: 1 passed.
39+
40+
PASS - Targeted regression Playwright tests:
41+
42+
- `npx playwright test tests/playwright/tools/MidiStudioV2.spec.mjs --grep "PR069|PR070|PR071" --reporter=list`
43+
- Result: 3 passed.
44+
45+
PASS - Diff hygiene:
46+
47+
- `git diff --check`
48+
- Result: no whitespace errors.
49+
- Note: Git reported LF-to-CRLF normalization warnings for touched text files only.
50+
51+
SKIPPED - Full samples smoke test:
52+
53+
- Not run, per PR instructions.
54+
55+
## Requirement Matrix
56+
57+
PASS - Available Sections shows only populated sections.
58+
59+
PASS - Empty sections do not appear in Available Sections.
60+
61+
PASS - Add appends the selected Available Sections item to the Song Sequence.
62+
63+
PASS - Move Up and Move Down reorder Song Sequence.
64+
65+
PASS - Remove removes the selected Song Sequence item.
66+
67+
PASS - Loop sections field is removed from visible Song Sheet UI.
68+
69+
PASS - Apply Song Sheet To controls exist with defaults: Chords/Pad on, Bass on, Drums on when a drums instrument exists, Lead off.
70+
71+
PASS - Parse Guided Song Sheet uses sequence order and selected apply targets.
72+
73+
PASS - Canonical song model updates `studioArrangement.songSheet.sections`, `sequence`, `applyTargets`, and generated timeline lanes.
74+
75+
PASS - Octave Timeline colors follow the musical sequence, with repeated musical sections sharing colors.
76+
77+
PASS - Play and Stop still work in the targeted coverage.
78+
79+
## Result
80+
81+
PR PASS. The Song Sheet workflow now uses populated musical sections and a direct Song Sequence builder while preserving canvas timeline playback and section color behavior.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# PR_26146_071 MIDI Studio V2 Song Sheet Sequence Map
2+
3+
## Visible Workflow
4+
5+
| UI Control | Purpose | Canonical model |
6+
| --- | --- | --- |
7+
| Intro | Populated Intro chord/progression text | `music.songs[].studioArrangement.songSheet.sections` |
8+
| Verse | Populated Verse chord/progression text | `music.songs[].studioArrangement.songSheet.sections` |
9+
| Chorus | Populated Chorus chord/progression text | `music.songs[].studioArrangement.songSheet.sections` |
10+
| Bridge | Populated Bridge chord/progression text | `music.songs[].studioArrangement.songSheet.sections` |
11+
| Outro | Populated Outro chord/progression text | `music.songs[].studioArrangement.songSheet.sections` |
12+
| Custom sections | Additional `Name: chords` section rows | `music.songs[].studioArrangement.songSheet.sections` |
13+
| Available Sections | Derived list of populated sections only | Derived from section inputs |
14+
| Song Sequence | Ordered list of musical section labels | `music.songs[].studioArrangement.songSheet.sequence` |
15+
| Add | Appends selected available section to sequence | Updates `songSheet.sequence` |
16+
| Move Up | Moves selected sequence item earlier | Updates `songSheet.sequence` |
17+
| Move Down | Moves selected sequence item later | Updates `songSheet.sequence` |
18+
| Remove | Removes selected sequence item | Updates `songSheet.sequence` |
19+
| Drag/drop sequence | Future placeholder | Red/unwired, no data mutation |
20+
| Apply Chords/Pad | Applies Song Sheet chords and pad generation | `songSheet.applyTargets.chordsPad` |
21+
| Apply Bass | Applies generated bass from Song Sheet chords | `songSheet.applyTargets.bass` |
22+
| Apply Drums | Applies generated drums when enabled | `songSheet.applyTargets.drums` |
23+
| Apply Lead | Applies generated lead only when enabled | `songSheet.applyTargets.lead` |
24+
25+
## Canonical Flow
26+
27+
1. The Song Sheet control reads populated named/custom section inputs.
28+
2. Empty sections are omitted from Available Sections and are not emitted to canonical `songSheet.sections`.
29+
3. The Song Sequence list writes a comma-separated canonical `songSheet.sequence`.
30+
4. Parse Guided Song Sheet emits parser text using populated section definitions plus sequence order.
31+
5. `SongSheetParser` expands reusable definitions by sequence and skips empty musical sections.
32+
6. `MidiStudioV2App.applySongSheetToGrid` updates section colors and only the selected apply targets.
33+
7. The canonical arrangement refreshes Octave Timeline, diagnostics, and JSON Details.
34+
35+
## Loop Semantics
36+
37+
The separate Song Sheet Loop sections field was removed. Looping is controlled by playback loop behavior and selected playback regions, not by a Song Sheet loop text field.
38+
39+
## Defaults
40+
41+
- Chords/Pad: enabled.
42+
- Bass: enabled.
43+
- Drums: enabled when a drums lane/instrument exists.
44+
- Lead: disabled.
45+
46+
## UAT Notes
47+
48+
PASS - Empty Bridge input is omitted from Available Sections.
49+
50+
PASS - Repeated Verse and Chorus sequence entries share Octave Timeline section colors.
51+
52+
PASS - Lead remains excluded when the Lead target is disabled, while selected Chords/Pad, Bass, and Drums targets are applied.

src/engine/audio/SongSheetParser.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export class SongSheetParser {
3131
chords: [],
3232
label,
3333
lineNumber,
34-
loop: label.toLowerCase() === "loop",
3534
timeline: []
3635
};
3736
sectionDefinitions.push(activeSection);
@@ -79,11 +78,15 @@ export class SongSheetParser {
7978
sectionDefinitions.forEach((section) => {
8079
section.bars = section.chords.length;
8180
if (!section.chords.length) {
82-
warnings.push(`Section ${section.label} is empty.`);
81+
warnings.push(`Empty musical section ${section.label} was skipped.`);
8382
}
8483
});
85-
const sectionLookup = new Map(sectionDefinitions.map((section) => [section.label.toLowerCase(), section]));
86-
const sequence = metadata.sequence.length ? metadata.sequence : sectionDefinitions.map((section) => section.label);
84+
const populatedDefinitions = sectionDefinitions.filter((section) => section.chords.length);
85+
if (!populatedDefinitions.length) {
86+
return { ok: false, message: "Song Sheet must include at least one populated musical section." };
87+
}
88+
const sectionLookup = new Map(populatedDefinitions.map((section) => [section.label.toLowerCase(), section]));
89+
const sequence = metadata.sequence.length ? metadata.sequence : populatedDefinitions.map((section) => section.label);
8790
const missingSequenceLabel = sequence.find((label) => !sectionLookup.has(label.toLowerCase()));
8891
if (missingSequenceLabel) {
8992
return { ok: false, message: `Song Sheet Sequence references missing musical section: ${missingSequenceLabel}` };
@@ -108,7 +111,7 @@ export class SongSheetParser {
108111
ok: true,
109112
sections,
110113
sectionDefinitions,
111-
sectionSummary: sections.map((section) => `${section.label}: ${section.bars} bars, ${section.chords.length} chords${section.loop ? ", loop" : ""}`).join("; "),
114+
sectionSummary: sections.map((section) => `${section.label}: ${section.bars} bars, ${section.chords.length} chords`).join("; "),
112115
sequence,
113116
style: metadata.style || "not declared",
114117
tempo: metadata.tempo,

0 commit comments

Comments
 (0)