client/annotviz/app/js/doubleroll.js
author ymh <ymh.work@gmail.com>
Mon, 19 Jan 2015 13:31:28 +0100
changeset 95 806739a26858
parent 94 e0e514c5470f
child 98 72d767c5142d
permissions -rw-r--r--
add vertical version for pianoroll

/**
* scripts/doubleroll.js
*
* This is the starting point for your application.
* Take a look at http://browserify.org/ for more info
*/

/* global window: false */
/* global document: false */
/* global WebSocket: false */
/* global MozWebSocket: false */

'use strict';


var PIXI = require('pixi');
var _ = require('lodash');
var PianoRoll = require('./pianoroll.js');

var NTP_EPOCH_DELTA = 2208988800; //c.f. RFC 868

var defaultConfig = {
    orientation: 'horizontal',
    logger: false,
    sceneWidth: 1920,
    pianorolls : [
      {
        height: 435,
        timeWidth: 10,
        lineInterval: 5000,
        noteHeight: undefined
      },
      {
        height: 645,
        timeWidth: 60,
        lineInterval: 5000,
        noteHeight: undefined
      },
    ],
    framerate: 25,
    offsetMusic: false,
    sceneBgColor: 0xFFFFFF,
    lineColor: 0x444444,
    lineFillColor: 0xFFFF00,
    noteColors: [0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991],
    canvasContainer: 'canvasContainer',
    logContainer: 'log',
    timeContainer: 'timeStarted',
    noteHeight: undefined,
    zeroShift: 0.9,
    timeWidth: 60,
    lineInterval: 5000,
    annotationChannel: 'PIANOROLL'
//    wsUri: undefined,
//    eventCode: undefined

};

function DoubleRoll(options) {

    var _this = this;
    var opts = _(options).defaults(defaultConfig).value();

    var orientation = opts.orientation;
    var isHorizontal = (orientation !== 'vertical');

    this.logger = opts.logger;
    this.lineColor = opts.lineColor;
    this.lineFillColor = opts.lineFillColor;
    this.framerate = opts.framerate;
    this.offsetMusic = opts.offsetMusic;
    this.noteColors = opts.noteColors;

    var noteHeight = opts.noteHeight;
    var sceneBgColor = opts.sceneBgColor;
    var sceneHeight = opts.sceneHeight || _(opts.pianorolls).reduce(function(s,p) { return s + p.height; }, 0);
    var timeWidth = opts.timeWidth;
    var lineInterval = opts.lineInterval;
    var offsetMusic = opts.offsetMusic;

    var sceneWidth = opts.sceneWidth;
    var canvasContainer = opts.canvasContainer;
    var logContainer = opts.logContainer;
    var timeContainer = opts.timeContainer;

    var zeroShift = opts.zeroShift;

    var eventCode = opts.eventCode;
    var annotationChannel = opts.annotationChannel;
    var wsUri = opts.wsUri;
    if(!wsUri) {
        if (window.location.protocol === 'file:') {
            wsUri = 'ws://127.0.0.1:8090/broadcast';
        }
        else {
            wsUri = 'ws://' + window.location.hostname + ':8090/broadcast';
        }
        wsUri += '?channel='+annotationChannel+'&event_code='+eventCode;
    }


    var colorsReg = {};

    //create an new instance of a pixi stage
    var stage = new PIXI.Stage(sceneBgColor);
    //create a renderer instance.
    var renderer = PIXI.autoDetectRenderer(sceneWidth, sceneHeight);

    var uberContainer = new PIXI.DisplayObjectContainer();
    uberContainer.x = Math.floor(sceneWidth*zeroShift);
    uberContainer.y = 0;
    stage.addChild(uberContainer);

    var pianorollList = [];

    var pianorollOptions = {
        parentContainer: uberContainer,
        orientation: orientation,
        xInit: 0,
        width: sceneWidth,
        noteColors: this.noteColors,
        colorsReg: colorsReg,
        lineColor: this.lineColor,
        lineInterval: lineInterval,
        offsetMusic: offsetMusic,
    };

    var yInit = 0;
    var linesDown = true;
    _(opts.pianorolls).forEach(function(prDef, i) {
        var prNoteHeight = noteHeight || prDef.noteHeight || prDef.height / 128;
        var prTimeWidth = prDef.timeWidth || timeWidth;
        pianorollList.push(new PianoRoll(_({
            yInit: yInit,
            height: prDef.height,
            linesDown: linesDown,
            pixelsPerSecond: Math.floor(sceneWidth / prTimeWidth),
            noteHeight: prNoteHeight,
            lineInterval: prDef.lineInterval
        }).defaults(pianorollOptions).value()));
        yInit += prDef.height;
        linesDown = !linesDown;

        if(i<(opts.pianorolls.length-1)) {
            var lineGraphics = new PIXI.Graphics()
                .beginFill(_this.lineFillColor)
                .lineStyle(1, _this.lineColor)
                .moveTo(Math.floor(sceneWidth*zeroShift), yInit)
                .lineTo(-sceneWidth - Math.floor(sceneWidth*zeroShift), yInit)
                .endFill();
            uberContainer.addChild(lineGraphics);
        }
    });

    if(!isHorizontal) {
        uberContainer.rotation = Math.PI/2;
        uberContainer.y = sceneHeight;
        uberContainer.x = sceneWidth;
    }


    this.init = function() {

        if(typeof(canvasContainer) === 'string') {
            canvasContainer = document.getElementById(canvasContainer);
        }
        if(typeof(logContainer) === 'string') {
            logContainer = document.getElementById(logContainer);
        }
        if(typeof(timeContainer) === 'string') {
            timeContainer = document.getElementById(timeContainer);
        }


        if(!this.logger){
            document.body.removeChild(logContainer);
            logContainer = undefined;
        }
        var sock;

        canvasContainer.appendChild(renderer.view);

        if ('WebSocket' in window) {
            sock = new WebSocket(wsUri);
        } else if ('MozWebSocket' in window) {
            sock = new MozWebSocket(wsUri);
        } else {
            this.log('Browser does not support WebSocket!');
            window.location = 'http://autobahn.ws/unsupportedbrowser';
        }

        if (!sock) {
            return;
        }
        sock.onopen = function(){
            if(_this.logger){
                _this.log('Connected to ' + _this.wsUri);
            }
        };

        sock.onclose = function(e) {
            if(_this.logger){
                _this.log('Connection closed (wasClean = ' + e.wasClean + ', code = ' + e.code + ', reason = \'' + e.reason + '\')');
            }
            sock = null;
        };

        sock.onmessage = function(e) {
            var dataJson = JSON.parse(e.data);
            if(_this.logger){
                var dataDate = new Date((dataJson.content[0]-NTP_EPOCH_DELTA)*1000);
                _this.log('Got message: ' + e.data + ' - ' + dataDate.toISOString());
            }
            _this.addNotes(dataJson);
        };

    };


    this.addNotes = function(data) {
        var note = data.content[3];
        var velocity = data.content[4];
        var ts = (data.content[0] - NTP_EPOCH_DELTA)*1000;
        var channel = data.content[2];
        var sessionTs = data.content[1];

        pianorollList.forEach(function(c) {
            c.addNote(note, ts, sessionTs, velocity, channel, 0);
        });
    };

    this.refreshStage = function() {
        pianorollList.forEach(function(c) {
            c.move();
        });
        renderer.render(stage);
    };

    // Init page and intervals
    var refreshInterval;
    var refreshTimeInterval;
    var startTs;

    this.updateTime = function(){
        var nbSec = (Date.now() - startTs) / 1000;
        var hours = Math.floor( nbSec / 3600 ) % 24;
        var minutes = Math.floor( nbSec / 60 ) % 60;
        var seconds = Math.floor(nbSec % 60);
        var timeStr = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds  < 10 ? '0' + seconds : seconds);
        timeContainer.innerHTML = timeStr;
    };

    this.start = function() {

        startTs = Date.now();
        refreshInterval = window.setInterval(function() {_this.refreshStage();}, 1000/this.framerate);
        refreshTimeInterval = window.setInterval(function() {_this.updateTime();}, 1000);
        pianorollList.forEach(function(c) {
            c.start();
        });
    };

    this.stop = function() {
        window.clearInterval(refreshInterval);
        window.clearInterval(refreshTimeInterval);
        pianorollList.forEach(function(c) {
            c.stop();
        });
    };


    this.log = function(m) {
        if(this.logger){
            this.logContainer.innerHTML += m + '\n';
            this.logContainer.scrollTop = logContainer.scrollHeight;
        }
    };


    window.onload = function() {
        _this.init();
        _this.start();
    };

    return this;
}

module.exports = {
    DoubleRoll: DoubleRoll
};