import { Note } from "../music-theory/Note" ; const offsets = [ 0 , 0.5 , 1 , 1.5 , 2 , 3 , 3.5 , 4 , 4.5 , 5 , 5.5 , 6 ] ; class Keyboard { _container : HTMLElement ; _keys : Array < HTMLElement > ; _prevKeys : Array < HTMLElement > ; _chords : Array < Array < string > > ; onKeyDown : any ; onKeyUp : any ; constructor ( container ) { this . _container = document . createElement ( 'div' ) ; this . _container . id = 'keyboard' ; container . setAttribute ( 'touch-action' , 'none' ) ; container . appendChild ( this . _container ) ; this . _keys = [ ] ; this . _prevKeys = [ ] ; this . _chords = [ ] ; this . onKeyDown = function ( ) { } ; this . onKeyUp = function ( ) { } ; } param lowestKeyNum param octaves public resize ( lowestKeyNum = 0 , octaves ) { this . _container . innerHTML = '' ; const keyWidth = ( 1 / 7 ) / octaves ; for ( let keyNum = lowestKeyNum ; keyNum < lowestKeyNum + octaves * 12 ; keyNum ++ ) { let key = document . createElement ( 'div' ) ; key . classList . add ( 'key' ) ; let isSharp = ( [ 1 , 3 , 6 , 8 , 10 ] . indexOf ( keyNum % 12 ) !== - 1 ) ; if ( isSharp ) { key . classList . add ( 'black' , "shadow" ) ; } else { key . classList . add ( 'white' ) ; } const noteName = Note . toNoteName ( keyNum ) ; const isC = keyNum % 12 === 0 ; if ( isC ) { const noteP = document . createElement ( "p" ) ; noteP . classList . add ( 'note-name' ) ; noteP . dataset . noteName = noteName ; noteP . dataset . keyNum = keyNum . toString ( ) ; noteP . innerText = noteName ; key . appendChild ( noteP ) ; } let lowestOctave = Math . floor ( lowestKeyNum / 12 ) ; let lowestOffset = offsets [ lowestKeyNum % 12 ] + lowestOctave * 7 ; let noteOctave = Math . floor ( keyNum / 12 ) ; let offset = offsets [ keyNum % 12 ] + noteOctave * 7 - lowestOffset ; if ( Number . isInteger ( offset ) === true ) { key . style . width = `${ keyWidth * 100 }%` ; key . style . left = `${ offset * keyWidth * 100 }%` ; } else { key . style . width = `${ keyWidth * 100 / 2 }%` ; key . style . left = `${ offset * keyWidth * 100 + keyWidth * 100 / 4 }%` ; } key . dataset . keyNum = keyNum . toString ( ) ; key . dataset . noteName = noteName ; key . setAttribute ( 'touch-action' , 'none' ) ; this . _container . appendChild ( key ) ; this . _bindKeyEvents ( key ) ; this . _keys [ keyNum ] = key ; } } param key private private _bindKeyEvents ( key ) { key . addEventListener ( 'pointerdown' , ( e ) => { const target = e . target as HTMLDivElement ; const keyNum = parseInt ( target . getAttribute ( "data-key-num" ) as string ) ; this . onKeyDown ( keyNum ) ; } ) ; key . addEventListener ( "pointerout" , ( e ) => { const target = e . target as HTMLDivElement ; const keyNum = parseInt ( target . getAttribute ( "data-key-num" ) as string ) ; this . onKeyUp ( keyNum ) ; } ) ; key . addEventListener ( "pointerup" , ( e ) => { const target = e . target as HTMLDivElement ; const keyNum = parseInt ( target . getAttribute ( "data-key-num" ) as string ) ; this . onKeyUp ( keyNum ) ; } ) ; } param keys private _unhighlight ( keys ) { keys . forEach ( function ( key ) { key . classList . remove ( "highlight" ) ; key . classList . remove ( "highlight-root" ) ; } ) ; } public param chord param rootNote public highlight ( chord , rootNote ) { this . _unhighlight ( this . _prevKeys ) ; this . _prevKeys = [ ] ; if ( ! chord ) { console . warn ( "Notes not specified. Cannot play the chord" ) ; return false ; } for ( const note of chord as Array < string > ) { const selectorNote = note . replace ( "#" , "\\#" ) ; const key = document . querySelector ( `.key[data-note-name=${ selectorNote }]` ) ; if ( key !== null ) { key . classList . add ( "highlight" ) ; this . _prevKeys . push ( key as HTMLElement ) ; } } this . _chords . push ( chord ) ; if ( rootNote ) { this . _highlightRootNote ( rootNote ) ; } return true ; } param note private private _highlightRootNote ( note ) { const selectorNote = note . replace ( "#" , "\\#" ) ; const key = document . querySelector ( `.key[data-note-name=${ selectorNote }]` ) ; if ( key !== null ) { key . classList . add ( "highlight-root" ) ; this . _prevKeys . push ( key as HTMLElement ) ; } } } export { Keyboard } ;