Skip to content

Commit 52bdf18

Browse files
committed
Improve MIDI Studio V2 instrument audibility and icon tooltips - PR_26146_031-midi-studio-v2-instrument-audibility-and-tooltips
1 parent d87c0b5 commit 52bdf18

6 files changed

Lines changed: 332 additions & 28 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# PR_26146_031-midi-studio-v2-instrument-audibility-and-tooltips Validation
2+
3+
Status: PASS
4+
5+
## Scope
6+
7+
- Continued from `PR_26146_030-midi-studio-v2-instrument-row-polish`.
8+
- Expanded MIDI Studio V2 GM family instrument lists so every type exposes at least 3 options.
9+
- Added audible Preview Synth fallback mappings and visible WARN diagnostics for unsupported GM options.
10+
- Added short instrument audition playback on instrument/type changes after audio context access.
11+
- Added accessible `title`/`aria-label` coverage for icon-only mute, solo, show/hide, delete, and add controls.
12+
- Preserved compact instrument rows, widened left column, gray dimmed notes, selected-note layering, chord editing, playback, playhead timing, and no-scroll-jump behavior.
13+
14+
## Validation Commands
15+
16+
```powershell
17+
node --check src/engine/audio/PreviewInstrumentPacks.js
18+
node --check src/engine/audio/PreviewSynthEngine.js
19+
node --check tools/midi-studio-v2/js/MidiStudioV2App.js
20+
node --check tools/midi-studio-v2/js/controls/InstrumentGridControl.js
21+
node --check tests/playwright/tools/MidiStudioV2.spec.mjs
22+
npx playwright test tests/playwright/tools/MidiStudioV2.spec.mjs -g "octave timeline editor is the default editable and playable Studio workflow|octave grid density supports icon controls and simultaneous chord editing" --reporter=list --workers=1 --timeout=60000
23+
git diff --check
24+
```
25+
26+
## Results
27+
28+
- PASS: changed-file syntax checks passed.
29+
- PASS: targeted MIDI Studio V2 Playwright coverage passed, `2 passed`.
30+
- PASS: `git diff --check` exited successfully. Git reported only its line-ending notice for the touched Playwright spec.
31+
- PASS: Playwright V8 coverage artifacts were refreshed:
32+
- `docs/dev/reports/playwright_v8_coverage_report.txt`
33+
- `docs/dev/reports/coverage_changed_js_guardrail.txt`
34+
- PASS: coverage guardrail reports no changed runtime JS warnings.
35+
36+
## Playwright Proof
37+
38+
The targeted MIDI Studio V2 Playwright run validates:
39+
40+
- octave timeline editor remains visible, editable, and playable by default.
41+
- Songs populate the left Songs column and imported manifest data populates the timeline.
42+
- instrument controls populate the left Instruments column.
43+
- each GM Type/family exposes at least 3 instrument options.
44+
- quiet/unsupported GM instruments map to audible preview presets and log visible WARN diagnostics.
45+
- percussion instruments use percussion preview behavior.
46+
- Mute and Solo have tooltip/accessibility labels while remaining icon-only visually.
47+
- Show/Hide, Delete, and Add controls have tooltip/accessibility labels.
48+
- icon-only controls remain compact on one horizontal row without wrapping.
49+
- selecting an instrument highlights its notes while other visible instrument notes remain dimmed.
50+
- show/hide behavior still hides and restores instrument notes.
51+
- multiple notes can exist in the same bar/beat column, and adding one note does not remove sibling notes.
52+
- chords and simultaneous drum events remain editable and playable.
53+
- Play and Stop still work from visible timeline data.
54+
- playhead progression remains aligned to bar/beat timing.
55+
- instrument button clicks do not change the Instruments panel scroll position.
56+
57+
## Lanes
58+
59+
- engine/audio runtime: executed through changed-file syntax checks and MIDI Studio V2 browser playback coverage because `src/engine/audio` preview synthesis behavior changed.
60+
- tool runtime: executed through targeted MIDI Studio V2 Playwright coverage because instrument controls, diagnostics, and playback interactions changed.
61+
- integration: skipped; this PR does not change Workspace handoff contracts.
62+
- samples: skipped by explicit PR instruction. Full samples smoke test was not run.
63+
- recovery/UAT: covered only for the targeted MIDI Studio V2 runtime slice named by this PR.
64+
65+
## Manual Validation Notes
66+
67+
1. Open `tools/midi-studio-v2/index.html`.
68+
2. On the Studio tab, verify Instruments rows show compact icon-only Mute, Solo, Show/Hide, and Delete controls plus accessible browser tooltips.
69+
3. Open each GM Type dropdown family and verify at least 3 instrument options are available.
70+
4. Select `Synth Effects` -> `FX 4 (Atmosphere)` and verify the status log shows a WARN mapping message and the preview tone is audible after audio is enabled.
71+
5. Select `Percussive` -> `Room Drum Kit` and verify the status log shows a WARN mapping message and the preview is percussive.
72+
6. Click instrument buttons while the Instruments area is scrolled and verify scroll position does not jump.
73+
7. Add simultaneous notes in one bar/beat column, press Play, then Stop, and verify chords/drums play and stop correctly.
74+
75+
## Out Of Scope
76+
77+
- SoundFont playback.
78+
- export rendering.
79+
- MIDI recording/input.
80+
- full samples smoke test.
Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
export const PREVIEW_INSTRUMENT_PACKS = [
1+
const BASE_PREVIEW_INSTRUMENT_PACKS = [
22
{
33
id: "retro-square-lead",
44
label: "Lead 1 (Square)",
55
synthRole: "lead",
66
typeGroup: "Synth Lead",
7-
volume: 0.075,
7+
volume: 0.13,
88
waveform: "square"
99
},
1010
{
1111
id: "retro-pulse-lead",
1212
label: "Lead 2 (Sawtooth)",
1313
synthRole: "lead",
1414
typeGroup: "Synth Lead",
15-
volume: 0.07,
15+
volume: 0.12,
1616
waveform: "sawtooth"
1717
},
1818
{
@@ -21,7 +21,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
2121
synthRole: "bass",
2222
typeGroup: "Bass",
2323
transposeSemitones: -12,
24-
volume: 0.095,
24+
volume: 0.16,
2525
waveform: "triangle"
2626
},
2727
{
@@ -30,7 +30,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
3030
label: "Electric Piano 1",
3131
synthRole: "lead",
3232
typeGroup: "Piano",
33-
volume: 0.065,
33+
volume: 0.12,
3434
waveform: "sine"
3535
},
3636
{
@@ -39,7 +39,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
3939
label: "Acoustic Grand Piano",
4040
synthRole: "lead",
4141
typeGroup: "Piano",
42-
volume: 0.065,
42+
volume: 0.12,
4343
waveform: "sine"
4444
},
4545
{
@@ -48,7 +48,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
4848
label: "Celesta",
4949
synthRole: "lead",
5050
typeGroup: "Chromatic Percussion",
51-
volume: 0.058,
51+
volume: 0.11,
5252
waveform: "sine"
5353
},
5454
{
@@ -57,7 +57,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
5757
label: "Drawbar Organ",
5858
synthRole: "pad",
5959
typeGroup: "Organ",
60-
volume: 0.052,
60+
volume: 0.11,
6161
waveform: "sine"
6262
},
6363
{
@@ -66,15 +66,15 @@ export const PREVIEW_INSTRUMENT_PACKS = [
6666
durationScale: 1.3,
6767
synthRole: "pad",
6868
typeGroup: "Synth Pad",
69-
volume: 0.045,
69+
volume: 0.1,
7070
waveform: "sine"
7171
},
7272
{
7373
id: "basic-drums",
7474
label: "Standard Drum Kit",
7575
synthRole: "percussion",
7676
typeGroup: "Percussive",
77-
volume: 0.16,
77+
volume: 0.24,
7878
waveform: "noise"
7979
},
8080
{
@@ -84,7 +84,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
8484
synthRole: "pad",
8585
transposeSemitones: 12,
8686
typeGroup: "Synth Pad",
87-
volume: 0.035,
87+
volume: 0.1,
8888
waveform: "sine"
8989
},
9090
{
@@ -93,7 +93,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
9393
label: "Violin",
9494
synthRole: "lead",
9595
typeGroup: "Strings",
96-
volume: 0.055,
96+
volume: 0.11,
9797
waveform: "sine"
9898
},
9999
{
@@ -103,7 +103,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
103103
label: "String Ensemble 1",
104104
synthRole: "pad",
105105
typeGroup: "Ensemble",
106-
volume: 0.04,
106+
volume: 0.1,
107107
waveform: "sine"
108108
},
109109
{
@@ -112,7 +112,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
112112
label: "Brass Section",
113113
synthRole: "lead",
114114
typeGroup: "Brass",
115-
volume: 0.07,
115+
volume: 0.12,
116116
waveform: "sawtooth"
117117
},
118118
{
@@ -121,7 +121,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
121121
label: "Clarinet",
122122
synthRole: "lead",
123123
typeGroup: "Reed",
124-
volume: 0.06,
124+
volume: 0.11,
125125
waveform: "triangle"
126126
},
127127
{
@@ -130,7 +130,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
130130
label: "Flute",
131131
synthRole: "lead",
132132
typeGroup: "Pipe",
133-
volume: 0.055,
133+
volume: 0.11,
134134
waveform: "sine"
135135
},
136136
{
@@ -139,7 +139,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
139139
label: "Electric Guitar (Clean)",
140140
synthRole: "lead",
141141
typeGroup: "Guitar",
142-
volume: 0.06,
142+
volume: 0.11,
143143
waveform: "triangle"
144144
},
145145
{
@@ -148,7 +148,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
148148
label: "FX 1 (Rain)",
149149
synthRole: "pad",
150150
typeGroup: "Synth Effects",
151-
volume: 0.04,
151+
volume: 0.1,
152152
waveform: "sawtooth"
153153
},
154154
{
@@ -157,7 +157,7 @@ export const PREVIEW_INSTRUMENT_PACKS = [
157157
label: "Shamisen",
158158
synthRole: "lead",
159159
typeGroup: "Ethnic",
160-
volume: 0.055,
160+
volume: 0.11,
161161
waveform: "triangle"
162162
},
163163
{
@@ -166,11 +166,76 @@ export const PREVIEW_INSTRUMENT_PACKS = [
166166
label: "FX 8 (Sci-Fi)",
167167
synthRole: "lead",
168168
typeGroup: "Sound Effects",
169-
volume: 0.055,
169+
volume: 0.11,
170170
waveform: "square"
171171
}
172172
];
173173

174+
const SUPPLEMENTAL_GM_INSTRUMENTS = [
175+
gmFallback("gm-bright-acoustic-piano", "Bright Acoustic Piano", "Piano", "preview-acoustic-grand-piano"),
176+
gmFallback("gm-glockenspiel", "Glockenspiel", "Chromatic Percussion", "preview-celesta"),
177+
gmFallback("gm-marimba", "Marimba", "Chromatic Percussion", "preview-celesta"),
178+
gmFallback("gm-percussive-organ", "Percussive Organ", "Organ", "preview-drawbar-organ"),
179+
gmFallback("gm-church-organ", "Church Organ", "Organ", "preview-drawbar-organ"),
180+
gmFallback("gm-acoustic-guitar-nylon", "Acoustic Guitar (Nylon)", "Guitar", "preview-clean-guitar"),
181+
gmFallback("gm-overdriven-guitar", "Overdriven Guitar", "Guitar", "preview-clean-guitar"),
182+
gmFallback("gm-acoustic-bass", "Acoustic Bass", "Bass", "synth-bass"),
183+
gmFallback("gm-electric-bass-finger", "Electric Bass (Finger)", "Bass", "synth-bass"),
184+
gmFallback("gm-viola", "Viola", "Strings", "preview-violin"),
185+
gmFallback("gm-cello", "Cello", "Strings", "preview-violin"),
186+
gmFallback("gm-synth-strings-1", "Synth Strings 1", "Ensemble", "preview-string-ensemble"),
187+
gmFallback("gm-choir-aahs", "Choir Aahs", "Ensemble", "warm-pad"),
188+
gmFallback("gm-trumpet", "Trumpet", "Brass", "preview-brass-stab"),
189+
gmFallback("gm-trombone", "Trombone", "Brass", "preview-brass-stab"),
190+
gmFallback("gm-alto-sax", "Alto Sax", "Reed", "preview-woodwind"),
191+
gmFallback("gm-oboe", "Oboe", "Reed", "preview-woodwind"),
192+
gmFallback("gm-piccolo", "Piccolo", "Pipe", "preview-flute"),
193+
gmFallback("gm-recorder", "Recorder", "Pipe", "preview-flute"),
194+
gmFallback("gm-lead-calliope", "Lead 3 (Calliope)", "Synth Lead", "retro-square-lead"),
195+
gmFallback("gm-pad-polysynth", "Pad 3 (Polysynth)", "Synth Pad", "warm-pad"),
196+
gmFallback("gm-fx-soundtrack", "FX 2 (Soundtrack)", "Synth Effects", "preview-synth-fx"),
197+
gmFallback("gm-fx-atmosphere", "FX 4 (Atmosphere)", "Synth Effects", "preview-synth-fx"),
198+
gmFallback("gm-sitar", "Sitar", "Ethnic", "preview-shamisen"),
199+
gmFallback("gm-koto", "Koto", "Ethnic", "preview-shamisen"),
200+
gmFallback("gm-room-drum-kit", "Room Drum Kit", "Percussive", "basic-drums"),
201+
gmFallback("gm-power-drum-kit", "Power Drum Kit", "Percussive", "basic-drums"),
202+
gmFallback("gm-applause", "Applause", "Sound Effects", "preview-sci-fi"),
203+
gmFallback("gm-gunshot", "Gunshot", "Sound Effects", "preview-sci-fi")
204+
];
205+
206+
export const PREVIEW_INSTRUMENT_PACKS = [
207+
...BASE_PREVIEW_INSTRUMENT_PACKS,
208+
...SUPPLEMENTAL_GM_INSTRUMENTS
209+
];
210+
211+
function gmFallback(id, label, typeGroup, previewInstrumentId) {
212+
return {
213+
approximationWarning: `${label} is listed for GM coverage and maps to the closest audible Preview Synth preset.`,
214+
id,
215+
label,
216+
previewInstrumentId,
217+
typeGroup,
218+
unsupportedPreview: true
219+
};
220+
}
221+
174222
export function previewInstrumentById(id) {
175-
return PREVIEW_INSTRUMENT_PACKS.find((instrument) => instrument.id === id) || null;
223+
const instrument = PREVIEW_INSTRUMENT_PACKS.find((entry) => entry.id === id) || null;
224+
if (!instrument?.previewInstrumentId) {
225+
return instrument;
226+
}
227+
const mapped = BASE_PREVIEW_INSTRUMENT_PACKS.find((entry) => entry.id === instrument.previewInstrumentId) || null;
228+
if (!mapped) {
229+
return instrument;
230+
}
231+
return {
232+
...mapped,
233+
...instrument,
234+
mappedPreviewInstrumentId: mapped.id,
235+
mappedPreviewInstrumentLabel: mapped.label,
236+
synthRole: mapped.synthRole,
237+
transposeSemitones: mapped.transposeSemitones,
238+
volume: Math.max(Number(mapped.volume || 0), 0.11),
239+
waveform: mapped.waveform
240+
};
176241
}

0 commit comments

Comments
 (0)