gibberwocky
Version:
music sequencing for Live and Max/MSP
296 lines (230 loc) • 8.22 kB
JavaScript
let Gibber = null
let Communication = {
livePort: 8082,
maxPort: 8081,
maxIP: '127.0.0.1',
liveIP: '127.0.0.1',
socketInitialized: false,
connectMsg: null,
defaultMode: 'live',
debug: {
input:false,
output:false
},
liveSocket:null,
maxSocket: null,
callbacks: {
schemas: {
live:null,
max: null
}
},
connected:{
max:false,
live:false
},
count:0,
init( _Gibber, props ) {
Gibber = _Gibber
Object.assign( this, props )
this.maxSocket = this.createWebSocket( Gibber.Max.init, this.maxPort, this.maxIP, 'max' )
this.liveSocket = this.createWebSocket( Gibber.Live.init, this.livePort, this.liveIP, 'live' )
this.send = this.send.bind( Communication )
},
connect( props ) {
if( this.maxSocket !== undefined && this.maxSocket.readyState === 1 ) this.maxSocket.close()
if( this.liveSocket !== undefined && this.liveSocket.readyState === 1 ) this.liveSocket.close()
Object.assign( this, props )
this.maxSocket = this.createWebSocket( Gibber.Max.init, this.maxPort, this.maxIP, 'max' )
this.liveSocket = this.createWebSocket( Gibber.Live.init, this.livePort, this.liveIP, 'live' )
},
createWebSocket( init, port, host, clientName ) {
if ( this.connected[ clientName ] === true ) return
let wsocket = null
if ( 'WebSocket' in window ) {
//Gibber.log( 'Connecting' , this.querystring.host, this.querystring.port )
if( Gibber.isStandalone === true ) {
if( this.connectMsg === null ) {
this.connectMsg = Gibber.log( 'connecting' )
}else{
this.connectMsg.innerText += '.'
}
}
const address = "ws://" + host + ":" + port
wsocket = new WebSocket( address )
wsocket.errorCount = 0
wsocket.onopen = function(ev) {
//Gibber.log( 'CONNECTED to ' + address )
Gibber.log( `gibberwocky.${clientName} is ready to burble.` )
this.connected[ clientName ] = true
Communication[ clientName + 'Socket' ] = wsocket
init()
// cancel the auto-reconnect task:
if ( this.connectTask !== undefined ) clearTimeout( this.connectTask )
// apparently this first reply is necessary
wsocket.send( 'update on' )
if( Gibber.isStandalone )
Gibber.Environment.setServer( clientName )
}.bind( Communication )
wsocket.onclose = function(ev) {
if( this.connected[ clientName ] ) {
Gibber.log( 'disconnected from ' + address )
this.connectMsg = null
this.connected[ clientName ] = false
}
// set up an auto-reconnect task:
//if( wsocket.errorCount++ < 3 ) {
// this.connectTask = setTimeout( this.createWebSocket.bind(
// Communication,
// clientName === 'live' ? Gibber.Live.init : Gibber.Max.init,
// port, host, clientName )
// , 2000 )
//}else{
// Gibber.log( `too many failed connection attempts to ${clientName}. no more connections will be attempted. refresh the page to try again.\n\n` )
//}
}.bind( Communication )
wsocket.onmessage = function( ev ) {
//Gibber.log('msg:', ev )
this.handleMessage( ev, wsocket )
}.bind( Communication )
wsocket.onerror = function( ev ) {
Gibber.log( `gibberwocky.${clientName} was not able to connect.` )
if( wsocket.errorCount++ > 3 ) {
wsocket.close()
Gibber.log( `too many failed connection attempts to ${clientName}. no more connections will be attempted. refresh the page to try again.\n\n` )
}
}.bind( Communication )
wsocket.clientName = clientName
} else {
post( 'WebSockets are not available in this browser!!!' );
}
return wsocket
},
handleMessage( _msg, socket ) {
let id, key, data, msg, isLiveMsg=true
if( _msg.data.charAt( 0 ) === '{' ) {
data = _msg.data
key = null
const json = JSON.parse( data )
const schema = json.signals !== undefined ? 'max' : 'live'
if( Communication.callbacks.schemas[ schema ] ) {
Communication.callbacks.schemas[ schema ]( JSON.parse( data ) )
}
}else if( _msg.data.includes( 'snapshot' ) ) {
if( Gibber.isStandalone === true ) {
data = _msg.data.substr( 9 ).split(' ')
// if we're not using genish.js for modulation...
if( Gibber.__gen.enabled !== false ) {
for ( let i = 0; i < data.length; i += 2 ) {
let param_id = data[ i ]
let param_value = data[ i+1 ]
if( socket === Communication.liveSocket ) {
if( param_value < 0 ) {
param_value = 0
}else if( param_value > 1 ) {
param_value = 1
}
}
Gibber.Environment.codeMarkup.waveform.updateWidget( param_id, 1 - param_value )
}
}
}
return
}else{
msg = _msg.data.split( ' ' )
isLiveMsg = msg.length > 2
if( isLiveMsg ) {
id = msg[ 0 ]
key = msg[ 1 ]
data = msg.slice( 2 )
if( key === 'err' ) data = data.join(' ')
}else{
id = 0
key = msg[ 0 ]
if( key === 'err' ) {
data = msg.slice( 1 ).join( ' ' )
}else{
data = msg[ 1 ]
}
}
}
if( id === undefined && isLiveMsg === true ) {
//console.error( data )
return
}
if( Communication.debug.input ) {
if( id !== undefined ) {
Gibber.log( 'debug.input:', id, key, data )
}else{
Gibber.log( 'debug.input (obj):', JSON.parse( data ) )
}
}
switch( key ) {
case 'seq' :
if( data === undefined ) {
console.log( 'faulty ws seq message', _msg.data )
}else{
const from = socket === Communication.liveSocket ? 'live' : 'max'
Gibber.Scheduler.seq( data, socket.clientName );
}
break;
case 'ply' :
Gibber.publish( 'play' )
break;
case 'clr' :
Gibber.Environment.clearConsole()
break;
case 'bpm' :
Gibber.Scheduler.bpm = parseFloat( data )
break;
case 'err':
Gibber.Environment.error( data )
break;
case 'cli':
console.log( 'client!!!', data )
break;
case 'run':
Gibber.clear()
Gibber.Environment.codemirror.setValue( data )
Gibber.Environment.codemirror.execCommand( 'selectAll' )
Gibber.Environment.keymap[ 'Ctrl-Enter' ]( Gibber.Environment.codemirror )
break;
case 'set':
Gibber.Environment.codemirror.setValue( data )
break;
case 'end':
Gibber.clear()
break;
case 'add':
const lastLine = Gibber.Environment.codemirror.lastLine()
const current = Gibber.Environment.codemirror.getValue( '\n' )
Gibber.Environment.codemirror.setValue( current + '\n' + data )
const newLastLine = Gibber.Environment.codemirror.lastLine()
Gibber.Environment.codemirror.setSelection({ line:lastLine+1, ch:0 }, {line:newLastLine, ch:10000 })
Gibber.Environment.keymap[ 'Ctrl-Enter' ]( Gibber.Environment.codemirror )
break;
default:
break;
}
},
send( code, to='live' ) {
if( Communication[ to + 'Socket' ].readyState === 1 ) {
//if( code === true ) debugger
if( Communication.debug.output ) Gibber.log( 'beat:', Gibber.Scheduler.currentBeat, 'msg:', code )
const socket = to === 'live' ? Communication.liveSocket : Communication.maxSocket
socket.send( code )
}else{
Gibber.log( `socket ${to} is not ready for messaging.` )
}
},
querystring : null,
}
let qstr = window.location.search,
query = {},
a = qstr.substr( 1 ).split( '&' )
for ( let i = 0; i < a.length; i++ ) {
let b = a[ i ].split( '=' )
query[ decodeURIComponent( b[0]) ] = decodeURIComponent( b[1] || '' )
}
Communication.querystring = query
module.exports = Communication