UNPKG

gibberwocky

Version:

music sequencing for Live and Max/MSP

244 lines (202 loc) 7.43 kB
let Gibber = null const MIDI = { channels: [], init( _Gibber ) { Gibber = _Gibber //if( typeof navigator.requestMIDIAccess === 'function' ) { // const midiPromise = navigator.requestMIDIAccess() // .then( midiAccess => { // MIDI.midiAccess = midiAccess // MIDI.createInputAndOutputLists( midiAccess ) // MIDI.openLastUsedPorts() // }, ()=> console.log('access failure') ) // this.midiInputList = document.querySelector( '#midiInputSelect' ) // this.midiOutputList = document.querySelector( '#midiOutputSelect' ) // this.createChannels() //} // XXX restore //this.setModulationOutputRate() }, createClockMenu() { document.querySelector( '#liveSyncRadio' ) .addEventListener( 'click', Gibber.Scheduler.sync.bind( Gibber.Scheduler, 'live' ) ) document.querySelector( '#maxSyncRadio' ) .addEventListener( 'click', Gibber.Scheduler.sync.bind( Gibber.Scheduler, 'max' ) ) document.querySelector( '#clockSyncRadio' ) .addEventListener( 'click', Gibber.Scheduler.sync.bind( Gibber.Scheduler, 'external' ) ) document.querySelector( '#internalSyncRadio' ) .addEventListener( 'click', Gibber.Scheduler.sync.bind( Gibber.Scheduler, 'internal' ) ) }, setModulationOutputRate() { const modulationRate = localStorage.getItem('midi.modulationOutputRate') const modRateInput = document.querySelector('#modulationRate') if( modulationRate !== null && modulationRate !== undefined ) { Gibber.Gen.genish.gen.samplerate = parseFloat( modulationRate ) modRateInput.value = Gibber.Gen.genish.gen.samplerate }else{ Gibber.Gen.genish.gen.samplerate = 60 } modRateInput.onchange = function(e) { Gibber.Gen.genish.gen.samplerate = e.target.value localStorage.setItem('midi.modulationOutputRate', Gibber.Gen.genish.gen.samplerate ) } }, openLastUsedPorts() { const lastMIDIInput = localStorage.getItem('midi.input'), lastMIDIOutput = localStorage.getItem('midi.output') if( lastMIDIInput !== null && lastMIDIInput !== undefined ) { this.selectInputByName( lastMIDIInput ) } if( lastMIDIOutput !== null && lastMIDIOutput !== undefined ) { this.selectOutputByName( lastMIDIOutput ) } }, createChannels() { for( let i = 0; i < 16; i++ ) { this.channels.push( Gibber.Channel( i ) ) } }, createInputAndOutputLists( midiAccess ) { let optin = document.createElement( 'option' ) optin.text = 'none' let optout = document.createElement( 'option' ) optout.text = 'none' MIDI.midiInputList.add( optin ) MIDI.midiOutputList.add( optout ) MIDI.midiInputList.onchange = MIDI.selectInputViaGUI MIDI.midiOutputList.onchange = MIDI.selectOutputViaGUI const inputs = midiAccess.inputs for( let input of inputs.values() ) { const opt = document.createElement( 'option' ) opt.text = input.name opt.input = input MIDI.midiInputList.add( opt ) } const outputs = midiAccess.outputs for( let output of outputs.values() ) { const opt = document.createElement('option') opt.output = output opt.text = output.name MIDI.midiOutputList.add(opt) } }, selectInputViaGUI( e ) { if( e.target.selectedIndex !== 0 ) { // does not equal 'none' const opt = e.target[ e.target.selectedIndex ] const input = opt.input input.onmidimessage = MIDI.handleMsg input.open() MIDI.input = input localStorage.setItem( 'midi.input', input.name ) } }, selectOutputViaGUI( e ) { if( e.target.selectedIndex !== 0 ) { // does not equal 'none' const opt = e.target[ e.target.selectedIndex ] const output = opt.output output.open() MIDI.output = output localStorage.setItem( 'midi.output', output.name ) } }, selectInputByName( name ) { const inputs = MIDI.midiAccess.inputs let found = false for( let input of inputs.values() ) { if( name === input.name ) { input.onmidimessage = MIDI.handleMsg input.open() MIDI.input = input Gibber.Environment.log( 'MIDI input ' + name + ' opened.' ) found = true } } if( found === true ) { for( let i = 0; i < MIDI.midiInputList.children.length; i++ ) { if( name === MIDI.midiInputList.children[i].innerText ) { MIDI.midiInputList.selectedIndex = i } } } }, selectOutputByName( name ) { const outputs = MIDI.midiAccess.outputs let found = false for( let output of outputs.values() ) { if( name === output.name ) { output.onmidimessage = MIDI.handleMsg output.open() MIDI.output = output Gibber.Environment.log( 'MIDI output ' + name + ' opened.' ) found = true } } if( found === true ) { for( let i = 0; i < MIDI.midiOutputList.children.length; i++ ) { if( name === MIDI.midiOutputList.children[i].innerText ) { MIDI.midiOutputList.selectedIndex = i } } } }, send( msg, timestamp, isNoteMsg=false, duration=-1 ) { MIDI.output.send( msg, performance.now() + timestamp ) if( isNoteMsg === true ) { // send noteoff msg[0] -= 16 // change to noteoff msg MIDI.output.send( msg, timestamp + duration - 1 ) } }, beat:0, handleMsg( msg ) { //if( msg.data[0] !== 254 ) { console.log( 'midi message:', msg.data[0], msg.data[1] ) } if( Gibber.Scheduler.__sync__ === 'clock' ) { if( msg.data[0] === 0xf2 ) { // stop MIDI.timestamps.length = 0 MIDI.clockCount = 0 Gibber.Scheduler.currentBeat = -1 MIDI.lastClockTime = null } else if (msg.data[0] === 0xfa ) { // play MIDI.running = true } else if (msg.data[0] === 0xfc ) { // pause MIDI.running = false } else if( msg.data[0] === 248 && MIDI.running === true ) { // MIDI beat clock if( MIDI.timestamps.length > 0 ) { const diff = msg.timeStamp - MIDI.lastClockTime if( diff < 10 ) console.log( 'diff:', diff ) MIDI.timestamps.unshift( diff ) while( MIDI.timestamps.length > 20 ) MIDI.timestamps.pop() const sum = MIDI.timestamps.reduce( (a,b) => a+b ) const avg = sum / MIDI.timestamps.length let bpm = (1000 / (avg * 24)) * 60 Gibber.Scheduler.bpm = Math.round( Math.round( bpm * 100 ) / 100 ) if( MIDI.clockCount++ === 23 ) { Gibber.Scheduler.seq( MIDI.beat++ % 4 , 'clock' ) MIDI.clockCount = 0 } MIDI.lastClockTime = msg.timeStamp }else{ if( MIDI.lastClockTime !== null ) { const diff = msg.timeStamp - MIDI.lastClockTime MIDI.timestamps.unshift( diff ) MIDI.lastClockTime = msg.timeStamp }else{ MIDI.lastClockTime = msg.timeStamp Gibber.Scheduler.seq( MIDI.beat++ % 4, 'clock' ) } MIDI.clockCount++ } } } }, clear() { // This should only happen on a MIDI Stop message // this.timestamps.length = 0 // this.clockCount = 0 // this.lastClockTime = null }, running: false, timestamps:[], clockCount: 0, lastClockTime:null } module.exports = MIDI