rc-web@21: /* rc-web@0: //////////////////////////////////////////// rc-web@21: rc-web@21: nodescore server rc-web@21: nodescore.kiben.net rc-web@21: nodescore@kiben.net rc-web@21: rc-web@0: //////////////////////////////////////////// rc-web@21: */ rc-web@49: rc-web@0: var sio = require('socket.io') rc-web@0: , http = require('http') rc-web@69: , ch = require('./chronometer') rob@77: , osc = require('node-osc') rc@75: , fs = require('fs'); rc@75: //, static = require('node-static'); rc-web@49: rob@76: // start oscgroups client in a screen rob@76: //var sys = require('sys') rob@76: //var exec = require('child_process').exec; rob@76: //exec("./oscgroupsclient_start.sh"); rob@76: rob@77: var oscclient = new osc.Client('localhost', 22243); rob@77: function oscChron(unit,voice,counter){ rob@99: oscclient.send('/nodescore/voice_'+voice,unit,counter); rob@76: } rob@76: rc@73: var argu = process.argv.splice(2); rc@73: var port = argu[0] rc@73: var www = argu[1] rc@73: rc@73: console.log(www, port) rc@73: rc-web@54: var requirejs = require('requirejs'); rc-web@54: rc-web@54: requirejs.config({ rc-web@54: //Pass the top-level main.js/index.js require rc-web@54: //function to requirejs so that node modules rc-web@54: //are loaded relative to the top-level JS file. rc-web@54: nodeRequire: require, rc-web@54: findNestedDependencies: true rc-web@54: }); rc@73: rc-web@43: // run webserver serving static html rc-web@43: //////////////////////////////////////////// rc@73: var express = require("express"), rc@73: app = express(), rc@73: //bodyParser = require('body-parser') rc@73: errorHandler = require('errorhandler'), rc@73: methodOverride = require('method-override'), rc@73: port = parseInt(process.env.PORT, 10) || port; rc-web@65: rc@73: app.get("/", function (req, res) { rc@73: res.redirect("/index.html"); rc@73: }); rc-web@43: rc@73: app.use(methodOverride()); rc@73: //app.use(bodyParser()); rc@73: app.use(express.static(__dirname + '/' + www)); rc@73: app.use(errorHandler({ rc@73: dumpExceptions: true, rc@73: showStack: true rc@73: })); rc-web@43: rc@73: rc@73: // Create a Node.js based http server on port 8080 rc@73: var server = require('http').createServer(app).listen(port); rc-web@65: //////////////////////////////////////////// rc-web@65: rc-web@24: var pinging=0 rc-web@42: console.log("ping set to 0") rc-web@21: rc-web@54: //requirejs(['socketsstuff'],function(socketsstuff) {}); rc-web@0: //////////////////////////////////////////// rc-web@0: // connect to websockets rc-web@0: //////////////////////////////////////////// rc-web@0: rc@73: io = sio.listen(server) rc-web@0: , nicknames = {}; rc-web@0: rc-web@49: //var sequencer = require('./sequencer') rc-web@42: rob@91: //io.set('log level', 4); // reduce logging rc-web@0: io.sockets.on('connection', function (socket) { rc-web@0: rc-web@42: socket.on('nickname', function (nick, fn) { rc-web@42: if (nicknames[nick]) { rc-web@42: fn(true); rc-web@42: } else { rc-web@42: fn(false); rc-web@42: nicknames[nick] = socket.nickname = nick; rc-web@42: socket.broadcast.emit('announcement', nick + ' connected'); rc-web@42: io.sockets.emit('nicknames', nicknames); rc-web@42: } rc-web@42: }); rc-web@70: rc-web@42: /// chat user messages to screens and to log file rc-web@42: // date format for filename rc-web@42: var da = new Date(); var dtstring = da.getFullYear()+ '-' + da.getMonth()+ '-' + da.getDate(); rc-web@42: //////////////////////// rc-web@42: /// log messages to file rc-web@42: socket.on('user message', function (msg) { rc-web@42: fs.open('logs/chatlog-'+dtstring+'.txt', 'a', 666, function( e, id ) { rc-web@42: //time format for message stamp rc-web@42: var dt = new Date();var timestring = dt.getHours() + ':' + dt.getMinutes() + ':' + dt.getSeconds(); rc-web@70: socket.broadcast.emit('user message', socket.nickname, msg); rc-web@42: var fs = require('fs'), str = msg; rc-web@42: fs.write( id, timestring+" " + socket.nickname + ": "+ msg+"\n", null, 'utf8', function(){}); rc-web@42: }); rc-web@38: }); rc-web@70: rc-web@0: //////////////////////////////////////////// rc-web@54: // chronometer + sequencer controls (all this should be modularised) rc-web@0: //////////////////////////////////////////// rc-web@0: rob@91: sstate=0; rc-web@54: socket.on('stopWatch', function (state) { stopWatch(state);}); rob@91: socket.on('stopChr', function () { rob@91: //sstate=0; rob@91: stopChr(); rob@91: rob@91: }); rob@91: rob@91: socket.on('resetChr', function () { rob@91: //reset currently just restarts current unit rob@91: //todo: make a total reset that sets unit and transect to 0 rob@91: resetChr(); rob@91: }); rob@91: rob@91: socket.on('startSeq', function () { rob@91: //if( sstate==0 ){ rob@91: startChr(); rob@91: // sstate=1; rob@91: //} rob@91: }); rob@91: rc-web@54: var chronstate=0; rc-web@0: rc-web@54: // send the date/time every second rc-web@49: rc-web@49: xdatetime = setInterval(function () { rc-web@49: d = ch.xdateTime() rc-web@49: socket.broadcast.emit('dateTime', d) rc-web@54: socket.emit('dateTime', d) rc-web@49: }, 1000) rc-web@70: rc-web@70: ////////////////////////////// rc-web@70: // /* this should be moved to its own file chronometer.js rc-web@54: rc-web@70: function chronCtrl (state,interval){ rc-web@70: if (state==1){ console.log("chronControl....onestate") rc-web@70: var date = new Date(); var starttime = new Date().getTime() / 1000; rc-web@70: xstopwatch = setInterval(function () { rc-web@70: var nowtime = new Date().getTime() / 1000; rc-web@70: now = nowtime-starttime rc-web@70: hours = parseInt( now / 3600 ) % 24; rc-web@70: minutes = parseInt( now / 60 ) % 60; rc-web@70: seconds = parseInt(now % 60); rc-web@70: milliseconds = Math.floor((now-seconds)*10)%60; rc-web@70: time = rc-web@70: (hours < 10 ? "0" + hours : hours) + ":" + rc-web@70: (minutes < 10 ? "0" + minutes : minutes) + ":" + rc-web@70: (seconds < 10 ? "0" + seconds : seconds) + "."+ rc-web@70: milliseconds; rc-web@70: socket.broadcast.emit('chronFromServer', time) rc-web@70: socket.emit('chronFromServer', time) rob@77: oscclient.send('/nodescore/time',time); rc-web@70: }, 100) rob@77: rob@77: } rc-web@70: rc-web@70: if (state==0) { console.log("chronControl....zerostate") ; clearInterval(xstopwatch); } rc-web@70: rc-web@70: } rc-web@70: rc-web@70: // if not already started start the chronometer and sequencer rob@83: function startChr(socket) { rob@83: chronCtrl(1,100); rob@83: // these seq[A-D] arrays come from scoreB.js and then become the seq variable rob@83: step(seqA); rob@83: step(seqB); rob@83: step(seqC); rob@83: step(seqD); rob@83: } rob@83: rc-web@70: // stop the chronometer rc-web@70: function stopChr() { console.log("stop chron"); chronCtrl(0); } rc-web@54: rc-web@49: function pad(number) { return (number < 10 ? '0' : '') + number } rc-web@42: function resetChr() {//clearInterval(); rc-web@70: zecsec = 0; seconds = 0; mins = 0; hours = 0; rc-web@49: var chron = pad(hours) +":"+pad(mins)+ ':'+ pad(seconds)+ ":"+ zecsec rc-web@70: // send 0.00.00 values to display rc-web@49: socket.broadcast.emit('chronFromServer', chron) rc-web@49: socket.emit('chronFromServer', chron) rc-web@49: } rc-web@49: rc-web@0: //////////////////////////////////////////// rc-web@54: // magic square sequencer (this should be modularised) rc-web@0: //////////////////////////////////////////// rc-web@54: // all the variables this sequencer needs are in scoreB.js rc-web@54: requirejs(['scoreB'],function(scoreB) {}); rc-web@70: rob@94: rob@94: socket.on('setSpeed', function (s) { rob@94: rob@94: speed=s; rob@94: console.log("speed set: " + speed); rob@94: }); rob@94: rc-web@64: var sequencerState=0; rob@83: var numberoftransects=3; rob@83: var order=8; rc-web@64: var countdowntick=function(seq){ rc-web@64: var unit=seq.units[seq.transect%numberoftransects][seq.counter]; rc-web@64: var unitlast=seq.units[seq.transect%numberoftransects][seq.counter-1]; rc-web@64: var voice=seq.voice; rob@83: var tempoms = Math.floor(60000/seq.mm); rob@83: var timemultiplier=1000; rc-web@65: var outcount=4; var incount=4; rc-web@65: var dur=srcsqr[Math.floor(unit/order)][unit%order] + 4 rc-web@64: var time = dur; rc-web@64: var ztime=time; rc-web@64: var totaltime=time rob@83: rob@87: rc-web@70: initPage=function(seq){ rc-web@70: // initiate first page here rob@99: //var nextunit=seq.units[seq.transect%numberoftransects][(seq.counter+1)%order]; rob@99: //socket.emit("pageIni", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@99: //socket.broadcast.emit("pageIni", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@95: rob@99: //socket.emit("pageFlip", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@99: //socket.broadcast.emit("pageFlip", voice, unit, time, seq.mm,seq.counter,nextunit ); rc-web@70: } rc-web@63: rob@90: rob@94: rob@94: rc-web@59: function sequenCer() { rc-web@70: if (ztime >= 0 ){ rc-web@64: var counter = ztime rc-web@70: // flip the page rc-web@70: if (counter == 0){ rc-web@70: //increment the row position rc-web@70: seq.counter = (seq.counter + 1) % (order) rc-web@70: //increment the transect rc-web@70: if ( seq.counter==0 ){ seq.transect += 1 } rob@93: // this flips the page at the begining of each new sequence rob@93: // the similar pageIni function checks if there is and svg loaded rob@93: // so late joining clients and post page refresh views are up to date rob@93: rob@93: var nextunit=seq.units[seq.transect%numberoftransects][(seq.counter+1)%order]; rob@93: console.log("pageflip:" + voice, unit+1, time, seq.mm,seq.counter,nextunit) rob@93: socket.broadcast.emit("pageFlip", voice, unit+1, time, seq.mm,seq.counter,nextunit); rob@93: socket.emit("pageFlip", voice, unit+1, time, seq.mm,seq.counter,nextunit); rob@99: oscclient.send('/nodescore/voice_'+voice +"/unit/" ,unit,Math.floor(Math.random(0,5))); rc-web@70: clearInterval(pulse) rc-web@70: step(seq); rc-web@70: } rc-web@70: rc-web@59: if (counter >= 0 ){ rc-web@59: socket.broadcast.emit('counterText', rc-web@64: voice, unit, counter,seq.counter,unitlast,seq.transect%numberoftransects); rob@77: oscChron(unit,voice,counter); rc-web@59: socket.emit('counterText', rc-web@64: voice, unit, counter,seq.counter,unitlast,seq.transect%numberoftransects); rc-web@59: rc-web@59: if (counter <= outcount ) { rc-web@59: socket.broadcast.emit('countinFromServer', rc-web@60: voice, counter, rc-web@60: "","stop in: ", "red", "transparent",unit); rc-web@59: socket.emit('countinFromServer', rc-web@60: voice, counter, rc-web@60: "","stop in: ", "red", "transparent",unit); rc-web@59: } rc-web@59: rc-web@59: if (counter > (totaltime)-incount && counter <= totaltime ) { rc-web@59: socket.broadcast.emit('countinFromServer', rc-web@60: voice, counter-(totaltime-incount), rc-web@60: "","play in: ", "green","transparent",unit); rc-web@59: socket.emit('countinFromServer', rc-web@60: voice,counter-(totaltime-incount), rc-web@60: "","play in: ", "green","transparent",unit); rc-web@59: } rc-web@59: rc-web@59: if (counter == (totaltime)-incount ) { rc-web@59: socket.broadcast.emit('countinFromServer', rc-web@60: voice, "+", rc-web@60: "","playing.. ", "green","transparent",unit); rc-web@59: socket.emit('countinFromServer', rc-web@60: voice, "+", rc-web@60: "","playing.. ", "green","transparent",unit); rc-web@59: } rob@87: } rob@91: rc-web@63: // on each beat do: rc-web@59: // push out the pulse to metronome rob@91: rc-web@59: seq.metrobeat = (seq.metrobeat+1)%seq.beatsinbar ; rob@91: socket.broadcast.emit('metroPulse', tempoms, voice, seq.metrobeat); rc-web@64: socket.emit('metroPulse', tempoms, voice, seq.metrobeat); rob@93: var nextunit=seq.units[seq.transect%numberoftransects][(seq.counter+1)%order]; rob@87: socket.broadcast.emit("pageIni", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@93: socket.emit("pageIni", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@93: //socket.broadcast.emit("pageFlip", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@93: //socket.emit("pageFlip", voice, unit, time, seq.mm,seq.counter,nextunit ); rc-web@59: } rob@91: rc-web@64: // decrement the time rc-web@64: ztime -= 1 rob@83: // this shows undefined counter output - bug related rob@83: // console.log(counter) rc-web@59: } rc-web@59: rob@93: //trigger a page refresh when the solo voice is changed rob@93: rob@94: rob@94: rob@93: socket.on('changeSoloVoice', function () { rob@93: var nextunit=seq.units[seq.transect%numberoftransects][(seq.counter+1)%order]; rob@93: socket.broadcast.emit("pageFlip", voice, unit, time, seq.mm,seq.counter,nextunit ); rob@94: socket.emit("pageFlip", voice, unit, time, seq.mm ,seq.counter,nextunit ); rob@93: }); rob@93: rob@93: rc-web@65: var pulse = setInterval(sequenCer, tempoms); rob@93: socket.on('breakSeq', function () { rob@93: //this should break the code which will auto-restart under node-supervisor rob@93: //aka hard reset rob@93: someAbsentFunction(breakmenow); rob@93: }); rob@93: rc-web@59: socket.on('stopSeq', function () { rc-web@59: sequenCer.clearInterval rc-web@59: console.log("sequencer stopping...") rob@91: rc-web@59: // grrr why wont this clearInterval work rob@91: rc-web@59: sequencerState = 0 rc-web@59: clearInterval(pulse) rob@91: rob@91: // the below breaks things but maybe this is for the best rob@91: // without breakage and nodejs supervisor restart then too many instances rob@91: // mount up and slow thinks down until crash rob@91: //sequenCer.clearInterval(pulse) rob@91: rob@91: rc-web@70: stopChr(); rc-web@59: }); rc-web@59: }; rc-web@59: rc-web@39: step = function (seq) { rc-web@54: clearInterval(countdowntick); rc-web@39: countdowntick(seq) rc-web@39: sequencerState=1; rc-web@70: initPage(seq) rc-web@42: }; rc-web@59: rob@90: socket.on('resetSeq', function () { rob@91: // start stop reset in sucession to setup page easy never mind the below rob@90: //var unit=seq.units[seq.transect%numberoftransects][seq.counter]; rob@90: resetChr(); rob@90: //socket.emit("pageIni", 1, unit, 0, 60, 0,1 ) rob@90: //socket.emit("pageIni", 2, 1, 0, 60, 0,1 ); rob@90: //socket.emit("pageIni", 3, 1, 0, 60, 0,1 ); rob@90: //socket.emit("pageIni", 4, 1, 0, 60, 0,1 ); rob@90: }); rc-web@31: rc-web@24: //////////////////////////////////////////// rc-web@24: // some latency calculations rc-web@24: /////////////////////////////////////////// rc-web@42: rc-web@24: /* rc-web@24: a ping is periodically broadcast to all connected clients each rc-web@24: connected returns a pong to the server via an "emit" and in turn rc-web@24: the server returns each unique client a report of the latency rc-web@24: via another emit - the emit only sends to the source of the rc-web@24: request, whereas the broadcast.emit.. broadcasts.. ie to all rc-web@24: connected clients rc-web@42: rc-web@24: TODO: smooth range and average out results to remove erratic ping rc-web@24: times. rc-web@24: rc-web@24: TODO: rc-web@24: The result then needs to be used to stagger outgoing messages to rc-web@24: compensate for latency - how much compensation is more connected rc-web@24: to the time that any audio/video feed needs to encode/decode as rc-web@24: the latency of the route from node A to node B is inavoidable?! rc-web@24: so maybe latency is irrelevant in this context - we just need to rc-web@24: stagger signals according to encoding decoding times.. hmmm rc-web@24: */ rc-web@24: rc-web@24: rc-web@43: // periodically broadcast a ping rc-web@24: function serverTime(freq) { rc-web@43: if (pinging==0){ st = setInterval(function() { rc-web@43: var pinging=1; rc-web@43: var d = new Date(); var n = d.getTime(); rc-web@43: socket.emit("timeFromServer", n); rc-web@43: }, 1000); } else console.log("already pinging") rc-web@24: } rc-web@42: // receive the pong calculate the latency and rc-web@24: // return the response to the client rc-web@43: rc-web@24: socket.on("clientTimeResponse", function(x) { rc-web@24: var d = new Date(); var n = d.getTime(); rc-web@24: var latency = (n-x)/2; rc-web@24: //console.log("SERVERTIME:"+x + " LATENCY:" + latency); rc-web@24: socket.emit("latencyFromServer", latency); rc-web@24: }); rc-web@42: rc-web@24: serverTime(1000); rc-web@42: rc-web@42: socket.on('disconnect', function(client) { rc-web@38: console.log(socket.nickname + " is gone..." ) rc-web@24: clearInterval(st); rc-web@42: rc-web@42: if (!socket.nickname) return; rc-web@42: rc-web@42: delete nicknames[socket.nickname]; rc-web@42: socket.broadcast.emit('announcement', socket.nickname + ' disconnected'); rc-web@42: socket.broadcast.emit('nicknames', nicknames); rc-web@24: }); rc-web@38: rc-web@70: }); rc-web@49: rc-web@54: exports.socket= io.sockets; rc@73: exports.httpServer = server;