· 6 years ago · Jan 24, 2020, 04:28 PM
1// ==UserScript==
2// @name Chara's Bot [1.0]
3// @namespace http://www.multiplayerpiano.com/
4// @notice Made By Fennece.
5// @Version 0.1
6// @description <3
7// @include http://www.multiplayerpiano.com/*
8// @match http://www.multiplayerpiano.com/script.js
9// @copyright 2013+
10// ==/UserScript==
11
12
13// 钢琴
14
15$(function() {
16
17 var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
18
19 var gSeeOwnCursor = true;
20
21 var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i)); // todo this is no longer needed
22
23 if (!Array.prototype.indexOf) {
24 Array.prototype.indexOf = function(elt /*, from*/) {
25 var len = this.length >>> 0;
26 var from = Number(arguments[1]) || 0;
27 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
28 if (from < 0) from += len;
29 for (; from < len; from++) {
30 if (from in this && this[from] === elt) return from;
31 }
32 return -1;
33 };
34 }
35
36 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
37 || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
38 || function (cb) { setTimeout(cb, 1000 / 30); };
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 var DEFAULT_VELOCITY = 0.5;
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 var TIMING_TARGET = 1000;
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141// Utility
142
143////////////////////////////////////////////////////////////////
144
145
146
147var Rect = function(x, y, w, h) {
148 this.x = x;
149 this.y = y;
150 this.w = w;
151 this.h = h;
152 this.x2 = x + w;
153 this.y2 = y + h;
154};
155Rect.prototype.contains = function(x, y) {
156 return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
157};
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174// performing translation
175
176////////////////////////////////////////////////////////////////
177
178 var Translation = (function() {
179 var strings = {
180 "people are playing": {
181 "pt": "pessoas estão jogando",
182 "es": "personas están jugando",
183 "ru": "человек играет",
184 "fr": "personnes jouent",
185 "ja": "人が遊んでいる",
186 "de": "Leute spielen",
187 "zh": "人在玩",
188 "nl": "mensen spelen",
189 "pl": "osób grają",
190 "hu": "ember játszik"
191 },
192 "New Room...": {
193 "pt": "Nova Sala ...",
194 "es": "Nueva sala de...",
195 "ru": "Новый номер...",
196 "ja": "新しい部屋",
197 "zh": "新房间",
198 "nl": "nieuwe Kamer",
199 "hu": "új szoba"
200 },
201 "room name": {
202 "pt": "nome da sala",
203 "es": "sala de nombre",
204 "ru": "название комнаты",
205 "fr": "nom de la chambre",
206 "ja": "ルーム名",
207 "de": "Raumnamen",
208 "zh": "房间名称",
209 "nl": "kamernaam",
210 "pl": "nazwa pokój",
211 "hu": "szoba neve"
212 },
213 "Visible (open to everyone)": {
214 "pt": "Visível (aberto a todos)",
215 "es": "Visible (abierto a todo el mundo)",
216 "ru": "Visible (открытый для всех)",
217 "fr": "Visible (ouvert à tous)",
218 "ja": "目に見える(誰にでも開いている)",
219 "de": "Sichtbar (offen für alle)",
220 "zh": "可见(向所有人开放)",
221 "nl": "Zichtbaar (open voor iedereen)",
222 "pl": "Widoczne (otwarte dla wszystkich)",
223 "hu": "Látható (nyitott mindenki számára)"
224 },
225 "Enable Chat": {
226 "pt": "Ativar bate-papo",
227 "es": "Habilitar chat",
228 "ru": "Включить чат",
229 "fr": "Activer discuter",
230 "ja": "チャットを有効にする",
231 "de": "aktivieren Sie chatten",
232 "zh": "启用聊天",
233 "nl": "Chat inschakelen",
234 "pl": "Włącz czat",
235 "hu": "a csevegést"
236 },
237 "Play Alone": {
238 "pt": "Jogar Sozinho",
239 "es": "Jugar Solo",
240 "ru": "Играть в одиночку",
241 "fr": "Jouez Seul",
242 "ja": "一人でプレイ",
243 "de": "Alleine Spielen",
244 "zh": "独自玩耍",
245 "nl": "Speel Alleen",
246 "pl": "Zagraj sam",
247 "hu": "Játssz egyedül"
248 }
249 // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr
250 // todo: Connecting, Offline mode, input placeholder, Notifications
251 };
252
253 var setLanguage = function(lang) {
254 language = lang
255 };
256
257 var getLanguage = function() {
258 if(window.navigator && navigator.language && navigator.language.length >= 2) {
259 return navigator.language.substr(0, 2).toLowerCase();
260 } else {
261 return "en";
262 }
263 };
264
265 var get = function(text, lang) {
266 if(typeof lang === "undefined") lang = language;
267 var row = strings[text];
268 if(row == undefined) return text;
269 var string = row[lang];
270 if(string == undefined) return text;
271 return string;
272 };
273
274 var perform = function(lang) {
275 if(typeof lang === "undefined") lang = language;
276 $(".translate").each(function(i, ele) {
277 var th = $(this);
278 if(ele.tagName && ele.tagName.toLowerCase() == "input") {
279 if(typeof ele.placeholder != "undefined") {
280 th.attr("placeholder", get(th.attr("placeholder"), lang))
281 }
282 } else {
283 th.text(get(th.text(), lang));
284 }
285 });
286 };
287
288 var language = getLanguage();
289
290 return {
291 setLanguage: setLanguage,
292 getLanguage: getLanguage,
293 get: get,
294 perform: perform
295 };
296 })();
297
298 Translation.perform();
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314// AudioEngine classes
315
316////////////////////////////////////////////////////////////////
317
318 var AudioEngine = function() {
319 };
320
321 AudioEngine.prototype.init = function(cb) {
322 this.volume = 0.6;
323 this.sounds = {};
324 this.paused = true;
325 return this;
326 };
327
328 AudioEngine.prototype.load = function(id, url, cb) {
329 };
330
331 AudioEngine.prototype.play = function() {
332 };
333
334 AudioEngine.prototype.stop = function() {
335 };
336
337 AudioEngine.prototype.setVolume = function(vol) {
338 this.volume = vol;
339 };
340
341 AudioEngine.prototype.resume = function() {
342 this.paused = false;
343 };
344
345
346 AudioEngineWeb = function() {
347 this.threshold = 1000;
348 this.worker = new Worker("/workerTimer.js");
349 var self = this;
350 this.worker.onmessage = function(event)
351 {
352 if(event.data.args)
353 if(event.data.args.action==0)
354 {
355 self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
356 }
357 else
358 {
359 self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
360 }
361 }
362 };
363
364 AudioEngineWeb.prototype = new AudioEngine();
365
366 AudioEngineWeb.prototype.init = function(cb) {
367 AudioEngine.prototype.init.call(this);
368
369 this.context = new AudioContext();
370
371 this.masterGain = this.context.createGain();
372 this.masterGain.connect(this.context.destination);
373 this.masterGain.gain.value = this.volume;
374
375 this.limiterNode = this.context.createDynamicsCompressor();
376 this.limiterNode.threshold.value = -10;
377 this.limiterNode.knee.value = 0;
378 this.limiterNode.ratio.value = 20;
379 this.limiterNode.attack.value = 0;
380 this.limiterNode.release.value = 0.1;
381 this.limiterNode.connect(this.masterGain);
382
383 // for synth mix
384 this.pianoGain = this.context.createGain();
385 this.pianoGain.gain.value = 0.5;
386 this.pianoGain.connect(this.limiterNode);
387 this.synthGain = this.context.createGain();
388 this.synthGain.gain.value = 0.5;
389 this.synthGain.connect(this.limiterNode);
390
391 this.playings = {};
392
393 if(cb) setTimeout(cb, 0);
394 return this;
395 };
396
397 AudioEngineWeb.prototype.load = function(id, url, cb) {
398 var audio = this;
399 var req = new XMLHttpRequest();
400 req.open("GET", url);
401 req.responseType = "arraybuffer";
402 req.addEventListener("readystatechange", function(evt) {
403 if(req.readyState !== 4) return;
404 try {
405 audio.context.decodeAudioData(req.response, function(buffer) {
406 audio.sounds[id] = buffer;
407 if(cb) cb();
408 });
409 } catch(e) {
410 /*throw new Error(e.message
411 + " / id: " + id
412 + " / url: " + url
413 + " / status: " + req.status
414 + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer)
415 + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/
416 new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ",
417 target: "#piano", duration: 10000});
418 }
419 });
420 req.send();
421 };
422
423 AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
424 if(this.paused) return;
425 if(!this.sounds.hasOwnProperty(id)) return;
426 var source = this.context.createBufferSource();
427 source.buffer = this.sounds[id];
428 var gain = this.context.createGain();
429 gain.gain.value = vol;
430 source.connect(gain);
431 gain.connect(this.pianoGain);
432 source.start(time);
433 // Patch from ste-art remedies stuttering under heavy load
434 if(this.playings[id]) {
435 var playing = this.playings[id];
436 playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
437 playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
438 playing.source.stop(time + 0.21);
439 if(enableSynth && playing.voice) {
440 playing.voice.stop(time);
441 }
442 }
443 this.playings[id] = {"source": source, "gain": gain, "part_id": part_id};
444
445 if(enableSynth) {
446 this.playings[id].voice = new synthVoice(id, time);
447 }
448 }
449
450 AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id)
451 {
452 if(!this.sounds.hasOwnProperty(id)) return;
453 var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
454 var delay = delay_ms - this.threshold;
455 if(delay<=0) this.actualPlay(id, vol, time, part_id);
456 else {
457 this.worker.postMessage({delay:delay,args:{action:0/*play*/,id:id, vol:vol, time:time, part_id:part_id}}); // but start scheduling right before play.
458 }
459 }
460
461 AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
462 if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
463 var gain = this.playings[id].gain.gain;
464 gain.setValueAtTime(gain.value, time);
465 gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
466 gain.linearRampToValueAtTime(0.0, time + 0.4);
467 this.playings[id].source.stop(time + 0.41);
468
469
470 if(this.playings[id].voice) {
471 this.playings[id].voice.stop(time);
472 }
473
474 this.playings[id] = null;
475 }
476 };
477
478 AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
479 var time = this.context.currentTime + (delay_ms / 1000);
480 var delay = delay_ms - this.threshold;
481 if(delay<=0) this.actualStop(id, time, part_id);
482 else {
483 this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}});
484 }
485 };
486
487 AudioEngineWeb.prototype.setVolume = function(vol) {
488 AudioEngine.prototype.setVolume.call(this, vol);
489 this.masterGain.gain.value = this.volume;
490 };
491
492 AudioEngineWeb.prototype.resume = function() {
493 this.paused = false;
494 this.context.resume();
495 };
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522// Renderer classes
523
524////////////////////////////////////////////////////////////////
525
526 var Renderer = function() {
527 };
528
529 Renderer.prototype.init = function(piano) {
530 this.piano = piano;
531 this.resize();
532 return this;
533 };
534
535 Renderer.prototype.resize = function(width, height) {
536 if(typeof width == "undefined") width = $(this.piano.rootElement).width();
537 if(typeof height == "undefined") height = Math.floor(width * 0.2);
538 $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"});
539 this.width = width * window.devicePixelRatio;
540 this.height = height * window.devicePixelRatio;
541 };
542
543 Renderer.prototype.visualize = function(key, color) {
544 };
545
546
547
548
549 var CanvasRenderer = function() {
550 Renderer.call(this);
551 };
552
553 CanvasRenderer.prototype = new Renderer();
554
555 CanvasRenderer.prototype.init = function(piano) {
556 this.canvas = document.createElement("canvas");
557 this.ctx = this.canvas.getContext("2d");
558 piano.rootElement.appendChild(this.canvas);
559
560 Renderer.prototype.init.call(this, piano); // calls resize()
561
562 // create render loop
563 var self = this;
564 var render = function() {
565 self.redraw();
566 requestAnimationFrame(render);
567 };
568 requestAnimationFrame(render);
569
570 // add event listeners
571 var mouse_down = false;
572 var last_key = null;
573 $(piano.rootElement).mousedown(function(event) {
574 mouse_down = true;
575 //event.stopPropagation();
576 event.preventDefault();
577
578 var pos = CanvasRenderer.translateMouseEvent(event);
579 var hit = self.getHit(pos.x, pos.y);
580 if(hit) {
581 press(hit.key.note, hit.v);
582 last_key = hit.key;
583 }
584 });
585 piano.rootElement.addEventListener("touchstart", function(event) {
586 mouse_down = true;
587 //event.stopPropagation();
588 event.preventDefault();
589 for(var i in event.changedTouches) {
590 var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
591 var hit = self.getHit(pos.x, pos.y);
592 if(hit) {
593 press(hit.key.note, hit.v);
594 last_key = hit.key;
595 }
596 }
597 }, false);
598 $(window).mouseup(function(event) {
599 if(last_key) {
600 release(last_key.note);
601 }
602 mouse_down = false;
603 last_key = null;
604 });
605 /*$(piano.rootElement).mousemove(function(event) {
606 if(!mouse_down) return;
607 var pos = CanvasRenderer.translateMouseEvent(event);
608 var hit = self.getHit(pos.x, pos.y);
609 if(hit && hit.key != last_key) {
610 press(hit.key.note, hit.v);
611 last_key = hit.key;
612 }
613 });*/
614
615 return this;
616 };
617
618 CanvasRenderer.prototype.resize = function(width, height) {
619 Renderer.prototype.resize.call(this, width, height);
620 if(this.width < 52 * 2) this.width = 52 * 2;
621 if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
622 this.canvas.width = this.width;
623 this.canvas.height = this.height;
624 this.canvas.style.width = this.width / window.devicePixelRatio + "px";
625 this.canvas.style.height = this.height / window.devicePixelRatio + "px";
626
627 // calculate key sizes
628 this.whiteKeyWidth = Math.floor(this.width / 52);
629 this.whiteKeyHeight = Math.floor(this.height * 0.9);
630 this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
631 this.blackKeyHeight = Math.floor(this.height * 0.5);
632
633 this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
634 this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
635
636 this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
637 this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
638 this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
639 this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
640 this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
641 this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
642 this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
643 this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
644
645 // prerender white key
646 this.whiteKeyRender = document.createElement("canvas");
647 this.whiteKeyRender.width = this.whiteKeyWidth;
648 this.whiteKeyRender.height = this.height + 10;
649 var ctx = this.whiteKeyRender.getContext("2d");
650 if(ctx.createLinearGradient) {
651 var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
652 gradient.addColorStop(0, "#eee");
653 gradient.addColorStop(0.75, "#fff");
654 gradient.addColorStop(1, "#dad4d4");
655 ctx.fillStyle = gradient;
656 } else {
657 ctx.fillStyle = "#fff";
658 }
659 ctx.strokeStyle = "#000";
660 ctx.lineJoin = "round";
661 ctx.lineCap = "round";
662 ctx.lineWidth = 10;
663 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
664 ctx.lineWidth = 4;
665 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
666
667 // prerender black key
668 this.blackKeyRender = document.createElement("canvas");
669 this.blackKeyRender.width = this.blackKeyWidth + 10;
670 this.blackKeyRender.height = this.blackKeyHeight + 10;
671 var ctx = this.blackKeyRender.getContext("2d");
672 if(ctx.createLinearGradient) {
673 var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
674 gradient.addColorStop(0, "#000");
675 gradient.addColorStop(1, "#444");
676 ctx.fillStyle = gradient;
677 } else {
678 ctx.fillStyle = "#000";
679 }
680 ctx.strokeStyle = "#222";
681 ctx.lineJoin = "round";
682 ctx.lineCap = "round";
683 ctx.lineWidth = 8;
684 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
685 ctx.lineWidth = 4;
686 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
687
688 // prerender shadows
689 this.shadowRender = [];
690 var y = -this.canvas.height * 2;
691 for(var j = 0; j < 2; j++) {
692 var canvas = document.createElement("canvas");
693 this.shadowRender[j] = canvas;
694 canvas.width = this.canvas.width;
695 canvas.height = this.canvas.height;
696 var ctx = canvas.getContext("2d");
697 var sharp = j ? true : false;
698 ctx.lineJoin = "round";
699 ctx.lineCap = "round";
700 ctx.lineWidth = 1;
701 ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
702 ctx.shadowBlur = this.keyMovement * 3;
703 ctx.shadowOffsetY = -y + this.keyMovement;
704 if(sharp) {
705 ctx.shadowOffsetX = this.keyMovement;
706 } else {
707 ctx.shadowOffsetX = 0;
708 ctx.shadowOffsetY = -y + this.keyMovement;
709 }
710 for(var i in this.piano.keys) {
711 if(!this.piano.keys.hasOwnProperty(i)) continue;
712 var key = this.piano.keys[i];
713 if(key.sharp != sharp) continue;
714
715 if(key.sharp) {
716 ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
717 y + ctx.lineWidth / 2,
718 this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
719 } else {
720 ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
721 y + ctx.lineWidth / 2,
722 this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
723 }
724 }
725 }
726
727 // update key rects
728 for(var i in this.piano.keys) {
729 if(!this.piano.keys.hasOwnProperty(i)) continue;
730 var key = this.piano.keys[i];
731 if(key.sharp) {
732 key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
733 this.blackKeyWidth, this.blackKeyHeight);
734 } else {
735 key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
736 this.whiteKeyWidth, this.whiteKeyHeight);
737 }
738 }
739 };
740
741 CanvasRenderer.prototype.visualize = function(key, color) {
742 key.timePlayed = Date.now();
743 key.blips.push({"time": key.timePlayed, "color": color});
744 };
745
746 CanvasRenderer.prototype.redraw = function() {
747 var now = Date.now();
748 var timeLoadedEnd = now - 1000;
749 var timePlayedEnd = now - 100;
750 var timeBlipEnd = now - 1000;
751
752 this.ctx.save();
753 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
754 // draw all keys
755 for(var j = 0; j < 2; j++) {
756 this.ctx.globalAlpha = 1.0;
757 this.ctx.drawImage(this.shadowRender[j], 0, 0);
758 var sharp = j ? true : false;
759 for(var i in this.piano.keys) {
760 if(!this.piano.keys.hasOwnProperty(i)) continue;
761 var key = this.piano.keys[i];
762 if(key.sharp != sharp) continue;
763
764 if(!key.loaded) {
765 this.ctx.globalAlpha = 0.2;
766 } else if(key.timeLoaded > timeLoadedEnd) {
767 this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
768 } else {
769 this.ctx.globalAlpha = 1.0;
770 }
771 var y = 0;
772 if(key.timePlayed > timePlayedEnd) {
773 y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
774 }
775 var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial
776 : this.whiteKeyWidth * key.spatial);
777 var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
778 this.ctx.drawImage(image, x, y);
779
780 // render blips
781 if(key.blips.length) {
782 var alpha = this.ctx.globalAlpha;
783 var w, h;
784 if(key.sharp) {
785 x += this.blackBlipX;
786 y = this.blackBlipY;
787 w = this.blackBlipWidth;
788 h = this.blackBlipHeight;
789 } else {
790 x += this.whiteBlipX;
791 y = this.whiteBlipY;
792 w = this.whiteBlipWidth;
793 h = this.whiteBlipHeight;
794 }
795 for(var b = 0; b < key.blips.length; b++) {
796 var blip = key.blips[b];
797 if(blip.time > timeBlipEnd) {
798 this.ctx.fillStyle = blip.color;
799 this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
800 this.ctx.fillRect(x, y, w, h);
801 } else {
802 key.blips.splice(b, 1);
803 --b;
804 }
805 y -= Math.floor(h * 1.1);
806 }
807 }
808 }
809 }
810 this.ctx.restore();
811 };
812
813 CanvasRenderer.prototype.getHit = function(x, y) {
814 for(var j = 0; j < 2; j++) {
815 var sharp = j ? false : true; // black keys first
816 for(var i in this.piano.keys) {
817 if(!this.piano.keys.hasOwnProperty(i)) continue;
818 var key = this.piano.keys[i];
819 if(key.sharp != sharp) continue;
820 if(key.rect.contains(x, y)) {
821 var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
822 v += 0.25;
823 v *= DEFAULT_VELOCITY;
824 if(v > 1.0) v = 1.0;
825 return {"key": key, "v": v};
826 }
827 }
828 }
829 return null;
830 };
831
832
833 CanvasRenderer.isSupported = function() {
834 var canvas = document.createElement("canvas");
835 return !!(canvas.getContext && canvas.getContext("2d"));
836 };
837
838 CanvasRenderer.translateMouseEvent = function(evt) {
839 var element = evt.target;
840 var offx = 0;
841 var offy = 0;
842 do {
843 if(!element) break; // wtf, wtf?
844 offx += element.offsetLeft;
845 offy += element.offsetTop;
846 } while(element = element.offsetParent);
847 return {
848 x: (evt.pageX - offx) * window.devicePixelRatio,
849 y: (evt.pageY - offy) * window.devicePixelRatio
850 }
851 };
852
853
854
855
856
857
858
859
860
861
862
863// Soundpack Stuff by electrashave ♥
864
865////////////////////////////////////////////////////////////////
866
867 function SoundSelector(piano) {
868 this.initialized = false;
869 this.keys = piano.keys;
870 this.loading = {};
871 this.notification;
872 this.packs = [];
873 this.piano = piano;
874 this.soundSelection = localStorage.soundSelection || "MPP Classic";
875 this.addPack({name: "MPP Classic", keys: Object.keys(this.piano.keys), ext: ".mp3", url: "/sounds/mppclassic/"});
876 }
877
878 SoundSelector.prototype.addPack = function(pack, load) {
879 var self = this;
880 self.loading[pack.url || pack] = true;
881 function add(obj) {
882 var added = false;
883 for (var i = 0; self.packs.length > i; i++) {
884 if (obj.name == self.packs[i].name) {
885 added = true;
886 break;
887 }
888 }
889
890 if (added) return console.warn("Sounds already added!!"); //no adding soundpacks twice D:<
891
892 if (obj.url.substr(obj.url.length-1) != "/") obj.url = obj.url + "/";
893 var html = document.createElement("li");
894 html.classList = "pack";
895 html.innerText = obj.name + " (" + obj.keys.length + " keys)";
896 html.onclick = function() {
897 self.loadPack(obj.name);
898 self.notification.close();
899 };
900 obj.html = html;
901 self.packs.push(obj);
902 self.packs.sort(function(a, b) {
903 if(a.name < b.name) return -1;
904 if(a.name > b.name) return 1;
905 return 0;
906 });
907 if (load) self.loadPack(obj.name);
908 delete self.loading[obj.url];
909 }
910
911 if (typeof pack == "string") {
912 $.getJSON(pack + "/info.json").done(function(json) {
913 json.url = pack;
914 add(json);
915 });
916 } else add(pack); //validate packs??
917 };
918
919 SoundSelector.prototype.addPacks = function(packs) {
920 for (var i = 0; packs.length > i; i++) this.addPack(packs[i]);
921 };
922
923 SoundSelector.prototype.init = function() {
924 var self = this;
925 if (self.initialized) return console.warn("Sound selector already initialized!");
926
927 if (!!Object.keys(self.loading).length) return setTimeout(function() {
928 self.init();
929 }, 250);
930
931 $("#sound-btn").on("click", function() {
932 if (document.getElementById("Notification-Sound-Selector") != null) return self.notification.close();
933 var html = document.createElement("ul");
934 $(html).append("<h1>Current Sound: " + self.soundSelection + "</h1>");
935
936 for (var i = 0; self.packs.length > i; i++) {
937 var pack = self.packs[i];
938 if (pack.name == self.soundSelection) pack.html.classList = "pack enabled";
939 else pack.html.classList = "pack";
940 html.appendChild(pack.html);
941 }
942 self.notification = new Notification({title: "Sound Selector:", html: html, id: "Sound-Selector", duration: -1, target: "#sound-btn"});
943 });
944 self.initialized = true;
945 self.loadPack(self.soundSelection, true);
946 };
947
948 SoundSelector.prototype.loadPack = function(pack, f) {
949 for (var i = 0; this.packs.length > i; i++) {
950 var p = this.packs[i];
951 if (p.name == pack) {
952 pack = p;
953 break;
954 }
955 }
956 if (typeof pack == "string") {
957 console.warn("Sound pack does not exist! Loading default pack...");
958 return this.loadPack("MPP Classic");
959 }
960
961 if (pack.name == this.soundSelection && !f) return;
962 if (pack.keys.length != Object.keys(this.piano.keys).length) {
963 this.piano.keys = {};
964 for (var i = 0; pack.keys.length > i; i++) this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]];
965 this.piano.renderer.resize();
966 }
967
968 var self = this;
969 for (var i in this.piano.keys) {
970 if (!this.piano.keys.hasOwnProperty(i)) continue;
971 (function() {
972 var key = self.piano.keys[i];
973 key.loaded = false;
974 self.piano.audio.load(key.note, pack.url + key.note + pack.ext, function() {
975 key.loaded = true;
976 key.timeLoaded = Date.now();
977 });
978 })();
979 }
980 localStorage.soundSelection = pack.name;
981 this.soundSelection = pack.name;
982 };
983
984 SoundSelector.prototype.removePack = function(name) {
985 var found = false;
986 for (var i = 0; this.packs.length > i; i++) {
987 var pack = this.packs[i];
988 if (pack.name == name) {
989 this.packs.splice(i, 1);
990 if (pack.name == this.soundSelection) this.loadPack(this.packs[0].name); //add mpp default if none?
991 break;
992 }
993 }
994 if (!found) console.warn("Sound pack not found!");
995 };
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007// Pianoctor
1008
1009////////////////////////////////////////////////////////////////
1010
1011 var PianoKey = function(note, octave) {
1012 this.note = note + octave;
1013 this.baseNote = note;
1014 this.octave = octave;
1015 this.sharp = note.indexOf("s") != -1;
1016 this.loaded = false;
1017 this.timeLoaded = 0;
1018 this.domElement = null;
1019 this.timePlayed = 0;
1020 this.blips = [];
1021 };
1022
1023 var Piano = function(rootElement) {
1024
1025 var piano = this;
1026 piano.rootElement = rootElement;
1027 piano.keys = {};
1028
1029 var white_spatial = 0;
1030 var black_spatial = 0;
1031 var black_it = 0;
1032 var black_lut = [2, 1, 2, 1, 1];
1033 var addKey = function(note, octave) {
1034 var key = new PianoKey(note, octave);
1035 piano.keys[key.note] = key;
1036 if(key.sharp) {
1037 key.spatial = black_spatial;
1038 black_spatial += black_lut[black_it % 5];
1039 ++black_it;
1040 } else {
1041 key.spatial = white_spatial;
1042 ++white_spatial;
1043 }
1044 }
1045 if(test_mode) {
1046 addKey("c", 2);
1047 } else {
1048 addKey("a", -1);
1049 addKey("as", -1);
1050 addKey("b", -1);
1051 var notes = "c cs d ds e f fs g gs a as b".split(" ");
1052 for(var oct = 0; oct < 7; oct++) {
1053 for(var i in notes) {
1054 addKey(notes[i], oct);
1055 }
1056 }
1057 addKey("c", 7);
1058 }
1059
1060
1061 this.renderer = new CanvasRenderer().init(this);
1062
1063 window.addEventListener("resize", function() {
1064 piano.renderer.resize();
1065 });
1066
1067
1068 window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
1069 var audio_engine = AudioEngineWeb;
1070 this.audio = new audio_engine().init();
1071 };
1072
1073 Piano.prototype.play = function(note, vol, participant, delay_ms) {
1074 if(!this.keys.hasOwnProperty(note)) return;
1075 var key = this.keys[note];
1076 if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
1077 if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
1078 var self = this;
1079 var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
1080 if(jq_namediv) {
1081 setTimeout(function() {
1082 self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
1083 jq_namediv.addClass("play");
1084 setTimeout(function() {
1085 jq_namediv.removeClass("play");
1086 }, 30);
1087 }, delay_ms);
1088 }
1089 };
1090
1091 Piano.prototype.stop = function(note, participant, delay_ms) {
1092 if(!this.keys.hasOwnProperty(note)) return;
1093 var key = this.keys[note];
1094 if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
1095 if(typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
1096 };
1097
1098 var gPiano = new Piano(document.getElementById("piano"));
1099
1100 var gSoundSelector = new SoundSelector(gPiano);
1101 gSoundSelector.addPacks(["/sounds/Emotional_2.0/", "/sounds/Harp/", "/sounds/Music_Box/", "/sounds/Vintage_Upright/", "/sounds/Steinway_Grand/", "/sounds/Emotional/", "/sounds/Untitled/"]);
1102 gSoundSelector.init();
1103
1104
1105
1106
1107
1108
1109
1110 var gAutoSustain = false;
1111 var gSustain = false;
1112
1113 var gHeldNotes = {};
1114 var gSustainedNotes = {};
1115
1116
1117 function press(id, vol) {
1118 if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) {
1119 gHeldNotes[id] = true;
1120 gSustainedNotes[id] = true;
1121 gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
1122 gClient.startNote(id, vol);
1123 }
1124 }
1125
1126 function release(id) {
1127 if(gHeldNotes[id]) {
1128 gHeldNotes[id] = false;
1129 if((gAutoSustain || gSustain) && !enableSynth) {
1130 gSustainedNotes[id] = true;
1131 } else {
1132 if(gNoteQuota.spend(1)) {
1133 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1134 gClient.stopNote(id);
1135 gSustainedNotes[id] = false;
1136 }
1137 }
1138 }
1139 }
1140
1141 function pressSustain() {
1142 gSustain = true;
1143 }
1144
1145 function releaseSustain() {
1146 gSustain = false;
1147 if(!gAutoSustain) {
1148 for(var id in gSustainedNotes) {
1149 if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
1150 gSustainedNotes[id] = false;
1151 if(gNoteQuota.spend(1)) {
1152 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1153 gClient.stopNote(id);
1154 }
1155 }
1156 }
1157 }
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168// internet science
1169
1170////////////////////////////////////////////////////////////////
1171
1172 var channel_id = decodeURIComponent(window.location.pathname);
1173 if(channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
1174 if(channel_id == "") channel_id = "lobby";
1175
1176 var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080;
1177 var gClient = new Client("ws://" + window.location.hostname + ":" + wssport);
1178 gClient.setChannel(channel_id);
1179 gClient.start();
1180
1181
1182 // Setting status
1183 (function() {
1184 gClient.on("status", function(status) {
1185 $("#status").text(status);
1186 });
1187 gClient.on("count", function(count) {
1188 if(count > 0) {
1189 $("#status").html('<span class="number">'+count+'</span> '+(count==1? 'person is' : 'people are')+' playing');
1190 document.title = "Piano (" + count + ")";
1191 } else {
1192 document.title = "Multiplayer Piano";
1193 }
1194 });
1195 })();
1196
1197 // Handle changes to participants
1198 (function() {
1199 gClient.on("participant added", function(part) {
1200
1201 part.displayX = 150;
1202 part.displayY = 50;
1203
1204 // add nameDiv
1205 var div = document.createElement("div");
1206 div.className = "name";
1207 div.participantId = part.id;
1208 div.textContent = part.name || "";
1209 div.style.backgroundColor = part.color || "#777";
1210 if(gClient.participantId === part.id) {
1211 $(div).addClass("me");
1212 }
1213 if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
1214 $(div).addClass("owner");
1215 }
1216 if(gPianoMutes.indexOf(part._id) !== -1) {
1217 $(part.nameDiv).addClass("muted-notes");
1218 }
1219 if(gChatMutes.indexOf(part._id) !== -1) {
1220 $(part.nameDiv).addClass("muted-chat");
1221 }
1222 div.style.display = "none";
1223 part.nameDiv = $("#names")[0].appendChild(div);
1224 $(part.nameDiv).fadeIn(2000);
1225
1226 // sort names
1227 var arr = $("#names .name");
1228 arr.sort(function(a, b) {
1229 a = a.style.backgroundColor; // todo: sort based on user id instead
1230 b = b.style.backgroundColor;
1231 if (a > b) return 1;
1232 else if (a < b) return -1;
1233 else return 0;
1234 });
1235 $("#names").html(arr);
1236
1237 // add cursorDiv
1238 if(gClient.participantId !== part.id || gSeeOwnCursor) {
1239 var div = document.createElement("div");
1240 div.className = "cursor";
1241 div.style.display = "none";
1242 part.cursorDiv = $("#cursors")[0].appendChild(div);
1243 $(part.cursorDiv).fadeIn(2000);
1244
1245 var div = document.createElement("div");
1246 div.className = "name";
1247 div.style.backgroundColor = part.color || "#777"
1248 div.textContent = part.name || "";
1249 part.cursorDiv.appendChild(div);
1250
1251 } else {
1252 part.cursorDiv = undefined;
1253 }
1254 });
1255 gClient.on("participant removed", function(part) {
1256 // remove nameDiv
1257 var nd = $(part.nameDiv);
1258 var cd = $(part.cursorDiv);
1259 cd.fadeOut(2000);
1260 nd.fadeOut(2000, function() {
1261 nd.remove();
1262 cd.remove();
1263 part.nameDiv = undefined;
1264 part.cursorDiv = undefined;
1265 });
1266 });
1267 gClient.on("participant update", function(part) {
1268 var name = part.name || "";
1269 var color = part.color || "#777";
1270 part.nameDiv.style.backgroundColor = color;
1271 part.nameDiv.textContent = name;
1272 $(part.cursorDiv)
1273 .find(".name")
1274 .text(name)
1275 .css("background-color", color);
1276 });
1277 gClient.on("ch", function(msg) {
1278 for(var id in gClient.ppl) {
1279 if(gClient.ppl.hasOwnProperty(id)) {
1280 var part = gClient.ppl[id];
1281 if(part.id === gClient.participantId) {
1282 $(part.nameDiv).addClass("me");
1283 } else {
1284 $(part.nameDiv).removeClass("me");
1285 }
1286 if(msg.ch.crown && msg.ch.crown.participantId === part.id) {
1287 $(part.nameDiv).addClass("owner");
1288 $(part.cursorDiv).addClass("owner");
1289 } else {
1290 $(part.nameDiv).removeClass("owner");
1291 $(part.cursorDiv).removeClass("owner");
1292 }
1293 if(gPianoMutes.indexOf(part._id) !== -1) {
1294 $(part.nameDiv).addClass("muted-notes");
1295 } else {
1296 $(part.nameDiv).removeClass("muted-notes");
1297 }
1298 if(gChatMutes.indexOf(part._id) !== -1) {
1299 $(part.nameDiv).addClass("muted-chat");
1300 } else {
1301 $(part.nameDiv).removeClass("muted-chat");
1302 }
1303 }
1304 }
1305 });
1306 function updateCursor(msg) {
1307 const part = gClient.ppl[msg.id];
1308 if (part && part.cursorDiv) {
1309 part.cursorDiv.style.left = msg.x + "%";
1310 part.cursorDiv.style.top = msg.y + "%";
1311 }
1312 }
1313 gClient.on("m", updateCursor);
1314 gClient.on("participant added", updateCursor);
1315 })();
1316
1317
1318 // Handle changes to crown
1319 (function() {
1320 var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
1321 var jqcountdown = $('<span></span>').appendTo(jqcrown);
1322 var countdown_interval;
1323 jqcrown.click(function() {
1324 gClient.sendArray([{m: "chown", id: gClient.participantId}]);
1325 });
1326 gClient.on("ch", function(msg) {
1327 if(msg.ch.crown) {
1328 var crown = msg.ch.crown;
1329 if(!crown.participantId || !gClient.ppl[crown.participantId]) {
1330 var land_time = crown.time + 2000 - gClient.serverTimeOffset;
1331 var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
1332 jqcountdown.text("");
1333 jqcrown.show();
1334 if(land_time - Date.now() <= 0) {
1335 jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"});
1336 } else {
1337 jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"});
1338 jqcrown.addClass("spin");
1339 jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() {
1340 jqcrown.removeClass("spin");
1341 });
1342 }
1343 clearInterval(countdown_interval);
1344 countdown_interval = setInterval(function() {
1345 var time = Date.now();
1346 if(time >= land_time) {
1347 var ms = avail_time - time;
1348 if(ms > 0) {
1349 jqcountdown.text(Math.ceil(ms / 1000) + "s");
1350 } else {
1351 jqcountdown.text("");
1352 clearInterval(countdown_interval);
1353 }
1354 }
1355 }, 1000);
1356 } else {
1357 jqcrown.hide();
1358 }
1359 } else {
1360 jqcrown.hide();
1361 }
1362 });
1363 gClient.on("disconnect", function() {
1364 jqcrown.fadeOut(2000);
1365 });
1366 })();
1367
1368
1369 // Playing notes
1370 gClient.on("n", function(msg) {
1371 var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
1372 var participant = gClient.findParticipantById(msg.p);
1373 if(gPianoMutes.indexOf(participant._id) !== -1)
1374 return;
1375 for(var i = 0; i < msg.n.length; i++) {
1376 var note = msg.n[i];
1377 var ms = t + (note.d || 0);
1378 if(ms < 0) {
1379 ms = 0;
1380 }
1381 else if(ms > 10000) continue;
1382 if(note.s) {
1383 gPiano.stop(note.n, participant, ms);
1384 } else {
1385 var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY;
1386 if(vel < 0) vel = 0; else if (vel > 1) vel = 1;
1387 gPiano.play(note.n, vel, participant, ms);
1388 if(enableSynth) {
1389 gPiano.stop(note.n, participant, ms + 1000);
1390 }
1391 }
1392 }
1393 });
1394
1395 // Send cursor updates
1396 var mx = 0, last_mx = -10, my = 0, last_my = -10;
1397 setInterval(function() {
1398 if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
1399 last_mx = mx;
1400 last_my = my;
1401 gClient.sendArray([{m: "m", x: mx, y: my}]);
1402 if(gSeeOwnCursor) {
1403 gClient.emit("m", { m: "m", id: gClient.participantId, x: mx, y: my });
1404 }
1405 var part = gClient.getOwnParticipant();
1406 if(part) {
1407 part.x = mx;
1408 part.y = my;
1409 }
1410 }
1411 }, 50);
1412 $(document).mousemove(function(event) {
1413 mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
1414 my = ((event.pageY / $(window).height()) * 100).toFixed(2);
1415 });
1416
1417
1418 // Room settings button
1419 (function() {
1420 gClient.on("ch", function(msg) {
1421 if(gClient.isOwner()) {
1422 $("#room-settings-btn").show();
1423 } else {
1424 $("#room-settings-btn").hide();
1425 }
1426 });
1427 $("#room-settings-btn").click(function(evt) {
1428 if(gClient.channel && gClient.isOwner()) {
1429 var settings = gClient.channel.settings;
1430 openModal("#room-settings");
1431 setTimeout(function() {
1432 $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
1433 $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
1434 $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
1435 $("#room-settings input[name=color]").val(settings.color);
1436 }, 100);
1437 }
1438 });
1439 $("#room-settings .submit").click(function() {
1440 var settings = {
1441 visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
1442 chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
1443 crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
1444 color: $("#room-settings input[name=color]").val()
1445 };
1446 gClient.sendArray([{m: "chset", set: settings}]);
1447 closeModal();
1448 });
1449 $("#room-settings .drop-crown").click(function() {
1450 closeModal();
1451 if(confirm("This will drop the crown...!"))
1452 gClient.sendArray([{m: "chown"}]);
1453 });
1454 })();
1455
1456 // Handle notifications
1457 gClient.on("notification", function(msg) {
1458 new Notification(msg);
1459 });
1460
1461 // Don't foget spin
1462 gClient.on("ch", function(msg) {
1463 var chidlo = msg.ch._id.toLowerCase();
1464 if(chidlo === "spin" || chidlo.substr(-5) === "/spin") {
1465 $("#piano").addClass("spin");
1466 } else {
1467 $("#piano").removeClass("spin");
1468 }
1469 });
1470
1471 /*function eb() {
1472 if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
1473 ebsprite.start(gClient);
1474 } else {
1475 ebsprite.stop();
1476 }
1477 }
1478 if(ebsprite) {
1479 gClient.on("ch", eb);
1480 eb();
1481 }*/
1482
1483 // Crownsolo notice
1484 gClient.on("ch", function(msg) {
1485 if(msg.ch.settings.crownsolo) {
1486 if($("#crownsolo-notice").length == 0) {
1487 $('<div id="crownsolo-notice">').text('This room is set to "only the owner can play."').appendTo("body").fadeIn(1000);
1488 }
1489 } else {
1490 $("#crownsolo-notice").remove();
1491 }
1492 });
1493 gClient.on("disconnect", function() {
1494 $("#crownsolo-notice").remove();
1495 });
1496
1497
1498 // Background color
1499 (function() {
1500 var old_color1 = new Color("#000000");
1501 var old_color2 = new Color("#000000");
1502 function setColor(hex, hex2) {
1503 var color1 = new Color(hex);
1504 var color2 = new Color(hex2 || hex);
1505 if(!hex2)
1506 color2.add(-0x40, -0x40, -0x40);
1507
1508 var bottom = document.getElementById("bottom");
1509
1510 var duration = 500;
1511 var step = 0;
1512 var steps = 30;
1513 var step_ms = duration / steps;
1514 var difference = new Color(color1.r, color1.g, color1.b);
1515 difference.r -= old_color1.r;
1516 difference.g -= old_color1.g;
1517 difference.b -= old_color1.b;
1518 var inc1 = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
1519 difference = new Color(color2.r, color2.g, color2.b);
1520 difference.r -= old_color2.r;
1521 difference.g -= old_color2.g;
1522 difference.b -= old_color2.b;
1523 var inc2 = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
1524 var iv;
1525 iv = setInterval(function() {
1526 old_color1.add(inc1.r, inc1.g, inc1.b);
1527 old_color2.add(inc2.r, inc2.g, inc2.b);
1528 document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)";
1529 bottom.style.background = old_color2.toHexa();
1530 if(++step >= steps) {
1531 clearInterval(iv);
1532 old_color1 = color1;
1533 old_color2 = color2;
1534 document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)";
1535 bottom.style.background = color2.toHexa();
1536 }
1537 }, step_ms);
1538 }
1539
1540 function setColorToDefault() {
1541 setColor("#000000", "#000000");
1542 }
1543
1544 setColorToDefault();
1545
1546 gClient.on("ch", function(ch) {
1547 if(ch.ch.settings) {
1548 if(ch.ch.settings.color) {
1549 setColor(ch.ch.settings.color, ch.ch.settings.color2);
1550 } else {
1551 setColorToDefault();
1552 }
1553 }
1554 });
1555 })();
1556
1557
1558
1559
1560
1561
1562 var gPianoMutes = [];
1563
1564 var gChatMutes = [];
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584 var volume_slider = document.getElementById("volume-slider");
1585 volume_slider.value = gPiano.audio.volume;
1586 $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
1587 volume_slider.addEventListener("input", function(evt) {
1588 var v = +volume_slider.value;
1589 gPiano.audio.setVolume(v);
1590 if (window.localStorage) localStorage.volume = v;
1591 $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%");
1592 });
1593
1594
1595
1596
1597 var Note = function(note, octave) {
1598 this.note = note;
1599 this.octave = octave || 0;
1600 };
1601
1602
1603
1604 var n = function(a, b) { return {note: new Note(a, b), held: false}; };
1605 var key_binding = {
1606 65: n("gs"),
1607 90: n("a"),
1608 83: n("as"),
1609 88: n("b"),
1610 67: n("c", 1),
1611 70: n("cs", 1),
1612 86: n("d", 1),
1613 71: n("ds", 1),
1614 66: n("e", 1),
1615 78: n("f", 1),
1616 74: n("fs", 1),
1617 77: n("g", 1),
1618 75: n("gs", 1),
1619 188: n("a", 1),
1620 76: n("as", 1),
1621 190: n("b", 1),
1622 191: n("c", 2),
1623 222: n("cs", 2),
1624
1625 49: n("gs", 1),
1626 81: n("a", 1),
1627 50: n("as", 1),
1628 87: n("b", 1),
1629 69: n("c", 2),
1630 52: n("cs", 2),
1631 82: n("d", 2),
1632 53: n("ds", 2),
1633 84: n("e", 2),
1634 89: n("f", 2),
1635 55: n("fs", 2),
1636 85: n("g", 2),
1637 56: n("gs", 2),
1638 73: n("a", 2),
1639 57: n("as", 2),
1640 79: n("b", 2),
1641 80: n("c", 3),
1642 189: n("cs", 3),
1643 173: n("cs", 3), // firefox why
1644 219: n("d", 3),
1645 187: n("ds", 3),
1646 61: n("ds", 3), // firefox why
1647 221: n("e", 3)
1648 };
1649
1650 var capsLockKey = false;
1651
1652 var transpose_octave = 0;
1653
1654 function handleKeyDown(evt) {
1655 //console.log(evt);
1656 var code = parseInt(evt.keyCode);
1657 if(key_binding[code] !== undefined) {
1658 var binding = key_binding[code];
1659 if(!binding.held) {
1660 binding.held = true;
1661
1662 var note = binding.note;
1663 var octave = 1 + note.octave + transpose_octave;
1664 if(evt.shiftKey) ++octave;
1665 else if(capsLockKey || evt.ctrlKey) --octave;
1666 note = note.note + octave;
1667 var vol = velocityFromMouseY();
1668 press(note, vol);
1669 }
1670
1671 if(++gKeyboardSeq == 3) {
1672 gKnowsYouCanUseKeyboard = true;
1673 if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
1674 if(localStorage) localStorage.knowsYouCanUseKeyboard = true;
1675 if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
1676 }
1677
1678 evt.preventDefault();
1679 evt.stopPropagation();
1680 return false;
1681 } else if(code == 20) { // Caps Lock
1682 capsLockKey = true;
1683 evt.preventDefault();
1684 } else if(code === 0x20) { // Space Bar
1685 pressSustain();
1686 evt.preventDefault();
1687 } else if((code === 38 || code === 39) && transpose_octave < 3) {
1688 ++transpose_octave;
1689 } else if((code === 40 || code === 37) && transpose_octave > -2) {
1690 --transpose_octave;
1691 } else if(code == 9) { // Tab (don't tab away from the piano)
1692 evt.preventDefault();
1693 } else if(code == 8) { // Backspace (don't navigate Back)
1694 gAutoSustain = !gAutoSustain;
1695 evt.preventDefault();
1696 }
1697 };
1698
1699 function handleKeyUp(evt) {
1700 var code = parseInt(evt.keyCode);
1701 if(key_binding[code] !== undefined) {
1702 var binding = key_binding[code];
1703 if(binding.held) {
1704 binding.held = false;
1705
1706 var note = binding.note;
1707 var octave = 1 + note.octave + transpose_octave;
1708 if(evt.shiftKey) ++octave;
1709 else if(capsLockKey || evt.ctrlKey) --octave;
1710 note = note.note + octave;
1711 release(note);
1712 }
1713
1714 evt.preventDefault();
1715 evt.stopPropagation();
1716 return false;
1717 } else if(code == 20) { // Caps Lock
1718 capsLockKey = false;
1719 evt.preventDefault();
1720 } else if(code === 0x20) { // Space Bar
1721 releaseSustain();
1722 evt.preventDefault();
1723 }
1724 };
1725
1726 function handleKeyPress(evt) {
1727 evt.preventDefault();
1728 evt.stopPropagation();
1729 if(evt.keyCode == 27 || evt.keyCode == 13) {
1730 //$("#chat input").focus();
1731 }
1732 return false;
1733 };
1734
1735 var recapListener = function(evt) {
1736 captureKeyboard();
1737 };
1738
1739 function captureKeyboard() {
1740 $("#piano").off("mousedown", recapListener);
1741 $("#piano").off("touchstart", recapListener);
1742 $(document).on("keydown", handleKeyDown );
1743 $(document).on("keyup", handleKeyUp);
1744 $(window).on("keypress", handleKeyPress );
1745 };
1746
1747 function releaseKeyboard() {
1748 $(document).off("keydown", handleKeyDown );
1749 $(document).off("keyup", handleKeyUp);
1750 $(window).off("keypress", handleKeyPress );
1751 $("#piano").on("mousedown", recapListener);
1752 $("#piano").on("touchstart", recapListener);
1753 };
1754
1755 captureKeyboard();
1756
1757
1758 var velocityFromMouseY = function() {
1759 return 0.1 + (my / 100) * 0.6;
1760 };
1761
1762
1763
1764
1765
1766 // NoteQuota
1767 var gNoteQuota = (function() {
1768 var last_rat = 0;
1769 var nqjq = $("#quota .value");
1770 setInterval(function() {
1771 gNoteQuota.tick();
1772 }, 2000);
1773 return new NoteQuota(function(points) {
1774 // update UI
1775 var rat = (points / this.max) * 100;
1776 if(rat <= last_rat)
1777 nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
1778 else
1779 nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear");
1780 last_rat = rat;
1781 });
1782 })();
1783 gClient.on("nq", function(nq_params) {
1784 gNoteQuota.setParams(nq_params);
1785 });
1786 gClient.on("disconnect", function() {
1787 gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
1788 });
1789
1790
1791
1792 // click participant names
1793 (function() {
1794 var ele = document.getElementById("names");
1795 var touchhandler = function(e) {
1796 var target_jq = $(e.target);
1797 if(target_jq.hasClass("name")) {
1798 target_jq.addClass("play");
1799 if(e.target.participantId == gClient.participantId) {
1800 openModal("#rename", "input[name=name]");
1801 setTimeout(function() {
1802 $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
1803 $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
1804 }, 100);
1805 } else if(e.target.participantId) {
1806 var id = e.target.participantId;
1807 var part = gClient.ppl[id] || null;
1808 if(part) {
1809 participantMenu(part);
1810 e.stopPropagation();
1811 }
1812 }
1813 }
1814 };
1815 ele.addEventListener("mousedown", touchhandler);
1816 ele.addEventListener("touchstart", touchhandler);
1817 var releasehandler = function(e) {
1818 $("#names .name").removeClass("play");
1819 };
1820 document.body.addEventListener("mouseup", releasehandler);
1821 document.body.addEventListener("touchend", releasehandler);
1822
1823 var removeParticipantMenus = function() {
1824 $(".participant-menu").remove();
1825 $(".participantSpotlight").hide();
1826 document.removeEventListener("mousedown", removeParticipantMenus);
1827 document.removeEventListener("touchstart", removeParticipantMenus);
1828 };
1829
1830 var participantMenu = function(part) {
1831 if(!part) return;
1832 removeParticipantMenus();
1833 document.addEventListener("mousedown", removeParticipantMenus);
1834 document.addEventListener("touchstart", removeParticipantMenus);
1835 $("#" + part.id).find(".enemySpotlight").show();
1836 var menu = $('<div class="participant-menu"></div>');
1837 $("body").append(menu);
1838 // move menu to name position
1839 var jq_nd = $(part.nameDiv);
1840 var pos = jq_nd.position();
1841 menu.css({
1842 "top": pos.top + jq_nd.height() + 15,
1843 "left": pos.left + 6,
1844 "background": part.color || "black"
1845 });
1846 menu.on("mousedown touchstart", function(evt) {
1847 evt.stopPropagation();
1848 var target = $(evt.target);
1849 if(target.hasClass("menu-item")) {
1850 target.addClass("clicked");
1851 menu.fadeOut(200, function() {
1852 removeParticipantMenus();
1853 });
1854 }
1855 });
1856 // this spaces stuff out but also can be used for informational
1857 $('<div class="info"></div>').appendTo(menu).text(part._id);
1858 // add menu items
1859 if(gPianoMutes.indexOf(part._id) == -1) {
1860 $('<div class="menu-item">Mute Notes</div>').appendTo(menu)
1861 .on("mousedown touchstart", function(evt) {
1862 gPianoMutes.push(part._id);
1863 $(part.nameDiv).addClass("muted-notes");
1864 });
1865 } else {
1866 $('<div class="menu-item">Unmute Notes</div>').appendTo(menu)
1867 .on("mousedown touchstart", function(evt) {
1868 var i;
1869 while((i = gPianoMutes.indexOf(part._id)) != -1)
1870 gPianoMutes.splice(i, 1);
1871 $(part.nameDiv).removeClass("muted-notes");
1872 });
1873 }
1874 if(gChatMutes.indexOf(part._id) == -1) {
1875 $('<div class="menu-item">Mute Chat</div>').appendTo(menu)
1876 .on("mousedown touchstart", function(evt) {
1877 gChatMutes.push(part._id);
1878 $(part.nameDiv).addClass("muted-chat");
1879 });
1880 } else {
1881 $('<div class="menu-item">Unmute Chat</div>').appendTo(menu)
1882 .on("mousedown touchstart", function(evt) {
1883 var i;
1884 while((i = gChatMutes.indexOf(part._id)) != -1)
1885 gChatMutes.splice(i, 1);
1886 $(part.nameDiv).removeClass("muted-chat");
1887 });
1888 }
1889 if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
1890 $('<div class="menu-item">Mute Completely</div>').appendTo(menu)
1891 .on("mousedown touchstart", function(evt) {
1892 gPianoMutes.push(part._id);
1893 gChatMutes.push(part._id);
1894 $(part.nameDiv).addClass("muted-notes");
1895 $(part.nameDiv).addClass("muted-chat");
1896 });
1897 }
1898 if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
1899 $('<div class="menu-item">Unmute Completely</div>').appendTo(menu)
1900 .on("mousedown touchstart", function(evt) {
1901 var i;
1902 while((i = gPianoMutes.indexOf(part._id)) != -1)
1903 gPianoMutes.splice(i, 1);
1904 while((i = gChatMutes.indexOf(part._id)) != -1)
1905 gChatMutes.splice(i, 1);
1906 $(part.nameDiv).removeClass("muted-notes");
1907 $(part.nameDiv).removeClass("muted-chat");
1908 });
1909 }
1910 if(gClient.isOwner()) {
1911 $('<div class="menu-item give-crown">Give Crown</div>').appendTo(menu)
1912 .on("mousedown touchstart", function(evt) {
1913 if(confirm("Give room ownership to "+part.name+"?"))
1914 gClient.sendArray([{m: "chown", id: part.id}]);
1915 });
1916 $('<div class="menu-item kickban">Kickban</div>').appendTo(menu)
1917 .on("mousedown touchstart", function(evt) {
1918 var minutes = prompt("How many minutes? (0-60)", "30");
1919 if(minutes === null) return;
1920 minutes = parseFloat(minutes) || 0;
1921 var ms = minutes * 60 * 1000;
1922 gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]);
1923 });
1924 }
1925 menu.fadeIn(100);
1926 };
1927 })();
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944// Notification class
1945
1946////////////////////////////////////////////////////////////////
1947
1948 var Notification = function(par) {
1949 EventEmitter.call(this);
1950
1951 var par = par || {};
1952
1953 this.id = "Notification-" + (par.id || Math.random());
1954 this.title = par.title || "";
1955 this.text = par.text || "";
1956 this.html = par.html || "";
1957 this.target = $(par.target || "#piano");
1958 this.duration = par.duration || 30000;
1959 this["class"] = par["class"] || "classic";
1960
1961 var self = this;
1962 var eles = $("#" + this.id);
1963 if(eles.length > 0) {
1964 eles.remove();
1965 }
1966 this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
1967 '<div class="text"></div></div><div class="x">x</div></div>');
1968 this.domElement[0].id = this.id;
1969 this.domElement.addClass(this["class"]);
1970 this.domElement.find(".title").text(this.title);
1971 if(this.text.length > 0) {
1972 this.domElement.find(".text").text(this.text);
1973 } else if(this.html instanceof HTMLElement) {
1974 this.domElement.find(".text")[0].appendChild(this.html);
1975 } else if(this.html.length > 0) {
1976 this.domElement.find(".text").html(this.html);
1977 }
1978 document.body.appendChild(this.domElement.get(0));
1979
1980 this.position();
1981 this.onresize = function() {
1982 self.position();
1983 };
1984 window.addEventListener("resize", this.onresize);
1985
1986 this.domElement.find(".x").click(function() {
1987 self.close();
1988 });
1989
1990 if(this.duration > 0) {
1991 setTimeout(function() {
1992 self.close();
1993 }, this.duration);
1994 }
1995
1996 return this;
1997 }
1998
1999 mixin(Notification.prototype, EventEmitter.prototype);
2000 Notification.prototype.constructor = Notification;
2001
2002 Notification.prototype.position = function() {
2003 var pos = this.target.offset();
2004 var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
2005 var y = pos.top - this.domElement.height() - 8;
2006 var width = this.domElement.width();
2007 if(x + width > $("body").width()) {
2008 x -= ((x + width) - $("body").width());
2009 }
2010 if(x < 0) x = 0;
2011 this.domElement.offset({left: x, top: y});
2012 };
2013
2014 Notification.prototype.close = function() {
2015 var self = this;
2016 window.removeEventListener("resize", this.onresize);
2017 this.domElement.fadeOut(500, function() {
2018 self.domElement.remove();
2019 self.emit("close");
2020 });
2021 };
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037// set variables from settings or set settings
2038
2039////////////////////////////////////////////////////////////////
2040
2041 var gKeyboardSeq = 0;
2042 var gKnowsYouCanUseKeyboard = false;
2043 if(localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
2044 if(!gKnowsYouCanUseKeyboard) {
2045 window.gKnowsYouCanUseKeyboardTimeout = setTimeout(function() {
2046 window.gKnowsYouCanUseKeyboardNotification = new Notification({title: "Did you know!?!",
2047 text: "You can play the piano with your keyboard, too. Try it!", target: "#piano", duration: 10000});
2048 }, 30000);
2049 }
2050
2051
2052
2053
2054 if(window.localStorage) {
2055
2056 if(localStorage.volume) {
2057 volume_slider.value = localStorage.volume;
2058 gPiano.audio.setVolume(localStorage.volume);
2059 $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
2060 }
2061 else localStorage.volume = gPiano.audio.volume;
2062
2063 window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
2064 if(gHasBeenHereBefore) {
2065 }
2066 localStorage.gHasBeenHereBefore = true;
2067
2068 }
2069
2070
2071
2072
2073 // warn user about loud noises before starting sound (no autoplay)
2074 openModal("#sound-warning");
2075 var user_interact = function(evt) {
2076 document.removeEventListener("click", user_interact);
2077 closeModal();
2078 MPP.piano.audio.resume();
2079 }
2080 document.addEventListener("click", user_interact);
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094// New room, change room
2095
2096////////////////////////////////////////////////////////////////
2097
2098 $("#room > .info").text("--");
2099 gClient.on("ch", function(msg) {
2100 var channel = msg.ch;
2101 var info = $("#room > .info");
2102 info.text(channel._id);
2103 if(channel.settings.lobby) info.addClass("lobby");
2104 else info.removeClass("lobby");
2105 if(!channel.settings.chat) info.addClass("no-chat");
2106 else info.removeClass("no-chat");
2107 if(channel.settings.crownsolo) info.addClass("crownsolo");
2108 else info.removeClass("crownsolo");
2109 if(!channel.settings.visible) info.addClass("not-visible");
2110 else info.removeClass("not-visible");
2111 });
2112 gClient.on("ls", function(ls) {
2113 for(var i in ls.u) {
2114 if(!ls.u.hasOwnProperty(i)) continue;
2115 var room = ls.u[i];
2116 var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
2117 if(info.length == 0) {
2118 info = $("<div class=\"info\"></div>");
2119 info.attr("roomname", room._id);
2120 $("#room .more").append(info);
2121 }
2122 info.text(room._id + " (" + room.count + ")");
2123 if(room.settings.lobby) info.addClass("lobby");
2124 else info.removeClass("lobby");
2125 if(!room.settings.chat) info.addClass("no-chat");
2126 else info.removeClass("no-chat");
2127 if(room.settings.crownsolo) info.addClass("crownsolo");
2128 else info.removeClass("crownsolo");
2129 if(!room.settings.visible) info.addClass("not-visible");
2130 else info.removeClass("not-visible");
2131 if(room.banned) info.addClass("banned");
2132 else info.removeClass("banned");
2133 }
2134 });
2135 $("#room").on("click", function(evt) {
2136 evt.stopPropagation();
2137
2138 // clicks on a new room
2139 if($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
2140 $("#room .more").fadeOut(250);
2141 var selected_name = $(evt.target).attr("roomname");
2142 if(typeof selected_name != "undefined") {
2143 changeRoom(selected_name, "right");
2144 }
2145 return false;
2146 }
2147 // clicks on "New Room..."
2148 else if($(evt.target).hasClass("new")) {
2149 openModal("#new-room", "input[name=name]");
2150 }
2151 // all other clicks
2152 var doc_click = function(evt) {
2153 if($(evt.target).is("#room .more")) return;
2154 $(document).off("mousedown", doc_click);
2155 $("#room .more").fadeOut(250);
2156 gClient.sendArray([{m: "-ls"}]);
2157 }
2158 $(document).on("mousedown", doc_click);
2159 $("#room .more .info").remove();
2160 $("#room .more").show();
2161 gClient.sendArray([{m: "+ls"}]);
2162 });
2163 $("#new-room-btn").on("click", function(evt) {
2164 evt.stopPropagation();
2165 openModal("#new-room", "input[name=name]");
2166 });
2167
2168
2169 $("#play-alone-btn").on("click", function(evt) {
2170 evt.stopPropagation();
2171 var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
2172 changeRoom(room_name, "right", {"visible": false, "chat": true, "crownsolo": false});
2173 setTimeout(function() {
2174 new Notification({id: "share", title: "Playing alone", html: 'You are playing alone in a room by yourself, but you can always invite \
2175 friends by sending them the link.<br/><br/>\
2176 <a href="#" onclick="window.open(\'https://www.facebook.com/sharer/sharer.php?u=\'+encodeURIComponent(location.href),\'facebook-share-dialog\',\'width=626,height=436\');return false;">Share on Facebook</a><br/><br/>\
2177 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
2178 }, 1000);
2179 });
2180
2181
2182
2183 var gModal;
2184
2185 function modalHandleEsc(evt) {
2186 if(evt.keyCode == 27) {
2187 closeModal();
2188 evt.preventDefault();
2189 evt.stopPropagation();
2190 }
2191 };
2192
2193 function openModal(selector, focus) {
2194 if(chat) chat.blur();
2195 releaseKeyboard();
2196 $(document).on("keydown", modalHandleEsc);
2197 $("#modal #modals > *").hide();
2198 $("#modal").fadeIn(250);
2199 $(selector).show();
2200 setTimeout(function() {
2201 $(selector).find(focus).focus();
2202 }, 100);
2203 gModal = selector;
2204 };
2205
2206 function closeModal() {
2207 $(document).off("keydown", modalHandleEsc);
2208 $("#modal").fadeOut(100);
2209 $("#modal #modals > *").hide();
2210 captureKeyboard();
2211 gModal = null;
2212 };
2213
2214 var modal_bg = $("#modal .bg")[0];
2215 $(modal_bg).on("click", function(evt) {
2216 if(evt.target != modal_bg) return;
2217 closeModal();
2218 });
2219
2220 (function() {
2221 function submit() {
2222 var name = $("#new-room .text[name=name]").val();
2223 var settings = {
2224 visible: $("#new-room .checkbox[name=visible]").is(":checked"),
2225 chat: true,
2226 crownsolo: false
2227 };
2228 $("#new-room .text[name=name]").val("");
2229 closeModal();
2230 changeRoom(name, "right", settings);
2231 setTimeout(function() {
2232 new Notification({id: "share", title: "Created a Room", html: 'You can invite friends to your room by sending them the link.<br/><br/>\
2233 <a href="#" onclick="window.open(\'https://www.facebook.com/sharer/sharer.php?u=\'+encodeURIComponent(location.href),\'facebook-share-dialog\',\'width=626,height=436\');return false;">Share on Facebook</a><br/><br/>\
2234 <a href="http://twitter.com/home?status='+encodeURIComponent(location.href)+'" target="_blank">Tweet</a>', duration: 25000});
2235 }, 1000);
2236 };
2237 $("#new-room .submit").click(function(evt) {
2238 submit();
2239 });
2240 $("#new-room .text[name=name]").keypress(function(evt) {
2241 if(evt.keyCode == 13) {
2242 submit();
2243 } else if(evt.keyCode == 27) {
2244 closeModal();
2245 } else {
2246 return;
2247 }
2248 evt.preventDefault();
2249 evt.stopPropagation();
2250 return false;
2251 });
2252 })();
2253
2254
2255
2256
2257
2258
2259
2260
2261 function changeRoom(name, direction, settings, push) {
2262 if(!settings) settings = {};
2263 if(!direction) direction = "right";
2264 if(typeof push == "undefined") push = true;
2265 var opposite = direction == "left" ? "right" : "left";
2266
2267 if(name == "") name = "lobby";
2268 if(gClient.channel && gClient.channel._id === name) return;
2269 if(push) {
2270 var url = "/" + encodeURIComponent(name).replace("'", "%27");
2271 if(window.history && history.pushState) {
2272 history.pushState({"depth": gHistoryDepth += 1, "name": name}, "Piano > " + name, url);
2273 } else {
2274 window.location = url;
2275 return;
2276 }
2277 }
2278
2279 gClient.setChannel(name, settings);
2280
2281 var t = 0, d = 100;
2282 $("#piano").addClass("ease-out").addClass("slide-" + opposite);
2283 setTimeout(function() {
2284 $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
2285 }, t += d);
2286 setTimeout(function() {
2287 $("#piano").addClass("ease-in").removeClass("slide-" + direction);
2288 }, t += d);
2289 setTimeout(function() {
2290 $("#piano").removeClass("ease-in");
2291 }, t += d);
2292 };
2293
2294 var gHistoryDepth = 0;
2295 $(window).on("popstate", function(evt) {
2296 var depth = evt.state ? evt.state.depth : 0;
2297 if(depth == gHistoryDepth) return; // <-- forgot why I did that though...
2298
2299 var direction = depth <= gHistoryDepth ? "left" : "right";
2300 gHistoryDepth = depth;
2301
2302 var name = decodeURIComponent(window.location.pathname);
2303 if(name.substr(0, 1) == "/") name = name.substr(1);
2304 changeRoom(name, direction, null, false);
2305 });
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326// Rename
2327
2328////////////////////////////////////////////////////////////////
2329
2330(function() {
2331 function submit() {
2332 var set = {
2333 name: $("#rename input[name=name]").val(),
2334 color: $("#rename input[name=color]").val()
2335 };
2336 //$("#rename .text[name=name]").val("");
2337 closeModal();
2338 gClient.sendArray([{m: "userset", set: set}]);
2339 };
2340 $("#rename .submit").click(function(evt) {
2341 submit();
2342 });
2343 $("#rename .text[name=name]").keypress(function(evt) {
2344 if(evt.keyCode == 13) {
2345 submit();
2346 } else if(evt.keyCode == 27) {
2347 closeModal();
2348 } else {
2349 return;
2350 }
2351 evt.preventDefault();
2352 evt.stopPropagation();
2353 return false;
2354 });
2355 })();
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371// chatctor
2372
2373////////////////////////////////////////////////////////////////
2374
2375 var chat = (function() {
2376 gClient.on("ch", function(msg) {
2377 if(msg.ch.settings.chat) {
2378 chat.show();
2379 } else {
2380 chat.hide();
2381 }
2382 });
2383 gClient.on("disconnect", function(msg) {
2384 chat.hide();
2385 });
2386 gClient.on("c", function(msg) {
2387 chat.clear();
2388 if(msg.c) {
2389 for(var i = 0; i < msg.c.length; i++) {
2390 chat.receive(msg.c[i]);
2391 }
2392 }
2393 });
2394 gClient.on("a", function(msg) {
2395 chat.receive(msg);
2396 });
2397
2398 $("#chat input").on("focus", function(evt) {
2399 releaseKeyboard();
2400 $("#chat").addClass("chatting");
2401 chat.scrollToBottom();
2402 });
2403 /*$("#chat input").on("blur", function(evt) {
2404 captureKeyboard();
2405 $("#chat").removeClass("chatting");
2406 chat.scrollToBottom();
2407 });*/
2408 $(document).mousedown(function(evt) {
2409 if(!$("#chat").has(evt.target).length > 0) {
2410 chat.blur();
2411 }
2412 });
2413 document.addEventListener("touchstart", function(event) {
2414 for(var i in event.changedTouches) {
2415 var touch = event.changedTouches[i];
2416 if(!$("#chat").has(touch.target).length > 0) {
2417 chat.blur();
2418 }
2419 }
2420 });
2421 $(document).on("keydown", function(evt) {
2422 if($("#chat").hasClass("chatting")) {
2423 if(evt.keyCode == 27) {
2424 chat.blur();
2425 evt.preventDefault();
2426 evt.stopPropagation();
2427 } else if(evt.keyCode == 13) {
2428 $("#chat input").focus();
2429 }
2430 } else if(!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
2431 $("#chat input").focus();
2432 }
2433 });
2434 $("#chat input").on("keydown", function(evt) {
2435 if(evt.keyCode == 13) {
2436 var message = $(this).val();
2437 if(message.length == 0) {
2438 setTimeout(function() {
2439 chat.blur();
2440 }, 100);
2441 } else if(message.length <= 512) {
2442 chat.send(message);
2443 $(this).val("");
2444 setTimeout(function() {
2445 chat.blur();
2446 }, 100);
2447 }
2448 evt.preventDefault();
2449 evt.stopPropagation();
2450 } else if(evt.keyCode == 27) {
2451 chat.blur();
2452 evt.preventDefault();
2453 evt.stopPropagation();
2454 } else if(evt.keyCode == 9) {
2455 evt.preventDefault();
2456 evt.stopPropagation();
2457 }
2458 });
2459
2460 return {
2461 show: function() {
2462 $("#chat").fadeIn();
2463 },
2464
2465 hide: function() {
2466 $("#chat").fadeOut();
2467 },
2468
2469 clear: function() {
2470 $("#chat li").remove();
2471 },
2472
2473 scrollToBottom: function() {
2474 var ele = $("#chat ul").get(0);
2475 ele.scrollTop = ele.scrollHeight;
2476 },
2477
2478 blur: function() {
2479 if($("#chat").hasClass("chatting")) {
2480 $("#chat input").get(0).blur();
2481 $("#chat").removeClass("chatting");
2482 chat.scrollToBottom();
2483 captureKeyboard();
2484 }
2485 },
2486
2487 send: function(message) {
2488 gClient.sendArray([{m:"a", message: message}]);
2489 },
2490
2491 receive: function(msg) {
2492 if(gChatMutes.indexOf(msg.p._id) != -1) return;
2493
2494 var li = $('<li><span class="name"/><span class="message"/>');
2495
2496 li.find(".name").text(msg.p.name + ":");
2497 li.find(".message").text(msg.a);
2498 li.css("color", msg.p.color || "white");
2499
2500 $("#chat ul").append(li);
2501
2502 var eles = $("#chat ul li").get();
2503 for(var i = 1; i <= 50 && i <= eles.length; i++) {
2504 eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
2505 }
2506 if(eles.length > 50) {
2507 eles[0].style.display = "none";
2508 }
2509 if(eles.length > 256) {
2510 $(eles[0]).remove();
2511 }
2512
2513 // scroll to bottom if not "chatting" or if not scrolled up
2514 if(!$("#chat").hasClass("chatting")) {
2515 chat.scrollToBottom();
2516 } else {
2517 var ele = $("#chat ul").get(0);
2518 if(ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
2519 chat.scrollToBottom();
2520 }
2521 }
2522 };
2523 })();
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539// MIDI
2540
2541////////////////////////////////////////////////////////////////
2542
2543 var MIDI_TRANSPOSE = -12;
2544 var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
2545 var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
2546 for(var oct = 0; oct < 7; oct++) {
2547 for(var i in bare_notes) {
2548 MIDI_KEY_NAMES.push(bare_notes[i] + oct);
2549 }
2550 }
2551 MIDI_KEY_NAMES.push("c7");
2552
2553 var devices_json;
2554 function sendDevices() {
2555 gClient.sendArray([{"m": "devices", "list": JSON.parse(devices_json)}]);
2556 }
2557 gClient.on("connect", sendDevices);
2558
2559 (function() {
2560
2561 if (navigator.requestMIDIAccess) {
2562 navigator.requestMIDIAccess().then(
2563 function(midi) {
2564 console.log(midi);
2565 function midimessagehandler(evt) {
2566 if(!evt.target.enabled) return;
2567 //console.log(evt);
2568 var channel = evt.data[0] & 0xf;
2569 var cmd = evt.data[0] >> 4;
2570 var note_number = evt.data[1];
2571 var vel = evt.data[2];
2572 //console.log(channel, cmd, note_number, vel);
2573 if(cmd == 8 || (cmd == 9 && vel == 0)) {
2574 // NOTE_OFF
2575 release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
2576 } else if(cmd == 9) {
2577 // NOTE_ON
2578 press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
2579 } else if(cmd == 11) {
2580 // CONTROL_CHANGE
2581 if(!gAutoSustain) {
2582 if(note_number == 64) {
2583 if(vel > 0) {
2584 pressSustain();
2585 } else {
2586 releaseSustain();
2587 }
2588 }
2589 }
2590 }
2591 }
2592
2593 function deviceInfo(dev) {
2594 return {
2595 type: dev.type,
2596 //id: dev.id,
2597 manufacturer: dev.manufacturer,
2598 name: dev.name,
2599 version: dev.version,
2600 //connection: dev.connection,
2601 //state: dev.state,
2602 enabled: dev.enabled
2603 };
2604 }
2605
2606 function updateDevices() {
2607 var list = [];
2608 if(midi.inputs.size > 0) {
2609 var inputs = midi.inputs.values();
2610 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2611 var input = input_it.value;
2612 list.push(deviceInfo(input));
2613 }
2614 }
2615 if(midi.outputs.size > 0) {
2616 var outputs = midi.outputs.values();
2617 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2618 var output = output_it.value;
2619 list.push(deviceInfo(output));
2620 }
2621 }
2622 var new_json = JSON.stringify(list);
2623 if(new_json !== devices_json) {
2624 devices_json = new_json;
2625 sendDevices();
2626 }
2627 }
2628
2629 function plug() {
2630 if(midi.inputs.size > 0) {
2631 var inputs = midi.inputs.values();
2632 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2633 var input = input_it.value;
2634 //input.removeEventListener("midimessage", midimessagehandler);
2635 //input.addEventListener("midimessage", midimessagehandler);
2636 input.onmidimessage = midimessagehandler;
2637 if(input.enabled !== false) {
2638 input.enabled = true;
2639 }
2640 console.log("input", input);
2641 }
2642 }
2643 if(midi.outputs.size > 0) {
2644 var outputs = midi.outputs.values();
2645 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2646 var output = output_it.value;
2647 //output.enabled = false; // edit: don't touch
2648 console.log("output", output);
2649 }
2650 gMidiOutTest = function(note_name, vel, delay_ms) {
2651 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
2652 if(note_number == -1) return;
2653 note_number = note_number + 9 - MIDI_TRANSPOSE;
2654
2655 var outputs = midi.outputs.values();
2656 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2657 var output = output_it.value;
2658 if(output.enabled) {
2659 output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
2660 }
2661 }
2662 }
2663 }
2664 showConnections(false);
2665 updateDevices();
2666 }
2667
2668 midi.addEventListener("statechange", function(evt) {
2669 if(evt instanceof MIDIConnectionEvent) {
2670 plug();
2671 }
2672 });
2673
2674 plug();
2675
2676
2677 var connectionsNotification;
2678
2679 function showConnections(sticky) {
2680 //if(document.getElementById("Notification-MIDI-Connections"))
2681 //sticky = 1; // todo: instead,
2682 var inputs_ul = document.createElement("ul");
2683 if(midi.inputs.size > 0) {
2684 var inputs = midi.inputs.values();
2685 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2686 var input = input_it.value;
2687 var li = document.createElement("li");
2688 li.connectionId = input.id;
2689 li.classList.add("connection");
2690 if(input.enabled) li.classList.add("enabled");
2691 li.textContent = input.name;
2692 li.addEventListener("click", function(evt) {
2693 var inputs = midi.inputs.values();
2694 for(var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2695 var input = input_it.value;
2696 if(input.id === evt.target.connectionId) {
2697 input.enabled = !input.enabled;
2698 evt.target.classList.toggle("enabled");
2699 console.log("click", input);
2700 updateDevices();
2701 return;
2702 }
2703 }
2704 });
2705 inputs_ul.appendChild(li);
2706 }
2707 } else {
2708 inputs_ul.textContent = "(none)";
2709 }
2710 var outputs_ul = document.createElement("ul");
2711 if(midi.outputs.size > 0) {
2712 var outputs = midi.outputs.values();
2713 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2714 var output = output_it.value;
2715 var li = document.createElement("li");
2716 li.connectionId = output.id;
2717 li.classList.add("connection");
2718 if(output.enabled) li.classList.add("enabled");
2719 li.textContent = output.name;
2720 li.addEventListener("click", function(evt) {
2721 var outputs = midi.outputs.values();
2722 for(var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2723 var output = output_it.value;
2724 if(output.id === evt.target.connectionId) {
2725 output.enabled = !output.enabled;
2726 evt.target.classList.toggle("enabled");
2727 console.log("click", output);
2728 updateDevices();
2729 return;
2730 }
2731 }
2732 });
2733 outputs_ul.appendChild(li);
2734 }
2735 } else {
2736 outputs_ul.textContent = "(none)";
2737 }
2738 var div = document.createElement("div");
2739 var h1 = document.createElement("h1");
2740 h1.textContent = "Inputs";
2741 div.appendChild(h1);
2742 div.appendChild(inputs_ul);
2743 h1 = document.createElement("h1");
2744 h1.textContent = "Outputs";
2745 div.appendChild(h1);
2746 div.appendChild(outputs_ul);
2747 connectionsNotification = new Notification({"id":"MIDI-Connections", "title":"MIDI Connections","duration":sticky?"-1":"4500","html":div,"target":"#midi-btn"});
2748 }
2749
2750 document.getElementById("midi-btn").addEventListener("click", function(evt) {
2751 if(!document.getElementById("Notification-MIDI-Connections"))
2752 showConnections(true);
2753 else {
2754 connectionsNotification.close();
2755 }
2756 });
2757 },
2758 function(err){
2759 console.log(err);
2760 } );
2761 }
2762 })();
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777// bug supply
2778
2779////////////////////////////////////////////////////////////////
2780
2781 window.onerror = function(message, url, line) {
2782 var url = url || "(no url)";
2783 var line = line || "(no line)";
2784 // errors in socket.io
2785 if(url.indexOf("socket.io.js") !== -1) {
2786 if(message.indexOf("INVALID_STATE_ERR") !== -1) return;
2787 if(message.indexOf("InvalidStateError") !== -1) return;
2788 if(message.indexOf("DOM Exception 11") !== -1) return;
2789 if(message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
2790 if(message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
2791 if(message.indexOf("Cannot call method 'close' of null") !== -1) return;
2792 if(message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
2793 if(message.indexOf("Cannot call method 'payload' of null") !== -1) return;
2794 if(message.indexOf("Unable to get value of the property 'close'") !== -1) return;
2795 if(message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
2796 if(message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
2797 if(message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
2798 if(message.indexOf("this.transport is null") !== -1) return;
2799 }
2800 // errors in soundmanager2
2801 if(url.indexOf("soundmanager2.js") !== -1) {
2802 // operation disabled in safe mode?
2803 if(message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
2804 if(message.indexOf("_s.o._setVolume is not a function") !== -1) return;
2805 }
2806 // errors in midibridge
2807 if(url.indexOf("midibridge") !== -1) {
2808 if(message.indexOf("Error calling method on NPObject") !== -1) return;
2809 }
2810 // too many failing extensions injected in my html
2811 if(url.indexOf(".js") !== url.length - 3) return;
2812 // extensions inject cross-domain embeds too
2813 if(url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
2814
2815 // errors in my code
2816 if(url.indexOf("script.js") !== -1) {
2817 if(message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
2818 if(message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
2819 if(message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
2820 }
2821
2822 var enc = "/bugreport/"
2823 + (message ? encodeURIComponent(message) : "") + "/"
2824 + (url ? encodeURIComponent(url) : "") + "/"
2825 + (line ? encodeURIComponent(line) : "");
2826 var img = new Image();
2827 img.src = enc;
2828 };
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838 // API
2839 window.MPP = {
2840 press: press,
2841 release: release,
2842 piano: gPiano,
2843 client: gClient,
2844 chat: chat,
2845 noteQuota: gNoteQuota,
2846 soundSelector: gSoundSelector
2847 };
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858 // record mp3
2859 (function() {
2860 var button = document.querySelector("#record-btn");
2861 var audio = MPP.piano.audio;
2862 var context = audio.context;
2863 var encoder_sample_rate = 44100;
2864 var encoder_kbps = 128;
2865 var encoder = null;
2866 var scriptProcessorNode = context.createScriptProcessor(4096, 2, 2);
2867 var recording = false;
2868 var recording_start_time = 0;
2869 var mp3_buffer = [];
2870 button.addEventListener("click", function(evt) {
2871 if(!recording) {
2872 // start recording
2873 mp3_buffer = [];
2874 encoder = new lamejs.Mp3Encoder(2, encoder_sample_rate, encoder_kbps);
2875 scriptProcessorNode.onaudioprocess = onAudioProcess;
2876 audio.masterGain.connect(scriptProcessorNode);
2877 scriptProcessorNode.connect(context.destination);
2878 recording_start_time = Date.now();
2879 recording = true;
2880 button.textContent = "Stop Recording";
2881 button.classList.add("stuck");
2882 new Notification({"id": "mp3", "title": "Recording MP3...", "html": "It's recording now. This could make things slow, maybe. Maybe give it a moment to settle before playing.<br><br>This feature is experimental.<br>Send complaints to <a href=\"mailto:multiplayerpiano.com@gmail.com\">multiplayerpiano.com@gmail.com</a>.", "duration": 10000});
2883 } else {
2884 // stop recording
2885 var mp3buf = encoder.flush();
2886 mp3_buffer.push(mp3buf);
2887 var blob = new Blob(mp3_buffer, {type: "audio/mp3"});
2888 var url = URL.createObjectURL(blob);
2889 scriptProcessorNode.onaudioprocess = null;
2890 audio.masterGain.disconnect(scriptProcessorNode);
2891 scriptProcessorNode.disconnect(context.destination);
2892 recording = false;
2893 button.textContent = "Record MP3";
2894 button.classList.remove("stuck");
2895 new Notification({"id": "mp3", "title": "MP3 recording finished", "html": "<a href=\""+url+"\" target=\"blank\">And here it is!</a> (open or save as)<br><br>This feature is experimental.<br>Send complaints to <a href=\"mailto:multiplayerpiano.com@gmail.com\">multiplayerpiano.com@gmail.com</a>.", "duration": 0});
2896 }
2897 });
2898 function onAudioProcess(evt) {
2899 var inputL = evt.inputBuffer.getChannelData(0);
2900 var inputR = evt.inputBuffer.getChannelData(1);
2901 var mp3buf = encoder.encodeBuffer(convert16(inputL), convert16(inputR));
2902 mp3_buffer.push(mp3buf);
2903 }
2904 function convert16(samples) {
2905 var len = samples.length;
2906 var result = new Int16Array(len);
2907 for(var i = 0; i < len; i++) {
2908 result[i] = 0x8000 * samples[i];
2909 }
2910 return(result);
2911 }
2912 })();
2913
2914
2915
2916
2917
2918
2919
2920 // synth
2921 var enableSynth = false;
2922 var audio = gPiano.audio;
2923 var context = gPiano.audio.context;
2924 var synth_gain = context.createGain();
2925 synth_gain.gain.value = 0.05;
2926 synth_gain.connect(audio.synthGain);
2927
2928 var osc_types = ["sine", "square", "sawtooth", "triangle"];
2929 var osc_type_index = 1;
2930
2931 var osc1_type = "square";
2932 var osc1_attack = 0;
2933 var osc1_decay = 0.2;
2934 var osc1_sustain = 0.5;
2935 var osc1_release = 2.0;
2936
2937 function synthVoice(note_name, time) {
2938 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
2939 note_number = note_number + 9 - MIDI_TRANSPOSE;
2940 var freq = Math.pow(2, (note_number - 69) / 12) * 440.0;
2941 this.osc = context.createOscillator();
2942 this.osc.type = osc1_type;
2943 this.osc.frequency.value = freq;
2944 this.gain = context.createGain();
2945 this.gain.gain.value = 0;
2946 this.osc.connect(this.gain);
2947 this.gain.connect(synth_gain);
2948 this.osc.start(time);
2949 this.gain.gain.setValueAtTime(0, time);
2950 this.gain.gain.linearRampToValueAtTime(1, time + osc1_attack);
2951 this.gain.gain.linearRampToValueAtTime(osc1_sustain, time + osc1_attack + osc1_decay);
2952 }
2953
2954 synthVoice.prototype.stop = function(time) {
2955 //this.gain.gain.setValueAtTime(osc1_sustain, time);
2956 this.gain.gain.linearRampToValueAtTime(0, time + osc1_release);
2957 this.osc.stop(time + osc1_release);
2958 };
2959
2960 (function() {
2961 var button = document.getElementById("synth-btn");
2962 var notification;
2963
2964 button.addEventListener("click", function() {
2965 if(notification) {
2966 notification.close();
2967 } else {
2968 showSynth();
2969 }
2970 });
2971
2972 function showSynth() {
2973
2974 var html = document.createElement("div");
2975
2976 // on/off button
2977 (function() {
2978 var button = document.createElement("input");
2979 mixin(button, {type: "button", value: "ON/OFF", className: enableSynth ? "switched-on" : "switched-off"});
2980 button.addEventListener("click", function(evt) {
2981 enableSynth = !enableSynth;
2982 button.className = enableSynth ? "switched-on" : "switched-off";
2983 if(!enableSynth) {
2984 // stop all
2985 for(var i in audio.playings) {
2986 if(!audio.playings.hasOwnProperty(i)) continue;
2987 var playing = audio.playings[i];
2988 if(playing && playing.voice) {
2989 playing.voice.osc.stop();
2990 playing.voice = undefined;
2991 }
2992 }
2993 }
2994 });
2995 html.appendChild(button);
2996 })();
2997
2998 // mix
2999 var knob = document.createElement("canvas");
3000 mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
3001 html.appendChild(knob);
3002 knob = new Knob(knob, 0, 100, 0.1, 50, "mix", "%");
3003 knob.canvas.style.width = "32px";
3004 knob.canvas.style.height = "32px";
3005 knob.on("change", function(k) {
3006 var mix = k.value / 100;
3007 audio.pianoGain.gain.value = 1 - mix;
3008 audio.synthGain.gain.value = mix;
3009 });
3010 knob.emit("change", knob);
3011
3012 // osc1 type
3013 (function() {
3014 osc1_type = osc_types[osc_type_index];
3015 var button = document.createElement("input");
3016 mixin(button, {type: "button", value: osc_types[osc_type_index]});
3017 button.addEventListener("click", function(evt) {
3018 if(++osc_type_index >= osc_types.length) osc_type_index = 0;
3019 osc1_type = osc_types[osc_type_index];
3020 button.value = osc1_type;
3021 });
3022 html.appendChild(button);
3023 })();
3024
3025 // osc1 attack
3026 var knob = document.createElement("canvas");
3027 mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
3028 html.appendChild(knob);
3029 knob = new Knob(knob, 0, 1, 0.001, osc1_attack, "osc1 attack", "s");
3030 knob.canvas.style.width = "32px";
3031 knob.canvas.style.height = "32px";
3032 knob.on("change", function(k) {
3033 osc1_attack = k.value;
3034 });
3035 knob.emit("change", knob);
3036
3037 // osc1 decay
3038 var knob = document.createElement("canvas");
3039 mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
3040 html.appendChild(knob);
3041 knob = new Knob(knob, 0, 2, 0.001, osc1_decay, "osc1 decay", "s");
3042 knob.canvas.style.width = "32px";
3043 knob.canvas.style.height = "32px";
3044 knob.on("change", function(k) {
3045 osc1_decay = k.value;
3046 });
3047 knob.emit("change", knob);
3048
3049 var knob = document.createElement("canvas");
3050 mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
3051 html.appendChild(knob);
3052 knob = new Knob(knob, 0, 1, 0.001, osc1_sustain, "osc1 sustain", "x");
3053 knob.canvas.style.width = "32px";
3054 knob.canvas.style.height = "32px";
3055 knob.on("change", function(k) {
3056 osc1_sustain = k.value;
3057 });
3058 knob.emit("change", knob);
3059
3060 // osc1 release
3061 var knob = document.createElement("canvas");
3062 mixin(knob, {width: 32 * window.devicePixelRatio, height: 32 * window.devicePixelRatio, className: "knob"});
3063 html.appendChild(knob);
3064 knob = new Knob(knob, 0, 2, 0.001, osc1_release, "osc1 release", "s");
3065 knob.canvas.style.width = "32px";
3066 knob.canvas.style.height = "32px";
3067 knob.on("change", function(k) {
3068 osc1_release = k.value;
3069 });
3070 knob.emit("change", knob);
3071
3072
3073
3074 var div = document.createElement("div");
3075 div.innerHTML = "<br><br><br><br><center>this space intentionally left blank</center><br><br><br><br>";
3076 html.appendChild(div);
3077
3078
3079
3080 // notification
3081 notification = new Notification({title: "Synthesize", html: html, duration: -1, target: "#synth-btn"});
3082 notification.on("close", function() {
3083 var tip = document.getElementById("tooltip");
3084 if(tip) tip.parentNode.removeChild(tip);
3085 notification = null;
3086 });
3087 }
3088 })();
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104});
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124// misc
3125
3126////////////////////////////////////////////////////////////////
3127
3128// analytics
3129window.google_analytics_uacct = "UA-882009-7";
3130var _gaq = _gaq || [];
3131_gaq.push(['_setAccount', 'UA-882009-7']);
3132_gaq.push(['_trackPageview']);
3133_gaq.push(['_setAllowAnchor', true]);
3134(function() {
3135 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
3136 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
3137 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
3138})();
3139
3140// twitter
3141!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;
3142 js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
3143
3144// fb
3145(function(d, s, id) {
3146 var js, fjs = d.getElementsByTagName(s)[0];
3147 if (d.getElementById(id)) return;
3148 js = d.createElement(s); js.id = id;
3149 js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";
3150 fjs.parentNode.insertBefore(js, fjs);
3151}(document, 'script', 'facebook-jssdk'));
3152
3153// non-ad-free experience
3154/*(function() {
3155 function adsOn() {
3156 if(window.localStorage) {
3157 var div = document.querySelector("#inclinations");
3158 div.innerHTML = "Ads:<br>ON / <a id=\"adsoff\" href=\"#\">OFF</a>";
3159 div.querySelector("#adsoff").addEventListener("click", adsOff);
3160 localStorage.ads = true;
3161 }
3162 // adsterra
3163 var script = document.createElement("script");
3164 script.src = "//pl132070.puhtml.com/68/7a/97/687a978dd26d579c788cb41e352f5a41.js";
3165 document.head.appendChild(script);
3166 }
3167
3168 function adsOff() {
3169 if(window.localStorage) localStorage.ads = false;
3170 document.location.reload(true);
3171 }
3172
3173 function noAds() {
3174 var div = document.querySelector("#inclinations");
3175 div.innerHTML = "Ads:<br><a id=\"adson\" href=\"#\">ON</a> / OFF";
3176 div.querySelector("#adson").addEventListener("click", adsOn);
3177 }
3178
3179 if(window.localStorage) {
3180 if(localStorage.ads === undefined || localStorage.ads === "true")
3181 adsOn();
3182 else
3183 noAds();
3184 } else {
3185 adsOn();
3186 }
3187})();*/
3188
3189(function() {
3190 var script = document.createElement('script');
3191 script.onload = function() {
3192 var stats = new Stats();
3193 stats.domElement.style.cssText = ' position: fixed; top: 50px; right: 28px; z-index:10000';
3194 document.body.appendChild(stats.domElement);
3195 requestAnimationFrame(function loop() {
3196 stats.update();
3197 requestAnimationFrame(loop)
3198 });
3199 };
3200 script.src = '//rawgit.com/mrdoob/stats.js/master/build/stats.min.js';
3201 document.head.appendChild(script);
3202})()
3203
3204
3205
3206var cantEatM = ["air","eat","/eat", "cant eat", "cant do that...", "Sorry you can't do that...", "pussy","dick","ass","water","liquid","shit","Shit"]
3207var canEatY = ["steak",' ',"cheeseburger",' ',"curry",' ',"chips",' ',"cheese cake",' ',"cum",' ',"cherries",' ',"apples",' ',"pizza",' ',"crisps",' ',"cheese",' ',"banana",' ',"fish"];
3208
3209MPP.client.on("a",function(msg){
3210 var args = msg.a.split(" ");
3211 var cmd = args[0].toLowerCase();
3212 var name = msg.p.name;
3213 var names = msg.p;
3214 var selfRoom = "Your name"; //Change to what ever
3215 var selfName = "Your name"; //Change to your name
3216 var selfId = "db571c12dca5ab0f88b8931d"; //Change to your ID.
3217 var lSeeOwnCursor = true; //Ignore
3218//MAKE COMMANDS BELOW//
3219//IF ANY ERRORS CHECK CONSOLE//
3220if (cmd == "!test") MPP.chat.send(""+ msg.p.name +" Working! Bot made by Fennece :3");
3221if (cmd == "!room") { if (msg.p.name == ""+ selfName +"") { MPP.client.setChannel("" + selfRoom +""); } }
3222if (cmd == "/chara") { MPP.chat.send(""); }
3223if (cmd == "") { MPP.chat.send(""); }
3224if (cmd == "") { MPP.chat.send(""); }
3225
3226 if (cmd == "/menu") {
3227 MPP.chat.send("Here is the menu for the /eat command: " + canEatY + "");
3228 }
3229
3230 if (cmd == "/eat") {
3231 if (msg.a.substring(cmd.length) == "") {
3232 MPP.chat.send("Input something to eat " + msg.p.name)
3233 } else {
3234 if (cantEatM.includes(msg.a.substring(cmd.length).trim())) {
3235 MPP.chat.send("You can't do that, sorry...");
3236
3237 } else {
3238 if (!canEatY.includes(msg.a.substring(cmd.length).trim())) {
3239 MPP.chat.send("That is not on our menu! Type /menu for more details... (also please recommend me some food to add in pls thx)");
3240 } else {
3241 MPP.chat.send(msg.p.name + " ate " + msg.a.substring(cmd.length) + ".");
3242 }
3243 }
3244 }
3245 }
3246
3247
3248});
3249
3250//END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT// //END OF SCRIPT//