|
1 /** |
|
2 * scripts/doubleroll.js |
|
3 * |
|
4 * This is the starting point for your application. |
|
5 * Take a look at http://browserify.org/ for more info |
|
6 */ |
|
7 |
|
8 /* global window: false */ |
|
9 /* global document: false */ |
|
10 /* global WebSocket: false */ |
|
11 /* global MozWebSocket: false */ |
|
12 |
|
13 'use strict'; |
|
14 |
|
15 |
|
16 var PIXI = require('pixi'); |
|
17 var _ = require('lodash'); |
|
18 var PianoRoll = require('./pianoroll.js'); |
|
19 |
|
20 var NTP_EPOCH_DELTA = 2208988800; //c.f. RFC 868 |
|
21 |
|
22 var defaultConfig = { |
|
23 logger: false, |
|
24 sceneWidth: 1920, |
|
25 pianorolls : [ |
|
26 { |
|
27 height: 435, |
|
28 timeWidth: 10, |
|
29 lineInterval: 5000, |
|
30 noteHeight: undefined |
|
31 }, |
|
32 { |
|
33 height: 645, |
|
34 timeWidth: 60, |
|
35 lineInterval: 5000, |
|
36 noteHeight: undefined |
|
37 }, |
|
38 ], |
|
39 framerate: 25, |
|
40 offsetMusic: false, |
|
41 sceneBgColor: 0xFFFFFF, |
|
42 lineColor: 0x444444, |
|
43 lineFillColor: 0xFFFF00, |
|
44 noteColors: [0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991], |
|
45 canvasContainer: 'canvasContainer', |
|
46 logContainer: 'log', |
|
47 timeContainer: 'timeStarted', |
|
48 noteHeight: undefined, |
|
49 zeroShift: 0.9, |
|
50 timeWidth: 60, |
|
51 lineInterval: 5000, |
|
52 annotationChannel: 'PIANOROLL' |
|
53 // wsUri: undefined, |
|
54 // eventCode: undefined |
|
55 |
|
56 }; |
|
57 |
|
58 function DoubleRoll(options) { |
|
59 |
|
60 var _this = this; |
|
61 var opts = _(options).defaults(defaultConfig).value(); |
|
62 |
|
63 this.logger = opts.logger; |
|
64 this.lineColor = opts.lineColor; |
|
65 this.lineFillColor = opts.lineFillColor; |
|
66 this.framerate = opts.framerate; |
|
67 this.offsetMusic = opts.offsetMusic; |
|
68 this.noteColors = opts.noteColors; |
|
69 |
|
70 var noteHeight = opts.noteHeight; |
|
71 var sceneBgColor = opts.sceneBgColor; |
|
72 var sceneHeight = opts.sceneHeight || _(opts.pianorolls).reduce(function(s,p) { return s + p.height; }, 0); |
|
73 var timeWidth = opts.timeWidth; |
|
74 var lineInterval = opts.lineInterval; |
|
75 var offsetMusic = opts.offsetMusic; |
|
76 |
|
77 var sceneWidth = opts.sceneWidth; |
|
78 var canvasContainer = opts.canvasContainer; |
|
79 var logContainer = opts.logContainer; |
|
80 var timeContainer = opts.timeContainer; |
|
81 |
|
82 var zeroShift = opts.zeroShift; |
|
83 |
|
84 var eventCode = opts.eventCode; |
|
85 var annotationChannel = opts.annotationChannel; |
|
86 var wsUri = opts.wsUri; |
|
87 if(!wsUri) { |
|
88 if (window.location.protocol === 'file:') { |
|
89 wsUri = 'ws://127.0.0.1:8090/broadcast'; |
|
90 } |
|
91 else { |
|
92 wsUri = 'ws://' + window.location.hostname + ':8090/broadcast'; |
|
93 } |
|
94 wsUri += '?channel='+annotationChannel+'&event_code='+eventCode; |
|
95 } |
|
96 |
|
97 |
|
98 var colorsReg = {}; |
|
99 |
|
100 //create an new instance of a pixi stage |
|
101 var stage = new PIXI.Stage(sceneBgColor); |
|
102 //create a renderer instance. |
|
103 var renderer = PIXI.autoDetectRenderer(sceneWidth, sceneHeight); |
|
104 |
|
105 var uberContainer = new PIXI.DisplayObjectContainer(); |
|
106 uberContainer.x = Math.floor(sceneWidth*zeroShift); |
|
107 uberContainer.y = 0; |
|
108 stage.addChild(uberContainer); |
|
109 |
|
110 var pianorollList = []; |
|
111 |
|
112 var pianorollOptions = { |
|
113 parentContainer: uberContainer, |
|
114 xInit: 0, |
|
115 width: sceneWidth, |
|
116 noteColors: this.noteColors, |
|
117 colorsReg: colorsReg, |
|
118 lineColor: this.lineColor, |
|
119 lineInterval: lineInterval, |
|
120 offsetMusic: offsetMusic, |
|
121 }; |
|
122 |
|
123 var yInit = 0; |
|
124 var linesDown = true; |
|
125 _(opts.pianorolls).forEach(function(prDef, i) { |
|
126 var prNoteHeight = noteHeight || prDef.noteHeight || prDef.height / 128; |
|
127 var prTimeWidth = prDef.timeWidth || timeWidth; |
|
128 pianorollList.push(new PianoRoll(_({ |
|
129 yInit: yInit, |
|
130 height: prDef.height, |
|
131 linesDown: linesDown, |
|
132 pixelsPerSecond: Math.floor(sceneWidth / prTimeWidth), |
|
133 noteHeight: prNoteHeight, |
|
134 lineInterval: prDef.lineInterval |
|
135 }).defaults(pianorollOptions).value())); |
|
136 yInit += prDef.height; |
|
137 linesDown = !linesDown; |
|
138 |
|
139 if(i<(opts.pianorolls.length-1)) { |
|
140 var lineGraphics = new PIXI.Graphics() |
|
141 .beginFill(_this.lineFillColor) |
|
142 .lineStyle(1, _this.lineColor) |
|
143 .moveTo(0, yInit) |
|
144 .lineTo(sceneWidth, yInit) |
|
145 .endFill(); |
|
146 stage.addChild(lineGraphics); |
|
147 } |
|
148 }); |
|
149 |
|
150 this.init = function() { |
|
151 |
|
152 if(typeof(canvasContainer) === 'string') { |
|
153 canvasContainer = document.getElementById(canvasContainer); |
|
154 } |
|
155 if(typeof(logContainer) === 'string') { |
|
156 logContainer = document.getElementById(logContainer); |
|
157 } |
|
158 if(typeof(timeContainer) === 'string') { |
|
159 timeContainer = document.getElementById(timeContainer); |
|
160 } |
|
161 |
|
162 |
|
163 if(!this.logger){ |
|
164 document.body.removeChild(logContainer); |
|
165 logContainer = undefined; |
|
166 } |
|
167 var sock; |
|
168 |
|
169 canvasContainer.appendChild(renderer.view); |
|
170 |
|
171 if ('WebSocket' in window) { |
|
172 sock = new WebSocket(wsUri); |
|
173 } else if ('MozWebSocket' in window) { |
|
174 sock = new MozWebSocket(wsUri); |
|
175 } else { |
|
176 this.log('Browser does not support WebSocket!'); |
|
177 window.location = 'http://autobahn.ws/unsupportedbrowser'; |
|
178 } |
|
179 |
|
180 if (!sock) { |
|
181 return; |
|
182 } |
|
183 sock.onopen = function(){ |
|
184 if(_this.logger){ |
|
185 _this.log('Connected to ' + _this.wsUri); |
|
186 } |
|
187 }; |
|
188 |
|
189 sock.onclose = function(e) { |
|
190 if(_this.logger){ |
|
191 _this.log('Connection closed (wasClean = ' + e.wasClean + ', code = ' + e.code + ', reason = \'' + e.reason + '\')'); |
|
192 } |
|
193 sock = null; |
|
194 }; |
|
195 |
|
196 sock.onmessage = function(e) { |
|
197 var dataJson = JSON.parse(e.data); |
|
198 if(_this.logger){ |
|
199 var dataDate = new Date((dataJson.content[0]-NTP_EPOCH_DELTA)*1000); |
|
200 _this.log('Got message: ' + e.data + ' - ' + dataDate.toISOString()); |
|
201 } |
|
202 _this.addNotes(dataJson); |
|
203 }; |
|
204 |
|
205 }; |
|
206 |
|
207 |
|
208 this.addNotes = function(data) { |
|
209 var note = data.content[3]; |
|
210 var velocity = data.content[4]; |
|
211 var ts = (data.content[0] - NTP_EPOCH_DELTA)*1000; |
|
212 var channel = data.content[2]; |
|
213 var sessionTs = data.content[1]; |
|
214 |
|
215 pianorollList.forEach(function(c) { |
|
216 c.addNote(note, ts, sessionTs, velocity, channel, 0); |
|
217 }); |
|
218 }; |
|
219 |
|
220 this.refreshStage = function() { |
|
221 pianorollList.forEach(function(c) { |
|
222 c.move(); |
|
223 }); |
|
224 renderer.render(stage); |
|
225 }; |
|
226 |
|
227 // Init page and intervals |
|
228 var refreshInterval; |
|
229 var refreshTimeInterval; |
|
230 var startTs; |
|
231 |
|
232 this.updateTime = function(){ |
|
233 var nbSec = (Date.now() - startTs) / 1000; |
|
234 var hours = Math.floor( nbSec / 3600 ) % 24; |
|
235 var minutes = Math.floor( nbSec / 60 ) % 60; |
|
236 var seconds = Math.floor(nbSec % 60); |
|
237 var timeStr = (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds); |
|
238 timeContainer.innerHTML = timeStr; |
|
239 }; |
|
240 |
|
241 this.start = function() { |
|
242 |
|
243 startTs = Date.now(); |
|
244 refreshInterval = window.setInterval(function() {_this.refreshStage();}, 1000/this.framerate); |
|
245 refreshTimeInterval = window.setInterval(function() {_this.updateTime();}, 1000); |
|
246 pianorollList.forEach(function(c) { |
|
247 c.start(); |
|
248 }); |
|
249 }; |
|
250 |
|
251 this.stop = function() { |
|
252 window.clearInterval(refreshInterval); |
|
253 window.clearInterval(refreshTimeInterval); |
|
254 pianorollList.forEach(function(c) { |
|
255 c.stop(); |
|
256 }); |
|
257 }; |
|
258 |
|
259 |
|
260 this.log = function(m) { |
|
261 if(this.logger){ |
|
262 this.logContainer.innerHTML += m + '\n'; |
|
263 this.logContainer.scrollTop = logContainer.scrollHeight; |
|
264 } |
|
265 }; |
|
266 |
|
267 |
|
268 window.onload = function() { |
|
269 _this.init(); |
|
270 _this.start(); |
|
271 }; |
|
272 |
|
273 return this; |
|
274 } |
|
275 |
|
276 module.exports = { |
|
277 DoubleRoll: DoubleRoll |
|
278 }; |