@@ -761,6 +761,7 @@ async function visibleMidiStudioControlOwnership(page, activeTabId) {
761761 playButton : { canonical : "playback from selected canonical song model" , kind : "action" , owner : "Global NAV" , wired : "wired" } ,
762762 playLoopButton : { canonical : "timing preview playback state" , kind : "workflow-state" , owner : "Octave Timeline" , wired : "wired" } ,
763763 playSectionButton : { canonical : "timing preview playback state" , kind : "workflow-state" , owner : "Octave Timeline" , wired : "wired" } ,
764+ playSequenceButton : { canonical : "timing preview playback state from Song Sequence order" , kind : "workflow-state" , owner : "Octave Timeline" , wired : "wired" } ,
764765 regenerateArrangementButton : { canonical : "music.songs[].studioArrangement generated lanes from Song Sheet sequence" , kind : "canonical-action" , owner : "Song Setup" , wired : "wired" } ,
765766 renderedExportSaveButton : { canonical : "future rendered audio renderer" , kind : "unwired" , owner : "Export" , wired : "unwired" } ,
766767 renderedExportTargetTypeSelect : { canonical : "future rendered audio renderer" , kind : "unwired" , owner : "Export" , wired : "unwired" } ,
@@ -968,6 +969,9 @@ async function visibleMidiStudioControlOwnership(page, activeTabId) {
968969 if ( element . dataset . instrumentDerivedField ) {
969970 return { canonical : "derived from selected instrument preview mapping" , kind : "readonly" , owner : "Instruments" , wired : "wired" } ;
970971 }
972+ if ( element . dataset . auditionRangeSummary ) {
973+ return { canonical : "derived from selected instrument octave range" , kind : "readonly" , owner : "Instruments" , wired : "wired" } ;
974+ }
971975 if ( element . dataset . auditionNote ) {
972976 return { canonical : "Preview Synth audition action" , kind : "action" , owner : "Instruments" , wired : "wired" } ;
973977 }
@@ -5267,7 +5271,7 @@ test.describe("MIDI Studio V2", () => {
52675271 }
52685272 } ) ;
52695273
5270- test ( "validates PR089-092 production templates safe regeneration instruments and export readiness" , async ( { page } ) => {
5274+ test ( "validates PR089-096 production templates arrangement playback instruments and export readiness" , async ( { page } ) => {
52715275 await page . setViewportSize ( { width : 1600 , height : 900 } ) ;
52725276 const server = await openMidiStudioForImport ( page ) ;
52735277 try {
@@ -5306,6 +5310,12 @@ test.describe("MIDI Studio V2", () => {
53065310 const manualLead = "F5 F5 F5 F5 | F5 F5 F5 F5 | F5 F5 F5 F5 | F5 F5 F5 F5 | F5 F5 F5 F5 | F5 F5 F5 F5" ;
53075311 await page . locator ( "#instrumentGridLeadInput" ) . fill ( manualLead ) ;
53085312 await page . locator ( "#normalizeInstrumentGridButton" ) . click ( ) ;
5313+ await selectMidiStudioTab ( page , "studio" ) ;
5314+ await waitForCanvasRender ( page ) ;
5315+ const manualLeadCounts = page . locator ( "#timelineInstrumentQuickList [data-arrangement-source-counts='lead']" ) ;
5316+ await expect ( manualLeadCounts ) . toContainText ( "Generated 0 / Manual 24" ) ;
5317+ await expect ( manualLeadCounts ) . toHaveAttribute ( "data-generated-count" , "0" ) ;
5318+ await expect ( manualLeadCounts ) . toHaveAttribute ( "data-manual-count" , "24" ) ;
53095319 await selectMidiStudioTab ( page , "song-setup" ) ;
53105320 await page . locator ( "#regenerateArrangementButton" ) . click ( ) ;
53115321 await expect ( page . locator ( "#regenerateArrangementButton" ) ) . toHaveAttribute ( "data-regeneration-pending" , "true" ) ;
@@ -5342,6 +5352,38 @@ test.describe("MIDI Studio V2", () => {
53425352
53435353 await selectMidiStudioTab ( page , "studio" ) ;
53445354 await waitForCanvasRender ( page ) ;
5355+ const generatedLeadCounts = page . locator ( "#timelineInstrumentQuickList [data-arrangement-source-counts='lead']" ) ;
5356+ await expect ( generatedLeadCounts ) . toContainText ( "Generated 24 / Manual 0" ) ;
5357+ await expect ( generatedLeadCounts ) . toHaveAttribute ( "data-generated-count" , "24" ) ;
5358+ await expect ( generatedLeadCounts ) . toHaveAttribute ( "data-manual-count" , "0" ) ;
5359+ const canvasSourceState = await canvasTimelineState ( page ) ;
5360+ expect ( canvasSourceState . sourceCounts . generated ) . toBeGreaterThan ( 0 ) ;
5361+ expect ( canvasSourceState . sourceCounts . generated + canvasSourceState . sourceCounts . manual ) . toBeGreaterThan ( 0 ) ;
5362+ await expect ( octaveTimelineCanvas ( page ) ) . toHaveAttribute ( "data-generated-note-count" , / \d + / ) ;
5363+ await expect ( octaveTimelineCanvas ( page ) ) . toHaveAttribute ( "data-manual-note-count" , / \d + / ) ;
5364+
5365+ await page . locator ( "#instrumentGridSectionSelect" ) . selectOption ( "Verse" ) ;
5366+ await page . locator ( "#playSectionButton" ) . click ( ) ;
5367+ await expect ( page . locator ( "#instrumentGridTransportState" ) ) . toContainText ( "Playing section: Verse" ) ;
5368+ await expect ( page . locator ( "#instrumentGridOutput" ) ) . toHaveAttribute ( "data-preview-playback-mode" , "section" ) ;
5369+ await expect ( octaveTimelineCanvas ( page ) ) . toHaveAttribute ( "data-playback-section" , "Verse" ) ;
5370+ await expect ( octaveTimelineCanvas ( page ) ) . toHaveAttribute ( "data-playback-section-color" , / # / ) ;
5371+ await page . locator ( "#stopTimingPreviewButton" ) . click ( ) ;
5372+ await expect ( page . locator ( "#instrumentGridTransportState" ) ) . toContainText ( "Preview Synth timing preview stopped." ) ;
5373+
5374+ await page . locator ( "#playSequenceButton" ) . click ( ) ;
5375+ await expect ( page . locator ( "#instrumentGridTransportState" ) ) . toContainText ( "Playing sequence: Song Sequence" ) ;
5376+ await expect ( page . locator ( "#instrumentGridOutput" ) ) . toHaveAttribute ( "data-preview-playback-mode" , "sequence" ) ;
5377+ await expect ( octaveTimelineCanvas ( page ) ) . toHaveAttribute ( "data-playback-section" , "Verse" ) ;
5378+ await page . locator ( "#stopTimingPreviewButton" ) . click ( ) ;
5379+
5380+ await page . locator ( "#instrumentGridLoopStartSelect" ) . selectOption ( "Verse" ) ;
5381+ await page . locator ( "#instrumentGridLoopEndSelect" ) . selectOption ( "Solo" ) ;
5382+ await page . locator ( "#playLoopButton" ) . click ( ) ;
5383+ await expect ( page . locator ( "#instrumentGridTransportState" ) ) . toContainText ( "Playing loop: Verse to Solo" ) ;
5384+ await expect ( page . locator ( "#instrumentGridOutput" ) ) . toHaveAttribute ( "data-preview-playback-mode" , "loop" ) ;
5385+ await page . locator ( "#stopTimingPreviewButton" ) . click ( ) ;
5386+
53455387 await page . locator ( "#duplicateInstrumentRowButton" ) . click ( ) ;
53465388 await expect ( timelineQuickInstrumentRow ( page , "lead-1" ) ) . toHaveClass ( / i s - s e l e c t e d / ) ;
53475389 await expect ( page . locator ( "#timelineInstrumentQuickList" ) ) . toHaveAttribute ( "data-selected-instrument-id" , "lead-1" ) ;
@@ -5352,15 +5394,27 @@ test.describe("MIDI Studio V2", () => {
53525394 await selectMidiStudioTab ( page , "instruments" ) ;
53535395 await expect ( instrumentRow ( page , "lead-1" ) ) . toHaveClass ( / i s - s e l e c t e d / ) ;
53545396 await expect ( page . locator ( "#instrumentAuditionKeyboard" ) ) . toHaveAttribute ( "data-selected-instrument-id" , "lead-1" ) ;
5397+ await expect ( page . locator ( "#selectedInstrumentEditor [data-instrument-playable-range-lane='lead-1']" ) ) . toHaveText ( "C3 to B6" ) ;
5398+ await expect ( page . locator ( "#instrumentAuditionKeyboard" ) ) . toHaveAttribute ( "data-playable-range" , "C3 to B6" ) ;
5399+ await expect ( page . locator ( "#instrumentAuditionKeyboard [data-audition-range-summary='lead-1']" ) ) . toHaveText ( "C3 to B6" ) ;
5400+ await setInputValue ( page , "#previewOctaveLowLead1Input" , "4" ) ;
5401+ await expect ( page . locator ( "#selectedInstrumentEditor [data-instrument-playable-range-lane='lead-1']" ) ) . toHaveText ( "C4 to B6" ) ;
5402+ await expect ( page . locator ( "#instrumentAuditionKeyboard" ) ) . toHaveAttribute ( "data-playable-range" , "C4 to B6" ) ;
5403+ await expect ( page . locator ( "#instrumentAuditionKeyboard [data-audition-note='C3']" ) ) . toHaveCount ( 0 ) ;
5404+ await expect ( page . locator ( "#instrumentAuditionKeyboard [data-audition-note='C4']" ) ) . toHaveCount ( 1 ) ;
53555405 await page . locator ( "#instrumentAuditionKeyboard [data-audition-note='C5']" ) . click ( ) ;
53565406 await expect ( page . locator ( "#statusLog" ) ) . toHaveValue ( / I N F O A u d i t i o n e d C 5 f o r L e a d 1 / ) ;
53575407
53585408 await selectMidiStudioTab ( page , "export" ) ;
53595409 await expect ( page . locator ( "#exportRenderSource [data-export-field='selected-song'] dd" ) ) . toHaveText ( "Camptown Races UAT Reel" ) ;
53605410 await expect ( page . locator ( "#exportRenderSource [data-export-field='classification'] dd" ) ) . toHaveText ( "Loop" ) ;
53615411 await expect ( page . locator ( "#exportRenderSource [data-export-field='generated-id'] dd" ) ) . toHaveText ( "camptownRacesUatReel-Loop" ) ;
5412+ await expect ( page . locator ( "#exportRenderSource [data-export-field='sequence-length'] dd" ) ) . toHaveText ( "2" ) ;
5413+ await expect . poll ( async ( ) => Number ( await page . locator ( "#exportRenderSource [data-export-field='note-count'] dd" ) . textContent ( ) ) ) . toBeGreaterThan ( 0 ) ;
5414+ await expect . poll ( async ( ) => Number ( await page . locator ( "#exportRenderSource [data-export-field='instrument-count'] dd" ) . textContent ( ) ) ) . toBeGreaterThan ( 4 ) ;
53625415 await expect ( page . locator ( "#exportRenderSource [data-export-field='target-output-formats'] dd" ) ) . toHaveText ( "WAV: missing; MP3: missing; OGG: missing" ) ;
5363- await expect ( page . locator ( "#exportStatusDetails [data-export-field='status'] dd" ) ) . toContainText ( "WARN: Export source is ready, but target paths are missing for WAV, MP3, OGG" ) ;
5416+ await expect ( page . locator ( "#exportStatusDetails [data-export-field='status'] dd" ) ) . toContainText ( "WARN: Export source is ready with 2 sequence item(s)" ) ;
5417+ await expect ( page . locator ( "#exportStatusDetails [data-export-field='status'] dd" ) ) . toContainText ( "target paths are missing for WAV, MP3, OGG" ) ;
53645418 await expect ( page . locator ( "#futureSoundFontSelect" ) ) . toHaveAttribute ( "data-midi-studio-unwired" , "not-implemented" ) ;
53655419 await expect ( page . locator ( "#renderedExportSaveButton" ) ) . toHaveText ( "Save WAV" ) ;
53665420 await page . locator ( "#renderedExportTargetTypeSelect" ) . selectOption ( "mp3" ) ;
0 commit comments