/**
* js/pianoroll.js
*
* pianoroll basic component
*
*/
/* global window: false */
'use strict';
var PIXI = require('pixi');
var randomColor = require('randomColor');
var _ = require('lodash');
function PianoRoll(options) {
var _this = this;
this.container = new PIXI.DisplayObjectContainer();
this.container.position.x = options.xInit;
this.container.position.y = options.yInit;
options.parentContainer.addChild(this.container);
this.linesDown = options.linesDown;
this.height = options.height;
this.pixelsPerSecond = options.pixelsPerSecond;
this.width = options.width;
this.noteColors = options.noteColors;
this.colorsReg = options.colorsReg || {};
this.lineColor = options.lineColor;
this.lineInterval = options.lineInterval;
this.offsetMusic = options.offsetMusic || false;
this.noteHeight = options.noteHeight;
this.noteDict = {};
this.startTs = options.startTs || Date.now();
//TODO: I do not like the "regColor" object. This should not be global, but local
this.getColor = function(canal) {
var color = this.colorsReg[canal];
if(typeof(color) === 'undefined') {
var colorsRegSize = Object.keys(this.colorsReg).length;
if(colorsRegSize < this.noteColors.length) {
color = this.colorsReg[canal] = this.noteColors[colorsRegSize];
}
else {
color = this.colorsReg[canal] = parseInt(randomColor({ luminosity: 'light', hue: 'random', format:'hex'}).replace(/^#/, ''), 16);
}
}
return color;
};
this.getNoteRect = function(x, y, color, alpha, width, height) {
var graphics = new PIXI.Graphics();
//console.log("beginX = ", beginX, "canal = ", canal, "color = ", noteColor[canal], "width = ", width, "note = ", note, "velocity = ", velocity);
graphics.beginFill(color, alpha);
graphics.drawRect(0, 0, width, height);
graphics.endFill();
graphics.x = x;
graphics.y = y;
graphics.width = width;
graphics.height = height;
return graphics;
};
this.addNote = function(note, startTime, sessionTs, velocity, channel, duration) {
var ts = startTime;
if(this.offsetMusic) {
ts = this.startTs + sessionTs;
}
var noteDuration = duration;
var noteVelocity = velocity;
var graphics;
if(!duration) {
if(typeof this.noteDict[channel]==='undefined'){
this.noteDict[channel] = {};
}
if(velocity===0) {
if(typeof this.noteDict[channel][note] !== 'undefined') {
var noteDef = this.noteDict[channel][note];
delete this.noteDict[channel][note];
noteDuration = sessionTs - noteDef.sessionTs;
graphics = noteDef.graphics;
noteVelocity = noteDef.velocity;
ts = noteDef.ts;
}
}
else {
noteDuration = Date.now() - ts;
this.noteDict[channel][note] = { ts: ts, velocity: velocity, sessionTs: sessionTs};
}
}
if(!this.offsetMusic || velocity===0) {
var width = noteDuration * this.pixelsPerSecond / 1000;
if(!graphics) {
var x = (ts-this.startTs) * this.pixelsPerSecond / 1000;
if((x+width) < (Math.abs(this.container.x) - this.width)) {
// not visible. do nothing
return;
}
var y = Math.floor((128-note+0.5) * this.height / 128 - (this.noteHeight/2));
var color = this.getColor(channel);
var alpha = (noteVelocity / 128);
graphics = this.getNoteRect(x, y, color, alpha, width, this.noteHeight);
this.container.addChild(graphics);
//console.log(color, alpha, graphics.lineColor, graphics.fillAlpha);
}
else {
graphics.width = width;
}
if(!duration && velocity) {
this.noteDict[channel][note].graphics = graphics;
}
}
};
this.addLine = function(ts){
if(typeof(ts) === 'undefined') {
ts = new Date();
}
var graphics = new PIXI.Graphics();
var x = -this.container.x;
graphics.beginFill(0xFFFF00);
graphics.lineStyle(1, this.lineColor);
var y = this.linesDown ? this.height - 20 : 0;
graphics.moveTo(x, y);
graphics.lineTo(x, y + 20);
graphics.endFill();
this.container.addChild(graphics);
// Add text
//var totalSec = lineNb * this.lineInterval / 1000;
var hours = ts.getHours();
var minutes =ts.getMinutes();
var seconds = ts.getSeconds();
var timeStr = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
var fontObj = { font: '10pt Arial', fill: '#444444' };
var t = new PIXI.Text(timeStr, fontObj);
t.x = x + 2;
t.y = this.linesDown ? this.height - 15 : 2;
this.container.addChild(t);
};
this.moveTo = function(diffTime){
var oldX = this.container.x;
this.container.x = Math.floor(diffTime*this.pixelsPerSecond);
var deltaX = Math.abs(oldX-this.container.x);
_.forOwn(this.noteDict, function(channelDict) {
_.forOwn(channelDict, function(noteDef) {
if(noteDef.graphics) {
noteDef.graphics.width = noteDef.graphics.width + deltaX;
}
});
});
};
this.move = function() {
var diff = (this.startTs - Date.now())/1000;
this.moveTo(diff);
};
this.removePassedObjets = function(){
var nbChilds = _this.container.children.length;
var i = 0, childIsNowDisplayed = false, childrenToRemove = [];
while(i<nbChilds && !childIsNowDisplayed){
var child = _this.container.children[i++];
//console.log("remove ? ", child.x, child.width, ((child.x + child.width) < (Math.abs(_this.container.x) - _this.width)));
if(typeof(child) === 'undefined') {
continue;
}
if((child.x + child.width) < (Math.abs(_this.container.x) - _this.width)){
childrenToRemove.push(child);
//console.log(" remove !!!");
}
else {
childIsNowDisplayed = true;
//console.log(" childIsNowDisplayed");
}
}
childrenToRemove.forEach(function(child) {
_this.container.removeChild(child);
});
//console.log("before : ", nbChilds, ", after : ", _this.container.children.length);
};
this.start = function() {
if(typeof(this.startTs) === 'undefined') {
console.log('Container Started');
this.startTs = Date.now();
this.addLine();
}
var _this = this;
this.verticalLinesInterval = window.setInterval(function() { _this.addLine(); }, this.lineInterval);
this.cleanInterval = window.setInterval(function () { _this.removePassedObjets(); }, 1000 * this.width / this.pixelsPerSecond );
};
this.stop = function() {
//window.clearInterval(this.moveInterval);
window.clearInterval(this.verticalLinesInterval);
window.clearInterval(this.cleanInterval);
};
}
module.exports = PianoRoll;