char-slop/chordpad.vibed.software
git clone https://git.t4t.associates/char-slop/chordpad.vibed.software
040fec5
main
1import { chordsOf } from "../midi/chords.ts" ; 2import type { MidiPart } from "../midi/smf.ts" ; 3 4export type PartKind = "chords" | "notes" ; 5 6/** ask whether each part of a midi file comes in as chords or as melody: null if cancelled */ 7export function choosePartKinds ( 8fileName :string , 9parts :readonly MidiPart [], 10) :Promise < PartKind [] | null > { 11return new Promise ( resolve => { 12const done = ( choice :PartKind [] | null ) => { 13dialog . close (); 14dialog . remove (); 15resolve ( choice ); 16}; 17 18const rows = parts . map ( part => { 19const chords = chordsOf ( part . notes ); 20const timeWith = ( atLeast :number ) => 21chords . filter ( c => c . notes . length >= atLeast ). reduce (( t , c ) => t + c . dur , 0 ); 22const most = Math . max ( ...chords . map ( c => c . notes . length )); 23const select = ( 24< select aria-label = { `import ${ part . name } as` }> 25< option value = "chords" > chords </ option > 26< option value = "notes" > melody </ option > 27</ select > 28) as HTMLSelectElement ; 29select . value = timeWith ( 2 ) > timeWith ( 1 ) / 2 ?"chords" :"notes" ; 30return { 31 select, 32row :( 33< div class = "midi-part" > 34< b >{ part . name }</ b > 35< span class = "midi-detail" > 36{ ` ${ part . notes . length } notes · ${ most === 1 ? "one" : `up to ${ most } ` } at once` } 37< / span> 38{ select } 39< / div> 40), 41}; 42}); 43 44const confirm = ( 45< button type = "button" _onclick = {() => done ( rows . map ( r => r . select . value as PartKind ))}> 46import 47</ button > 48) as HTMLButtonElement ; 49 50const dialog = ( 51< dialog class = "dialog midi-dialog" _oncancel = {() => done ( null )}> 52< h2 >{ fileName }</ h2 > 53< div class = "midi-parts" >{ rows . map ( r => r . row )}</ div > 54< div class = "dialog-actions" > 55< button type = "button" _onclick = {() => done ( null )}> 56cancel 57</ button > 58{ confirm } 59< /div> 60</ dialog > 61) as HTMLDialogElement ; 62 63document . body . append ( dialog ); 64dialog . showModal (); 65confirm . focus (); 66}); 67}