· 5 years ago · May 11, 2020, 09:44 PM
1// ==UserScript==
2// @name Phoenix's Backup Script
3// @namespace https://www.multiplayerpiano.com/
4// @version 13.5
5// @description Script for MPP
6// @include https://www.multiplayerpiano.com/*
7// @author Fennece
8// @match http://mpp-evolution.com/*
9// @match http://cursors.me/piano/*
10// @match https://www.multiplayerpiano.com/*
11// @grant 2020+
12// ==/UserScript==
13// 钢琴
14$(function() {
15 //Notes are taken throughout the script.
16 var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i));
17 //Variables For Guess game
18 var gSeeOwnCursor = false;
19 /* vvvvv These tags need to be renamed to create a new button element. You need to do it every time you make a new button. */
20 $("body").append('<div id="clear-btn" class="ugly-button" style="bottom: 7px; left: 660px; position: fixed; z-index: 500;">Clear Chat</div>');
21 $("#clear-btn").on("click", function(evt) {
22 MPP.chat.clear()
23 });
24
25
26 $("body").append('<div id="hidechat-btn" class="ugly-button" style="bottom: 7px; left: 780px; position: fixed; z-index: 500;">Hide Chat</div>');
27 $("#hidechat-btn").on("click", function(evt) {
28 if (piano) {
29 piano = false;
30 MPP.chat.hide();
31 } else {
32 piano = true;
33 MPP.chat.show();
34 }
35 });
36 $("body").append('<div id="hidepiano-btn" class="ugly-button" style="bottom: 35px; left: 900px; position: fixed; z-index: 500;">Hide Piano</div>');
37 $("#hidepiano-btn").on("click", function(evt) {
38 if (piano) {
39 piano = false;
40 $("#piano").hide();
41 } else {
42 piano = true;
43 $("#piano").show();
44 }
45
46 });
47
48
49 var gMidiOutTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midiout(?:#.+)*$/i));
50
51 if (!Array.prototype.indexOf) {
52 Array.prototype.indexOf = function(elt /*, from*/ ) {
53 var len = this.length >>> 0;
54 var from = Number(arguments[1]) || 0;
55 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
56 if (from < 0) from += len;
57 for (; from < len; from++) {
58 if (from in this && this[from] === elt) return from;
59 }
60 return -1;
61 };
62 }
63
64 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
65 window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
66 function(cb) {
67 setTimeout(cb, 1000 / 30);
68 };
69
70
71
72
73 var DEFAULT_VELOCITY = 0.5;
74
75
76
77
78 var TIMING_TARGET = 1000;
79
80
81
82
83 // Utility
84
85 ////////////////////////////////////////////////////////////////
86
87
88
89 var Rect = function(x, y, w, h) {
90 this.x = x;
91 this.y = y;
92 this.w = w;
93 this.h = h;
94 this.x2 = x + w;
95 this.y2 = y + h;
96 };
97 Rect.prototype.contains = function(x, y) {
98 return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2);
99 };
100
101
102
103
104 // performing translation
105
106 ////////////////////////////////////////////////////////////////
107
108 var Translation = (function() {
109 var strings = {
110 "people are playing": {
111 "pt": "pessoas estão jogando",
112 "es": "personas están jugando",
113 "ru": "человек играет",
114 "fr": "personnes jouent",
115 "ja": "人が遊んでいる",
116 "de": "Leute spielen",
117 "zh": "人在玩",
118 "nl": "mensen spelen",
119 "pl": "osób grają",
120 "hu": "ember játszik"
121 },
122 "New Room...": {
123 "pt": "Nova Sala ...",
124 "es": "Nueva sala de...",
125 "ru": "Новый номер...",
126 "ja": "新しい部屋",
127 "zh": "新房间",
128 "nl": "nieuwe Kamer",
129 "hu": "új szoba"
130 },
131 "room name": {
132 "pt": "nome da sala",
133 "es": "sala de nombre",
134 "ru": "название комнаты",
135 "fr": "nom de la chambre",
136 "ja": "ルーム名",
137 "de": "Raumnamen",
138 "zh": "房间名称",
139 "nl": "kamernaam",
140 "pl": "nazwa pokój",
141 "hu": "szoba neve"
142 },
143 "Visible (open to everyone)": {
144 "pt": "Visível (aberto a todos)",
145 "es": "Visible (abierto a todo el mundo)",
146 "ru": "Visible (открытый для всех)",
147 "fr": "Visible (ouvert à tous)",
148 "ja": "目に見える(誰にでも開いている)",
149 "de": "Sichtbar (offen für alle)",
150 "zh": "可见(向所有人开放)",
151 "nl": "Zichtbaar (open voor iedereen)",
152 "pl": "Widoczne (otwarte dla wszystkich)",
153 "hu": "Látható (nyitott mindenki számára)"
154 },
155 "Enable Chat": {
156 "pt": "Ativar bate-papo",
157 "es": "Habilitar chat",
158 "ru": "Включить чат",
159 "fr": "Activer discuter",
160 "ja": "チャットを有効にする",
161 "de": "aktivieren Sie chatten",
162 "zh": "启用聊天",
163 "nl": "Chat inschakelen",
164 "pl": "Włącz czat",
165 "hu": "a csevegést"
166 },
167 "Play Alone": {
168 "pt": "Jogar Sozinho",
169 "es": "Jugar Solo",
170 "ru": "Играть в одиночку",
171 "fr": "Jouez Seul",
172 "ja": "一人でプレイ",
173 "de": "Alleine Spielen",
174 "zh": "独自玩耍",
175 "nl": "Speel Alleen",
176 "pl": "Zagraj sam",
177 "hu": "Játssz egyedül"
178 }
179 // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr
180 // todo: Connecting, Offline mode, input placeholder, Notifications
181 };
182
183 var setLanguage = function(lang) {
184 language = lang
185 };
186
187 var getLanguage = function() {
188 if (window.navigator && navigator.language && navigator.language.length >= 2) {
189 return navigator.language.substr(0, 2).toLowerCase();
190 } else {
191 return "en";
192 }
193 };
194
195 var get = function(text, lang) {
196 if (typeof lang === "undefined") lang = language;
197 var row = strings[text];
198 if (row == undefined) return text;
199 var string = row[lang];
200 if (string == undefined) return text;
201 return string;
202 };
203
204 var perform = function(lang) {
205 if (typeof lang === "undefined") lang = language;
206 $(".translate").each(function(i, ele) {
207 var th = $(this);
208 if (ele.tagName && ele.tagName.toLowerCase() == "input") {
209 if (typeof ele.placeholder != "undefined") {
210 th.attr("placeholder", get(th.attr("placeholder"), lang))
211 }
212 } else {
213 th.text(get(th.text(), lang));
214 }
215 });
216 };
217
218 var language = getLanguage();
219
220 return {
221 setLanguage: setLanguage,
222 getLanguage: getLanguage,
223 get: get,
224 perform: perform
225 };
226 })();
227
228 Translation.perform();
229
230
231
232 // AudioEngine classes
233
234 ////////////////////////////////////////////////////////////////
235
236 var AudioEngine = function() {};
237
238 AudioEngine.prototype.init = function(cb) {
239 this.volume = 0.6;
240 this.sounds = {};
241 this.paused = true;
242 return this;
243 };
244
245 AudioEngine.prototype.load = function(id, url, cb) {};
246
247 AudioEngine.prototype.play = function() {};
248
249 AudioEngine.prototype.stop = function() {};
250
251 AudioEngine.prototype.setVolume = function(vol) {
252 this.volume = vol;
253 };
254
255 AudioEngine.prototype.resume = function() {
256 this.paused = false;
257 };
258
259
260 AudioEngineWeb = function() {
261 this.threshold = 1000;
262 this.worker = new Worker("/workerTimer.js");
263 var self = this;
264 this.worker.onmessage = function(event) {
265 if (event.data.args)
266 if (event.data.args.action == 0) {
267 self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id);
268 }
269 else {
270 self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id);
271 }
272 }
273 };
274
275 AudioEngineWeb.prototype = new AudioEngine();
276
277 AudioEngineWeb.prototype.init = function(cb) {
278 AudioEngine.prototype.init.call(this);
279
280 this.context = new AudioContext();
281
282 this.masterGain = this.context.createGain();
283 this.masterGain.connect(this.context.destination);
284 this.masterGain.gain.value = this.volume;
285
286 this.limiterNode = this.context.createDynamicsCompressor();
287 this.limiterNode.threshold.value = -10;
288 this.limiterNode.knee.value = 0;
289 this.limiterNode.ratio.value = 20;
290 this.limiterNode.attack.value = 0;
291 this.limiterNode.release.value = 0.1;
292 this.limiterNode.connect(this.masterGain);
293
294 // for synth mix
295 this.pianoGain = this.context.createGain();
296 this.pianoGain.gain.value = 0.5;
297 this.pianoGain.connect(this.limiterNode);
298 this.synthGain = this.context.createGain();
299 this.synthGain.gain.value = 0.5;
300 this.synthGain.connect(this.limiterNode);
301
302 this.playings = {};
303
304 if (cb) setTimeout(cb, 0);
305 return this;
306 };
307
308 AudioEngineWeb.prototype.load = function(id, url, cb) {
309 var audio = this;
310 var req = new XMLHttpRequest();
311 req.open("GET", url);
312 req.responseType = "arraybuffer";
313 req.addEventListener("readystatechange", function(evt) {
314 if (req.readyState !== 4) return;
315 try {
316 audio.context.decodeAudioData(req.response, function(buffer) {
317 audio.sounds[id] = buffer;
318 if (cb) cb();
319 });
320 } catch (e) {
321 /*throw new Error(e.message
322 + " / id: " + id
323 + " / url: " + url
324 + " / status: " + req.status
325 + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer)
326 + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/
327 new Notification({
328 id: "audio-download-error",
329 title: "Problem",
330 text: "For some reason, an audio download failed with a status of " + req.status + ". ",
331 target: "#piano",
332 duration: 10000
333 });
334 }
335 });
336 req.send();
337 };
338
339 AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms.
340 if (this.paused) return;
341 if (!this.sounds.hasOwnProperty(id)) return;
342 var source = this.context.createBufferSource();
343 source.buffer = this.sounds[id];
344 var gain = this.context.createGain();
345 gain.gain.value = vol;
346 source.connect(gain);
347 gain.connect(this.pianoGain);
348 source.start(time);
349 // Patch from ste-art remedies stuttering under heavy load
350 if (this.playings[id]) {
351 var playing = this.playings[id];
352 playing.gain.gain.setValueAtTime(playing.gain.gain.value, time);
353 playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2);
354 playing.source.stop(time + 0.21);
355 if (enableSynth && playing.voice) {
356 playing.voice.stop(time);
357 }
358 }
359 this.playings[id] = {
360 "source": source,
361 "gain": gain,
362 "part_id": part_id
363 };
364
365 if (enableSynth) {
366 this.playings[id].voice = new synthVoice(id, time);
367 }
368 }
369
370 AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id) {
371 if (!this.sounds.hasOwnProperty(id)) return;
372 var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive.
373 var delay = delay_ms - this.threshold;
374 if (delay <= 0) this.actualPlay(id, vol, time, part_id);
375 else {
376 this.worker.postMessage({
377 delay: delay,
378 args: {
379 action: 0 /*play*/ ,
380 id: id,
381 vol: vol,
382 time: time,
383 part_id: part_id
384 }
385 }); // but start scheduling right before play.
386 }
387 }
388
389 AudioEngineWeb.prototype.actualStop = function(id, time, part_id) {
390 if (this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) {
391 var gain = this.playings[id].gain.gain;
392 gain.setValueAtTime(gain.value, time);
393 gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16);
394 gain.linearRampToValueAtTime(0.0, time + 0.4);
395 this.playings[id].source.stop(time + 0.41);
396
397
398 if (this.playings[id].voice) {
399 this.playings[id].voice.stop(time);
400 }
401
402 this.playings[id] = null;
403 }
404 };
405
406 AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) {
407 var time = this.context.currentTime + (delay_ms / 1000);
408 var delay = delay_ms - this.threshold;
409 if (delay <= 0) this.actualStop(id, time, part_id);
410 else {
411 this.worker.postMessage({
412 delay: delay,
413 args: {
414 action: 1 /*stop*/ ,
415 id: id,
416 time: time,
417 part_id: part_id
418 }
419 });
420 }
421 };
422
423 AudioEngineWeb.prototype.setVolume = function(vol) {
424 AudioEngine.prototype.setVolume.call(this, vol);
425 this.masterGain.gain.value = this.volume;
426 };
427
428 AudioEngineWeb.prototype.resume = function() {
429 this.paused = false;
430 this.context.resume();
431 };
432
433
434
435
436 // Renderer classes
437
438 ////////////////////////////////////////////////////////////////
439
440 var Renderer = function() {};
441
442 Renderer.prototype.init = function(piano) {
443 this.piano = piano;
444 this.resize();
445 return this;
446 };
447
448 Renderer.prototype.resize = function(width, height) {
449 if (typeof width == "undefined") width = $(this.piano.rootElement).width();
450 if (typeof height == "undefined") height = Math.floor(width * 0.2);
451 $(this.piano.rootElement).css({
452 "height": height + "px",
453 marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"
454 });
455 this.width = width * window.devicePixelRatio;
456 this.height = height * window.devicePixelRatio;
457 };
458
459 Renderer.prototype.visualize = function(key, color) {};
460
461
462
463
464 var CanvasRenderer = function() {
465 Renderer.call(this);
466 };
467
468 CanvasRenderer.prototype = new Renderer();
469
470 CanvasRenderer.prototype.init = function(piano) {
471 this.canvas = document.createElement("canvas");
472 this.ctx = this.canvas.getContext("2d");
473 piano.rootElement.appendChild(this.canvas);
474
475 Renderer.prototype.init.call(this, piano); // calls resize()
476
477 // create render loop
478 var self = this;
479 var render = function() {
480 self.redraw();
481 requestAnimationFrame(render);
482 };
483 requestAnimationFrame(render);
484
485 // add event listeners
486 var mouse_down = false;
487 var last_key = null;
488 $(piano.rootElement).mousedown(function(event) {
489 mouse_down = true;
490 //event.stopPropagation();
491 event.preventDefault();
492
493 var pos = CanvasRenderer.translateMouseEvent(event);
494 var hit = self.getHit(pos.x, pos.y);
495 if (hit) {
496 press(hit.key.note, hit.v);
497 last_key = hit.key;
498 }
499 });
500 piano.rootElement.addEventListener("touchstart", function(event) {
501 mouse_down = true;
502 //event.stopPropagation();
503 event.preventDefault();
504 for (var i in event.changedTouches) {
505 var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]);
506 var hit = self.getHit(pos.x, pos.y);
507 if (hit) {
508 press(hit.key.note, hit.v);
509 last_key = hit.key;
510 }
511 }
512 }, false);
513 $(window).mouseup(function(event) {
514 if (last_key) {
515 release(last_key.note);
516 }
517 mouse_down = false;
518 last_key = null;
519 });
520 /*$(piano.rootElement).mousemove(function(event) {
521 if(!mouse_down) return;
522 var pos = CanvasRenderer.translateMouseEvent(event);
523 var hit = self.getHit(pos.x, pos.y);
524 if(hit && hit.key != last_key) {
525 press(hit.key.note, hit.v);
526 last_key = hit.key;
527 }
528 });*/
529
530 return this;
531 };
532
533 CanvasRenderer.prototype.resize = function(width, height) {
534 Renderer.prototype.resize.call(this, width, height);
535 if (this.width < 52 * 2) this.width = 52 * 2;
536 if (this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2);
537 this.canvas.width = this.width;
538 this.canvas.height = this.height;
539 this.canvas.style.width = this.width / window.devicePixelRatio + "px";
540 this.canvas.style.height = this.height / window.devicePixelRatio + "px";
541
542 // calculate key sizes
543 this.whiteKeyWidth = Math.floor(this.width / 52);
544 this.whiteKeyHeight = Math.floor(this.height * 0.9);
545 this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75);
546 this.blackKeyHeight = Math.floor(this.height * 0.5);
547
548 this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2));
549 this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015);
550
551 this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7);
552 this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8);
553 this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2);
554 this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2);
555 this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7);
556 this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8);
557 this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2);
558 this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2);
559
560 // prerender white key
561 this.whiteKeyRender = document.createElement("canvas");
562 this.whiteKeyRender.width = this.whiteKeyWidth;
563 this.whiteKeyRender.height = this.height + 10;
564 var ctx = this.whiteKeyRender.getContext("2d");
565 if (ctx.createLinearGradient) {
566 var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight);
567 gradient.addColorStop(0.15, "#e642f5"); // Top half of piano
568 gradient.addColorStop(0.30, "#7845f7"); //Middle half
569 gradient.addColorStop(0.55, "#f745c5"); //Bottom half
570 gradient.addColorStop(0.75, "#f745df");
571 gradient.addColorStop(1, "#0011ff");
572 ctx.fillStyle = gradient;
573 } else {
574 ctx.fillStyle = "#fff";
575 }
576 ctx.strokeStyle = "#000";
577 ctx.lineJoin = "round";
578 ctx.lineCap = "round";
579 ctx.lineWidth = 10;
580 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
581 ctx.lineWidth = 4;
582 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
583
584 // prerender black key
585 this.blackKeyRender = document.createElement("canvas");
586 this.blackKeyRender.width = this.blackKeyWidth + 10;
587 this.blackKeyRender.height = this.blackKeyHeight + 10;
588 var ctx = this.blackKeyRender.getContext("2d");
589 if (ctx.createLinearGradient) {
590 var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight);
591 gradient.addColorStop(0, "#000");
592 gradient.addColorStop(1, "#444");
593 ctx.fillStyle = gradient;
594 } else {
595 ctx.fillStyle = "#000";
596 }
597 ctx.strokeStyle = "#222";
598 ctx.lineJoin = "round";
599 ctx.lineCap = "round";
600 ctx.lineWidth = 8;
601 ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
602 ctx.lineWidth = 4;
603 ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
604
605 // prerender shadows
606 this.shadowRender = [];
607 var y = -this.canvas.height * 2;
608 for (var j = 0; j < 2; j++) {
609 var canvas = document.createElement("canvas");
610 this.shadowRender[j] = canvas;
611 canvas.width = this.canvas.width;
612 canvas.height = this.canvas.height;
613 var ctx = canvas.getContext("2d");
614 var sharp = j ? true : false;
615 ctx.lineJoin = "round";
616 ctx.lineCap = "round";
617 ctx.lineWidth = 1;
618 ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
619 ctx.shadowBlur = this.keyMovement * 3;
620 ctx.shadowOffsetY = -y + this.keyMovement;
621 if (sharp) {
622 ctx.shadowOffsetX = this.keyMovement;
623 } else {
624 ctx.shadowOffsetX = 0;
625 ctx.shadowOffsetY = -y + this.keyMovement;
626 }
627 for (var i in this.piano.keys) {
628 if (!this.piano.keys.hasOwnProperty(i)) continue;
629 var key = this.piano.keys[i];
630 if (key.sharp != sharp) continue;
631
632 if (key.sharp) {
633 ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
634 y + ctx.lineWidth / 2,
635 this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth);
636 } else {
637 ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2,
638 y + ctx.lineWidth / 2,
639 this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth);
640 }
641 }
642 }
643
644 // update key rects
645 for (var i in this.piano.keys) {
646 if (!this.piano.keys.hasOwnProperty(i)) continue;
647 var key = this.piano.keys[i];
648 if (key.sharp) {
649 key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0,
650 this.blackKeyWidth, this.blackKeyHeight);
651 } else {
652 key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0,
653 this.whiteKeyWidth, this.whiteKeyHeight);
654 }
655 }
656 };
657
658 CanvasRenderer.prototype.visualize = function(key, color) {
659 key.timePlayed = Date.now();
660 key.blips.push({
661 "time": key.timePlayed,
662 "color": color
663 });
664 };
665
666 CanvasRenderer.prototype.redraw = function() {
667 var now = Date.now();
668 var timeLoadedEnd = now - 1000;
669 var timePlayedEnd = now - 100;
670 var timeBlipEnd = now - 1000;
671
672 this.ctx.save();
673 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
674 // draw all keys
675 for (var j = 0; j < 2; j++) {
676 this.ctx.globalAlpha = 1.0;
677 this.ctx.drawImage(this.shadowRender[j], 0, 0);
678 var sharp = j ? true : false;
679 for (var i in this.piano.keys) {
680 if (!this.piano.keys.hasOwnProperty(i)) continue;
681 var key = this.piano.keys[i];
682 if (key.sharp != sharp) continue;
683
684 if (!key.loaded) {
685 this.ctx.globalAlpha = 0.2;
686 } else if (key.timeLoaded > timeLoadedEnd) {
687 this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2;
688 } else {
689 this.ctx.globalAlpha = 1.0;
690 }
691 var y = 0;
692 if (key.timePlayed > timePlayedEnd) {
693 y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement));
694 }
695 var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial :
696 this.whiteKeyWidth * key.spatial);
697 var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender;
698 this.ctx.drawImage(image, x, y);
699
700 // render blips
701 if (key.blips.length) {
702 var alpha = this.ctx.globalAlpha;
703 var w, h;
704 if (key.sharp) {
705 x += this.blackBlipX;
706 y = this.blackBlipY;
707 w = this.blackBlipWidth;
708 h = this.blackBlipHeight;
709 } else {
710 x += this.whiteBlipX;
711 y = this.whiteBlipY;
712 w = this.whiteBlipWidth;
713 h = this.whiteBlipHeight;
714 }
715 for (var b = 0; b < key.blips.length; b++) {
716 var blip = key.blips[b];
717 if (blip.time > timeBlipEnd) {
718 this.ctx.fillStyle = blip.color;
719 this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000);
720 this.ctx.fillRect(x, y, w, h);
721 } else {
722 key.blips.splice(b, 1);
723 --b;
724 }
725 y -= Math.floor(h * 1.1);
726 }
727 }
728 }
729 }
730 this.ctx.restore();
731 };
732
733 CanvasRenderer.prototype.getHit = function(x, y) {
734 for (var j = 0; j < 2; j++) {
735 var sharp = j ? false : true; // black keys first
736 for (var i in this.piano.keys) {
737 if (!this.piano.keys.hasOwnProperty(i)) continue;
738 var key = this.piano.keys[i];
739 if (key.sharp != sharp) continue;
740 if (key.rect.contains(x, y)) {
741 var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight);
742 v += 0.25;
743 v *= DEFAULT_VELOCITY;
744 if (v > 1.0) v = 1.0;
745 return {
746 "key": key,
747 "v": v
748 };
749 }
750 }
751 }
752 return null;
753 };
754
755
756 CanvasRenderer.isSupported = function() {
757 var canvas = document.createElement("canvas");
758 return !!(canvas.getContext && canvas.getContext("2d"));
759 };
760
761 CanvasRenderer.translateMouseEvent = function(evt) {
762 var element = evt.target;
763 var offx = 0;
764 var offy = 0;
765 do {
766 if (!element) break; // wtf, wtf?
767 offx += element.offsetLeft;
768 offy += element.offsetTop;
769 } while (element = element.offsetParent);
770 return {
771 x: (evt.pageX - offx) * window.devicePixelRatio,
772 y: (evt.pageY - offy) * window.devicePixelRatio
773 }
774 };
775
776
777
778
779 // Soundpack Stuff by electrashave ♥
780
781 ////////////////////////////////////////////////////////////////
782
783 function SoundSelector(piano) {
784 this.initialized = false;
785 this.keys = piano.keys;
786 this.loading = {};
787 this.notification;
788 this.packs = [];
789 this.piano = piano;
790 this.soundSelection = localStorage.soundSelection || "MPP Classic";
791 this.addPack({
792 name: "MPP Classic",
793 keys: Object.keys(this.piano.keys),
794 ext: ".mp3",
795 url: "/sounds/mppclassic/"
796 });
797 }
798
799 SoundSelector.prototype.addPack = function(pack, load) {
800 var self = this;
801 self.loading[pack.url || pack] = true;
802
803 function add(obj) {
804 var added = false;
805 for (var i = 0; self.packs.length > i; i++) {
806 if (obj.name == self.packs[i].name) {
807 added = true;
808 break;
809 }
810 }
811
812 if (added) return console.warn("Sounds already added!!"); //no adding soundpacks twice D:<
813
814 if (obj.url.substr(obj.url.length - 1) != "/") obj.url = obj.url + "/";
815 var html = document.createElement("li");
816 html.classList = "pack";
817 html.innerText = obj.name + " (" + obj.keys.length + " keys)";
818 html.onclick = function() {
819 self.loadPack(obj.name);
820 self.notification.close();
821 };
822 obj.html = html;
823 self.packs.push(obj);
824 self.packs.sort(function(a, b) {
825 if (a.name < b.name) return -1;
826 if (a.name > b.name) return 1;
827 return 0;
828 });
829 if (load) self.loadPack(obj.name);
830 delete self.loading[obj.url];
831 }
832
833 if (typeof pack == "string") {
834 $.getJSON(pack + "/info.json").done(function(json) {
835 json.url = pack;
836 add(json);
837 });
838 } else add(pack); //validate packs??
839 };
840
841 SoundSelector.prototype.addPacks = function(packs) {
842 for (var i = 0; packs.length > i; i++) this.addPack(packs[i]);
843 };
844
845 SoundSelector.prototype.init = function() {
846 var self = this;
847 if (self.initialized) return console.warn("Sound selector already initialized!");
848
849 if (!!Object.keys(self.loading).length) return setTimeout(function() {
850 self.init();
851 }, 250);
852
853 $("#sound-btn").on("click", function() {
854 if (document.getElementById("Notification-Sound-Selector") != null) return self.notification.close();
855 var html = document.createElement("ul");
856 $(html).append("<h1>Current Sound: " + self.soundSelection + "</h1>");
857
858 for (var i = 0; self.packs.length > i; i++) {
859 var pack = self.packs[i];
860 if (pack.name == self.soundSelection) pack.html.classList = "pack enabled";
861 else pack.html.classList = "pack";
862 html.appendChild(pack.html);
863 }
864 self.notification = new Notification({
865 title: "Sound Selector:",
866 html: html,
867 id: "Sound-Selector",
868 duration: -1,
869 target: "#sound-btn"
870 });
871 });
872 self.initialized = true;
873 self.loadPack(self.soundSelection, true);
874 };
875
876 SoundSelector.prototype.loadPack = function(pack, f) {
877 for (var i = 0; this.packs.length > i; i++) {
878 var p = this.packs[i];
879 if (p.name == pack) {
880 pack = p;
881 break;
882 }
883 }
884 if (typeof pack == "string") {
885 console.warn("Sound pack does not exist! Loading default pack...");
886 return this.loadPack("MPP Classic");
887 }
888
889 if (pack.name == this.soundSelection && !f) return;
890 if (pack.keys.length != Object.keys(this.piano.keys).length) {
891 this.piano.keys = {};
892 for (var i = 0; pack.keys.length > i; i++) this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]];
893 this.piano.renderer.resize();
894 }
895
896 var self = this;
897 for (var i in this.piano.keys) {
898 if (!this.piano.keys.hasOwnProperty(i)) continue;
899 (function() {
900 var key = self.piano.keys[i];
901 key.loaded = false;
902 self.piano.audio.load(key.note, pack.url + key.note + pack.ext, function() {
903 key.loaded = true;
904 key.timeLoaded = Date.now();
905 });
906 })();
907 }
908 localStorage.soundSelection = pack.name;
909 this.soundSelection = pack.name;
910 };
911
912 SoundSelector.prototype.removePack = function(name) {
913 var found = false;
914 for (var i = 0; this.packs.length > i; i++) {
915 var pack = this.packs[i];
916 if (pack.name == name) {
917 this.packs.splice(i, 1);
918 if (pack.name == this.soundSelection) this.loadPack(this.packs[0].name); //add mpp default if none?
919 break;
920 }
921 }
922 if (!found) console.warn("Sound pack not found!");
923 };
924
925
926
927
928 // Pianoctor
929
930 ////////////////////////////////////////////////////////////////
931
932 var PianoKey = function(note, octave) {
933 this.note = note + octave;
934 this.baseNote = note;
935 this.octave = octave;
936 this.sharp = note.indexOf("s") != -1;
937 this.loaded = false;
938 this.timeLoaded = 0;
939 this.domElement = null;
940 this.timePlayed = 0;
941 this.blips = [];
942 };
943
944 var Piano = function(rootElement) {
945
946 var piano = this;
947 piano.rootElement = rootElement;
948 piano.keys = {};
949
950 var white_spatial = 0;
951 var black_spatial = 0;
952 var black_it = 0;
953 var black_lut = [2, 1, 2, 1, 1];
954 var addKey = function(note, octave) {
955 var key = new PianoKey(note, octave);
956 piano.keys[key.note] = key;
957 if (key.sharp) {
958 key.spatial = black_spatial;
959 black_spatial += black_lut[black_it % 5];
960 ++black_it;
961 } else {
962 key.spatial = white_spatial;
963 ++white_spatial;
964 }
965 }
966 if (test_mode) {
967 addKey("c", 2);
968 } else {
969 addKey("a", -1);
970 addKey("as", -1);
971 addKey("b", -1);
972 var notes = "c cs d ds e f fs g gs a as b".split(" ");
973 for (var oct = 0; oct < 7; oct++) {
974 for (var i in notes) {
975 addKey(notes[i], oct);
976 }
977 }
978 addKey("c", 7);
979 }
980
981
982 this.renderer = new CanvasRenderer().init(this);
983
984 window.addEventListener("resize", function() {
985 piano.renderer.resize();
986 });
987
988
989 window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined;
990 var audio_engine = AudioEngineWeb;
991 this.audio = new audio_engine().init();
992 };
993
994 Piano.prototype.play = function(note, vol, participant, delay_ms) {
995 if (!this.keys.hasOwnProperty(note)) return;
996 var key = this.keys[note];
997 if (key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id);
998 if (typeof gMidiOutTest === "function") gMidiOutTest(key.note, vol * 100, delay_ms);
999 var self = this;
1000 var jq_namediv = $(typeof participant == "undefined" ? null : participant.nameDiv);
1001 if (jq_namediv) {
1002 setTimeout(function() {
1003 self.renderer.visualize(key, typeof participant == "undefined" ? "yellow" : (participant.color || "#777"));
1004 jq_namediv.addClass("play");
1005 setTimeout(function() {
1006 jq_namediv.removeClass("play");
1007 }, 30);
1008 }, delay_ms);
1009 }
1010 };
1011
1012 Piano.prototype.stop = function(note, participant, delay_ms) {
1013 if (!this.keys.hasOwnProperty(note)) return;
1014 var key = this.keys[note];
1015 if (key.loaded) this.audio.stop(key.note, delay_ms, participant.id);
1016 if (typeof gMidiOutTest === "function") gMidiOutTest(key.note, 0, delay_ms);
1017 };
1018
1019 var gPiano = new Piano(document.getElementById("piano"));
1020
1021 var gSoundSelector = new SoundSelector(gPiano);
1022 gSoundSelector.addPacks([
1023 "https://ledlamp.github.io/piano-sounds/Emotional/",
1024 "https://ledlamp.github.io/piano-sounds/Emotional_2.0/",
1025 "https://ledlamp.github.io/piano-sounds/GreatAndSoftPiano/",
1026 "https://ledlamp.github.io/piano-sounds/HardAndToughPiano/",
1027 "https://ledlamp.github.io/piano-sounds/HardPiano/",
1028 "https://ledlamp.github.io/piano-sounds/Harp/",
1029 "https://ledlamp.github.io/piano-sounds/Harpsicord/",
1030 "https://ledlamp.github.io/piano-sounds/LoudAndProudPiano/",
1031 "https://ledlamp.github.io/piano-sounds/MLG/",
1032 "https://ledlamp.github.io/piano-sounds/Music_Box/",
1033 "https://ledlamp.github.io/piano-sounds/NewPiano/",
1034 "https://ledlamp.github.io/piano-sounds/Orchestra/",
1035 "https://ledlamp.github.io/piano-sounds/Piano2/",
1036 "https://ledlamp.github.io/piano-sounds/PianoSounds/",
1037 "https://ledlamp.github.io/piano-sounds/Rhodes_MK1/",
1038 "https://ledlamp.github.io/piano-sounds/SoftPiano/",
1039 "https://ledlamp.github.io/piano-sounds/Steinway_Grand/",
1040 "https://ledlamp.github.io/piano-sounds/Untitled/",
1041 "https://ledlamp.github.io/piano-sounds/Vintage_Upright/",
1042 "https://ledlamp.github.io/piano-sounds/Vintage_Upright_Soft/"
1043 ]);
1044 gSoundSelector.init();
1045
1046
1047
1048
1049 var gAutoSustain = false;
1050 var gSustain = false;
1051
1052 var gHeldNotes = {};
1053 var gSustainedNotes = {};
1054
1055 var rollTemp = false;
1056 var delPlay = false;
1057
1058 var octTemp = {
1059 octLevel: 5
1060 }
1061
1062 $("body").append('<div id="octave-btn" class="ugly-button" style="bottom: 35px; left: 780px; position: fixed; z-index: 500;">Octave Level</div>');
1063 $("#octave-btn").on("click", function(evt) {
1064 var isNumber = Number.isInteger || function(number) {
1065 return !isNaN(parseFloat(n)) && isFinite(n)
1066 }
1067 var result = prompt("Enter Octave Level (MAX 5)");
1068 if (result > 6 || result < 1) {
1069 prompt("That number is too high, or too low!");
1070 octTemp.octLevel = 1;
1071 } else {
1072 octTemp.octLevel = result;
1073 }
1074
1075 });
1076
1077 function press(id, vol) {
1078 if (!gClient.preventsPlaying() && gNoteQuota.spend(1)) {
1079 gHeldNotes[id] = true;
1080 gSustainedNotes[id] = true;
1081 gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
1082 gClient.startNote(id, vol);
1083 }
1084 }
1085
1086
1087 function press(id, vol) {
1088 //used to have an if gClient.preventsPlaying()
1089 if (delPlay) {
1090 setTimeout(function() {
1091 gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
1092 gClient.startNote(id + oct, vol);
1093 }, delTime);
1094 }
1095
1096 gHeldNotes[id] = true;
1097 gSustainedNotes[id] = true;
1098 var octave = parseInt(id.replace(/[^\d.]/, '').replace('s', ''));
1099 var note = id.replace(/[0-9]/g, '').replace("-", "");
1100
1101 function pressDown(pressed, oct) {
1102 gPiano.play(pressed + oct, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0);
1103 gClient.startNote(pressed + oct, vol);
1104
1105
1106 }
1107 if (!rollTemp && octTemp.octLevel == 1) {
1108 pressDown(note, octave);
1109 }
1110 if (!rollTemp && octTemp.octLevel == 2) {
1111 pressDown(note, octave);
1112 pressDown(note, (octave - 1));
1113 }
1114 if (!rollTemp && octTemp.octLevel == 3) {
1115 pressDown(note, octave);
1116 pressDown(note, (octave - 1));
1117 pressDown(note, (octave + 1));
1118 }
1119 if (!rollTemp && octTemp.octLevel == 4) {
1120 pressDown(note, octave);
1121 pressDown(note, (octave - 1));
1122 pressDown(note, (octave + 1));
1123 pressDown(note, (octave - 2));
1124 }
1125 if (!rollTemp && octTemp.octLevel == 5) {
1126 pressDown(note, octave);
1127 pressDown(note, (octave - 1));
1128 pressDown(note, (octave + 1));
1129 pressDown(note, (octave - 2));
1130 pressDown(note, (octave + 2));
1131 }
1132 if (rollTemp && octTemp.octLevel == 1) {
1133 pressDown(note, octave);
1134 }
1135 if (rollTemp && octTemp.octLevel == 2) {
1136 setTimeout(function() {
1137 pressDown(note, octave);
1138 }, 50);
1139 pressDown(note, (octave - 1));
1140 }
1141 if (rollTemp && octTemp.octLevel == 3) {
1142 setTimeout(function() {
1143 pressDown(note, octave);
1144 }, 50);
1145 pressDown(note, (octave.octLevel - 1));
1146 setTimeout(function() {
1147 pressDown(note, (octave + 1));
1148 }, 100);
1149 }
1150 if (rollTemp && octTemp.octLevel == 4) {
1151 setTimeout(function() {
1152 pressDown(note, octave);
1153 }, 100);
1154 setTimeout(function() {
1155 pressDown(note, (octave - 1));
1156 }, 50);
1157 setTimeout(function() {
1158 pressDown(note, (octave + 1));
1159 }, 150);
1160 pressDown(note, (octave - 2));
1161 }
1162 if (rollTemp && octTemp.octLevel == 5) {
1163 setTimeout(function() {
1164 pressDown(note, octave);
1165 }, 100);
1166 setTimeout(function() {
1167 pressDown(note, (octave - 1));
1168 }, 50);
1169 setTimeout(function() {
1170 pressDown(note, (octave + 1));
1171 }, 150);
1172 pressDown(note, (octave - 2));
1173 setTimeout(function() {
1174 pressDown(note, (octave + 2));
1175 }, 200);
1176 }
1177 }
1178
1179
1180
1181
1182 function release(id) {
1183 if (gHeldNotes[id]) {
1184 gHeldNotes[id] = false;
1185 if ((gAutoSustain || gSustain) && !enableSynth) {
1186 gSustainedNotes[id] = true;
1187 } else {
1188 if (gNoteQuota.spend(1)) {
1189 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1190 gClient.stopNote(id);
1191 gSustainedNotes[id] = false;
1192 }
1193 }
1194 }
1195 }
1196
1197 function pressSustain() {
1198 gSustain = true;
1199 }
1200
1201 function releaseSustain() {
1202 gSustain = false;
1203 if (!gAutoSustain) {
1204 for (var id in gSustainedNotes) {
1205 if (gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) {
1206 gSustainedNotes[id] = false;
1207 if (gNoteQuota.spend(1)) {
1208 gPiano.stop(id, gClient.getOwnParticipant(), 0);
1209 gClient.stopNote(id);
1210 }
1211 }
1212 }
1213 }
1214 }
1215
1216
1217
1218 // internet science
1219
1220 ////////////////////////////////////////////////////////////////
1221
1222 var channel_id = decodeURIComponent(window.location.pathname);
1223 if (channel_id.substr(0, 1) == "/") channel_id = channel_id.substr(1);
1224 if (channel_id == "") channel_id = "lobby";
1225
1226 var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8443;
1227 var gClient = new Client("wss://" + window.location.hostname + ":" + wssport);
1228 gClient.setChannel(channel_id);
1229 gClient.start();
1230
1231
1232 // Setting status
1233 (function() {
1234 gClient.on("status", function(status) {
1235 $("#status").text(status);
1236 });
1237 gClient.on("count", function(count) {
1238 if (count > 0) {
1239 $("#status").html('<span class="number">' + count + '</span> ' + (count == 1 ? 'person is' : 'people are') + ' playing');
1240 document.title = "Piano (" + count + ")";
1241 } else {
1242 document.title = "Multiplayer Piano";
1243 }
1244 });
1245 })();
1246
1247 // Handle changes to participants
1248 (function() {
1249 gClient.on("participant added", function(part) {
1250
1251 part.displayX = 150;
1252 part.displayY = 50;
1253
1254 // add nameDiv
1255 var div = document.createElement("div");
1256 div.className = "name";
1257 div.participantId = part.id;
1258 div.textContent = part.name || "";
1259 div.style.backgroundColor = part.color || "#777";
1260 if (gClient.participantId === part.id) {
1261 $(div).addClass("me");
1262 }
1263 if (gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) {
1264 $(div).addClass("owner");
1265 }
1266 if (gPianoMutes.indexOf(part._id) !== -1) {
1267 $(part.nameDiv).addClass("muted-notes");
1268 }
1269 if (gChatMutes.indexOf(part._id) !== -1) {
1270 $(part.nameDiv).addClass("muted-chat");
1271 }
1272 div.style.display = "none";
1273 part.nameDiv = $("#names")[0].appendChild(div);
1274 $(part.nameDiv).fadeIn(2000);
1275
1276 // sort names
1277 var arr = $("#names .name");
1278 arr.sort(function(a, b) {
1279 a = a.style.backgroundColor; // todo: sort based on user id instead
1280 b = b.style.backgroundColor;
1281 if (a > b) return 1;
1282 else if (a < b) return -1;
1283 else return 0;
1284 });
1285 $("#names").html(arr);
1286
1287 // add cursorDiv
1288 if (gClient.participantId !== part.id || gSeeOwnCursor) {
1289 var div = document.createElement("div");
1290 div.className = "cursor";
1291 div.style.display = "none";
1292 part.cursorDiv = $("#cursors")[0].appendChild(div);
1293 $(part.cursorDiv).fadeIn(2000);
1294
1295 var div = document.createElement("div");
1296 div.className = "name";
1297 div.style.backgroundColor = part.color || "#777"
1298 div.textContent = part.name || "";
1299 part.cursorDiv.appendChild(div);
1300
1301 } else {
1302 part.cursorDiv = undefined;
1303 }
1304 });
1305 gClient.on("participant removed", function(part) {
1306 // remove nameDiv
1307 var nd = $(part.nameDiv);
1308 var cd = $(part.cursorDiv);
1309 cd.fadeOut(2000);
1310 nd.fadeOut(2000, function() {
1311 nd.remove();
1312 cd.remove();
1313 part.nameDiv = undefined;
1314 part.cursorDiv = undefined;
1315 });
1316 });
1317 gClient.on("participant update", function(part) {
1318 var name = part.name || "";
1319 var color = part.color || "#777";
1320 part.nameDiv.style.backgroundColor = color;
1321 part.nameDiv.textContent = name;
1322 $(part.cursorDiv)
1323 .find(".name")
1324 .text(name)
1325 .css("background-color", color);
1326 });
1327 gClient.on("ch", function(msg) {
1328 for (var id in gClient.ppl) {
1329 if (gClient.ppl.hasOwnProperty(id)) {
1330 var part = gClient.ppl[id];
1331 if (part.id === gClient.participantId) {
1332 $(part.nameDiv).addClass("me");
1333 } else {
1334 $(part.nameDiv).removeClass("me");
1335 }
1336 if (msg.ch.crown && msg.ch.crown.participantId === part.id) {
1337 $(part.nameDiv).addClass("owner");
1338 $(part.cursorDiv).addClass("owner");
1339 } else {
1340 $(part.nameDiv).removeClass("owner");
1341 $(part.cursorDiv).removeClass("owner");
1342 }
1343 if (gPianoMutes.indexOf(part._id) !== -1) {
1344 $(part.nameDiv).addClass("muted-notes");
1345 } else {
1346 $(part.nameDiv).removeClass("muted-notes");
1347 }
1348 if (gChatMutes.indexOf(part._id) !== -1) {
1349 $(part.nameDiv).addClass("muted-chat");
1350 } else {
1351 $(part.nameDiv).removeClass("muted-chat");
1352 }
1353 }
1354 }
1355 });
1356
1357 function updateCursor(msg) {
1358 const part = gClient.ppl[msg.id];
1359 if (part && part.cursorDiv) {
1360 part.cursorDiv.style.left = msg.x + "%";
1361 part.cursorDiv.style.top = msg.y + "%";
1362 }
1363 }
1364 gClient.on("m", updateCursor);
1365 gClient.on("participant added", updateCursor);
1366 })();
1367
1368
1369 // Handle changes to crown
1370 (function() {
1371 var jqcrown = $('<div id="crown"></div>').appendTo(document.body).hide();
1372 var jqcountdown = $('<span></span>').appendTo(jqcrown);
1373 var countdown_interval;
1374 jqcrown.click(function() {
1375 gClient.sendArray([{
1376 m: "chown",
1377 id: gClient.participantId
1378 }]);
1379 });
1380 gClient.on("ch", function(msg) {
1381 if (msg.ch.crown) {
1382 var crown = msg.ch.crown;
1383 if (!crown.participantId || !gClient.ppl[crown.participantId]) {
1384 var land_time = crown.time + 2000 - gClient.serverTimeOffset;
1385 var avail_time = crown.time + 15000 - gClient.serverTimeOffset;
1386 jqcountdown.text("");
1387 jqcrown.show();
1388 if (land_time - Date.now() <= 0) {
1389 jqcrown.css({
1390 "left": crown.endPos.x + "%",
1391 "top": crown.endPos.y + "%"
1392 });
1393 } else {
1394 jqcrown.css({
1395 "left": crown.startPos.x + "%",
1396 "top": crown.startPos.y + "%"
1397 });
1398 jqcrown.addClass("spin");
1399 jqcrown.animate({
1400 "left": crown.endPos.x + "%",
1401 "top": crown.endPos.y + "%"
1402 }, 2000, "linear", function() {
1403 jqcrown.removeClass("spin");
1404 });
1405 }
1406 clearInterval(countdown_interval);
1407 countdown_interval = setInterval(function() {
1408 var time = Date.now();
1409 if (time >= land_time) {
1410 var ms = avail_time - time;
1411 if (ms > 0) {
1412 jqcountdown.text(Math.ceil(ms / 1000) + "s");
1413 } else {
1414 jqcountdown.text("");
1415 clearInterval(countdown_interval);
1416 }
1417 }
1418 }, 1000);
1419 } else {
1420 jqcrown.hide();
1421 }
1422 } else {
1423 jqcrown.hide();
1424 }
1425 });
1426 gClient.on("disconnect", function() {
1427 jqcrown.fadeOut(2000);
1428 });
1429 })();
1430
1431
1432 // Playing notes
1433 gClient.on("n", function(msg) {
1434 var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now();
1435 var participant = gClient.findParticipantById(msg.p);
1436 if (gPianoMutes.indexOf(participant._id) !== -1)
1437 return;
1438 for (var i = 0; i < msg.n.length; i++) {
1439 var note = msg.n[i];
1440 var ms = t + (note.d || 0);
1441 if (ms < 0) {
1442 ms = 0;
1443 } else if (ms > 10000) continue;
1444 if (note.s) {
1445 gPiano.stop(note.n, participant, ms);
1446 } else {
1447 var vel = (typeof note.v !== "undefined") ? parseFloat(note.v) : DEFAULT_VELOCITY;
1448 if (vel < 0) vel = 0;
1449 else if (vel > 1) vel = 1;
1450 gPiano.play(note.n, vel, participant, ms);
1451 if (enableSynth) {
1452 gPiano.stop(note.n, participant, ms + 1000);
1453 }
1454 }
1455 }
1456 });
1457
1458 // Send cursor updates
1459 var mx = 0,
1460 last_mx = -10,
1461 my = 0,
1462 last_my = -10;
1463 setInterval(function() {
1464 if (Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) {
1465 last_mx = mx;
1466 last_my = my;
1467 gClient.sendArray([{
1468 m: "m",
1469 x: mx,
1470 y: my
1471 }]);
1472 if (gSeeOwnCursor) {
1473 gClient.emit("m", {
1474 m: "m",
1475 id: gClient.participantId,
1476 x: mx,
1477 y: my
1478 });
1479 }
1480 var part = gClient.getOwnParticipant();
1481 if (part) {
1482 part.x = mx;
1483 part.y = my;
1484 }
1485 }
1486 }, 50);
1487 $(document).mousemove(function(event) {
1488 mx = ((event.pageX / $(window).width()) * 100).toFixed(2);
1489 my = ((event.pageY / $(window).height()) * 100).toFixed(2);
1490 });
1491
1492
1493 // Room settings button
1494 (function() {
1495 gClient.on("ch", function(msg) {
1496 if (gClient.isOwner()) {
1497 $("#room-settings-btn").show();
1498 } else {
1499 $("#room-settings-btn").hide();
1500 }
1501 });
1502 $("#room-settings-btn").click(function(evt) {
1503 if (gClient.channel && gClient.isOwner()) {
1504 var settings = gClient.channel.settings;
1505 openModal("#room-settings");
1506 setTimeout(function() {
1507 $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible);
1508 $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat);
1509 $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo);
1510 $("#room-settings input[name=color]").val(settings.color);
1511 }, 100);
1512 }
1513 });
1514 $("#room-settings .submit").click(function() {
1515 var settings = {
1516 visible: $("#room-settings .checkbox[name=visible]").is(":checked"),
1517 chat: $("#room-settings .checkbox[name=chat]").is(":checked"),
1518 crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"),
1519 color: $("#room-settings input[name=color]").val()
1520 };
1521 gClient.sendArray([{
1522 m: "chset",
1523 set: settings
1524 }]);
1525 closeModal();
1526 });
1527 $("#room-settings .drop-crown").click(function() {
1528 closeModal();
1529 if (confirm("This will drop the crown...!"))
1530 gClient.sendArray([{
1531 m: "chown"
1532 }]);
1533 });
1534 })();
1535
1536 // Handle notifications
1537 gClient.on("notification", function(msg) {
1538 new Notification(msg);
1539 });
1540
1541 // Don't foget spin
1542 gClient.on("ch", function(msg) {
1543 var chidlo = msg.ch._id.toLowerCase();
1544 if (chidlo === "spin" || chidlo.substr(-5) === "/spin") {
1545 $("#piano").addClass("spin");
1546 } else {
1547 $("#piano").removeClass("spin");
1548 }
1549 });
1550
1551 /*function eb() {
1552 if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") {
1553 ebsprite.start(gClient);
1554 } else {
1555 ebsprite.stop();
1556 }
1557 }
1558 if(ebsprite) {
1559 gClient.on("ch", eb);
1560 eb();
1561 }*/
1562
1563 // Crownsolo notice
1564 gClient.on("ch", function(msg) {
1565 if (msg.ch.settings.crownsolo) {
1566 if ($("#crownsolo-notice").length == 0) {
1567 $('<div id="crownsolo-notice">').text('This room is set to "only the owner can play."').appendTo("body").fadeIn(1000);
1568 }
1569 } else {
1570 $("#crownsolo-notice").remove();
1571 }
1572 });
1573 gClient.on("disconnect", function() {
1574 $("#crownsolo-notice").remove();
1575 });
1576
1577
1578 // Background color
1579 (function() {
1580 var old_color1 = new Color("#000000");
1581 var old_color2 = new Color("#000000");
1582
1583 function setColor(hex, hex2) {
1584 var color1 = new Color(hex);
1585 var color2 = new Color(hex2 || hex);
1586 if (!hex2)
1587 color2.add(-0x40, -0x40, -0x40);
1588
1589 var bottom = document.getElementById("bottom");
1590
1591 var duration = 500;
1592 var step = 0;
1593 var steps = 30;
1594 var step_ms = duration / steps;
1595 var difference = new Color(color1.r, color1.g, color1.b);
1596 difference.r -= old_color1.r;
1597 difference.g -= old_color1.g;
1598 difference.b -= old_color1.b;
1599 var inc1 = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
1600 difference = new Color(color2.r, color2.g, color2.b);
1601 difference.r -= old_color2.r;
1602 difference.g -= old_color2.g;
1603 difference.b -= old_color2.b;
1604 var inc2 = new Color(difference.r / steps, difference.g / steps, difference.b / steps);
1605 var iv;
1606 iv = setInterval(function() {
1607 old_color1.add(inc1.r, inc1.g, inc1.b);
1608 old_color2.add(inc2.r, inc2.g, inc2.b);
1609 document.body.style.background = "radial-gradient(ellipse at center, " + old_color1.toHexa() + " 0%," + old_color2.toHexa() + " 100%)";
1610 bottom.style.background = old_color2.toHexa();
1611 if (++step >= steps) {
1612 clearInterval(iv);
1613 old_color1 = color1;
1614 old_color2 = color2;
1615 document.body.style.background = "radial-gradient(ellipse at center, " + color1.toHexa() + " 0%," + color2.toHexa() + " 100%)";
1616 bottom.style.background = color2.toHexa();
1617 }
1618 }, step_ms);
1619 }
1620
1621 function setColorToDefault() {
1622 setColor("#000000", "#000000");
1623 }
1624
1625 setColorToDefault();
1626
1627 gClient.on("ch", function(ch) {
1628 if (ch.ch.settings) {
1629 if (ch.ch.settings.color) {
1630 setColor(ch.ch.settings.color, ch.ch.settings.color2);
1631 } else {
1632 setColorToDefault();
1633 }
1634 }
1635 });
1636 })();
1637
1638
1639
1640
1641 var gPianoMutes = [];
1642
1643 var gChatMutes = [];
1644 var followId = undefined;
1645
1646
1647
1648
1649 var volume_slider = document.getElementById("volume-slider");
1650 volume_slider.value = gPiano.audio.volume;
1651 $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
1652 volume_slider.addEventListener("input", function(evt) {
1653 var v = +volume_slider.value;
1654 gPiano.audio.setVolume(v);
1655 if (window.localStorage) localStorage.volume = v;
1656 $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%");
1657 });
1658
1659
1660
1661
1662 var Note = function(note, octave) {
1663 this.note = note;
1664 this.octave = octave || 0;
1665 };
1666
1667
1668
1669 var n = function(a, b) {
1670 return {
1671 note: new Note(a, b),
1672 held: false
1673 };
1674 };
1675 var key_binding = {
1676 65: n("gs"),
1677 90: n("a"),
1678 83: n("as"),
1679 88: n("b"),
1680 67: n("c", 1),
1681 70: n("cs", 1),
1682 86: n("d", 1),
1683 71: n("ds", 1),
1684 66: n("e", 1),
1685 78: n("f", 1),
1686 74: n("fs", 1),
1687 77: n("g", 1),
1688 75: n("gs", 1),
1689 188: n("a", 1),
1690 76: n("as", 1),
1691 190: n("b", 1),
1692 191: n("c", 2),
1693 222: n("cs", 2),
1694
1695 49: n("gs", 1),
1696 81: n("a", 1),
1697 50: n("as", 1),
1698 87: n("b", 1),
1699 69: n("c", 2),
1700 52: n("cs", 2),
1701 82: n("d", 2),
1702 53: n("ds", 2),
1703 84: n("e", 2),
1704 89: n("f", 2),
1705 55: n("fs", 2),
1706 85: n("g", 2),
1707 56: n("gs", 2),
1708 73: n("a", 2),
1709 57: n("as", 2),
1710 79: n("b", 2),
1711 80: n("c", 3),
1712 189: n("cs", 3),
1713 173: n("cs", 3), // firefox why
1714 219: n("d", 3),
1715 187: n("ds", 3),
1716 61: n("ds", 3), // firefox why
1717 221: n("e", 3)
1718 };
1719
1720 var capsLockKey = false;
1721
1722 var transpose_octave = 0;
1723
1724 function handleKeyDown(evt) {
1725 //console.log(evt);
1726 var code = parseInt(evt.keyCode);
1727 if (key_binding[code] !== undefined) {
1728 var binding = key_binding[code];
1729 if (!binding.held) {
1730 binding.held = true;
1731
1732 var note = binding.note;
1733 var octave = 1 + note.octave + transpose_octave;
1734 if (evt.shiftKey) ++octave;
1735 else if (capsLockKey || evt.ctrlKey) --octave;
1736 note = note.note + octave;
1737 var vol = velocityFromMouseY();
1738 press(note, vol);
1739 }
1740
1741 if (++gKeyboardSeq == 3) {
1742 gKnowsYouCanUseKeyboard = true;
1743 if (window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout);
1744 if (localStorage) localStorage.knowsYouCanUseKeyboard = true;
1745 if (window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close();
1746 }
1747
1748 evt.preventDefault();
1749 evt.stopPropagation();
1750 return false;
1751 } else if (code == 20) { // Caps Lock
1752 capsLockKey = true;
1753 evt.preventDefault();
1754 } else if (code === 0x20) { // Space Bar
1755 pressSustain();
1756 evt.preventDefault();
1757 } else if ((code === 38 || code === 39) && transpose_octave < 3) {
1758 ++transpose_octave;
1759 } else if ((code === 40 || code === 37) && transpose_octave > -2) {
1760 --transpose_octave;
1761 } else if (code == 9) { // Tab (don't tab away from the piano)
1762 evt.preventDefault();
1763 } else if (code == 8) { // Backspace (don't navigate Back)
1764 gAutoSustain = !gAutoSustain;
1765 evt.preventDefault();
1766 }
1767 };
1768
1769 function handleKeyUp(evt) {
1770 var code = parseInt(evt.keyCode);
1771 if (key_binding[code] !== undefined) {
1772 var binding = key_binding[code];
1773 if (binding.held) {
1774 binding.held = false;
1775
1776 var note = binding.note;
1777 var octave = 1 + note.octave + transpose_octave;
1778 if (evt.shiftKey) ++octave;
1779 else if (capsLockKey || evt.ctrlKey) --octave;
1780 note = note.note + octave;
1781 release(note);
1782 }
1783
1784 evt.preventDefault();
1785 evt.stopPropagation();
1786 return false;
1787 } else if (code == 20) { // Caps Lock
1788 capsLockKey = false;
1789 evt.preventDefault();
1790 } else if (code === 0x20) { // Space Bar
1791 releaseSustain();
1792 evt.preventDefault();
1793 }
1794 };
1795
1796 function handleKeyPress(evt) {
1797 evt.preventDefault();
1798 evt.stopPropagation();
1799 if (evt.keyCode == 27 || evt.keyCode == 13) {
1800 //$("#chat input").focus();
1801 }
1802 return false;
1803 };
1804
1805 var recapListener = function(evt) {
1806 captureKeyboard();
1807 };
1808
1809 function captureKeyboard() {
1810 $("#piano").off("mousedown", recapListener);
1811 $("#piano").off("touchstart", recapListener);
1812 $(document).on("keydown", handleKeyDown);
1813 $(document).on("keyup", handleKeyUp);
1814 $(window).on("keypress", handleKeyPress);
1815 };
1816
1817 function releaseKeyboard() {
1818 $(document).off("keydown", handleKeyDown);
1819 $(document).off("keyup", handleKeyUp);
1820 $(window).off("keypress", handleKeyPress);
1821 $("#piano").on("mousedown", recapListener);
1822 $("#piano").on("touchstart", recapListener);
1823 };
1824
1825 captureKeyboard();
1826
1827
1828 var velocityFromMouseY = function() {
1829 return 0.1 + (my / 100) * 0.6;
1830 };
1831
1832
1833
1834
1835 // NoteQuota
1836 var gNoteQuota = (function() {
1837 var last_rat = 0;
1838 var nqjq = $("#quota .value");
1839 setInterval(function() {
1840 gNoteQuota.tick();
1841 }, 2000);
1842 return new NoteQuota(function(points) {
1843 // update UI
1844 var rat = (points / this.max) * 100;
1845 if (rat <= last_rat)
1846 nqjq.stop(true, true).css("width", rat.toFixed(0) + "%");
1847 else
1848 nqjq.stop(true, true).animate({
1849 "width": rat.toFixed(0) + "%"
1850 }, 2000, "linear");
1851 last_rat = rat;
1852 });
1853 })();
1854 gClient.on("nq", function(nq_params) {
1855 gNoteQuota.setParams(nq_params);
1856 });
1857 gClient.on("disconnect", function() {
1858 gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE);
1859 });
1860
1861
1862
1863 // click participant names
1864 (function() {
1865 var ele = document.getElementById("names");
1866 var touchhandler = function(e) {
1867 var target_jq = $(e.target);
1868 if (target_jq.hasClass("name")) {
1869 target_jq.addClass("play");
1870 if (e.target.participantId == gClient.participantId) {
1871 openModal("#rename", "input[name=name]");
1872 setTimeout(function() {
1873 $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name);
1874 $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color);
1875 }, 100);
1876 } else if (e.target.participantId) {
1877 var id = e.target.participantId;
1878 var part = gClient.ppl[id] || null;
1879 if (part) {
1880 participantMenu(part);
1881 e.stopPropagation();
1882 }
1883 }
1884 }
1885 };
1886 ele.addEventListener("mousedown", touchhandler);
1887 ele.addEventListener("touchstart", touchhandler);
1888 var releasehandler = function(e) {
1889 $("#names .name").removeClass("play");
1890 };
1891 document.body.addEventListener("mouseup", releasehandler);
1892 document.body.addEventListener("touchend", releasehandler);
1893
1894 var removeParticipantMenus = function() {
1895 $(".participant-menu").remove();
1896 $(".participantSpotlight").hide();
1897 document.removeEventListener("mousedown", removeParticipantMenus);
1898 document.removeEventListener("touchstart", removeParticipantMenus);
1899 };
1900
1901 var participantMenu = function(part) {
1902 if (!part) return;
1903 removeParticipantMenus();
1904 document.addEventListener("mousedown", removeParticipantMenus);
1905 document.addEventListener("touchstart", removeParticipantMenus);
1906 $("#" + part.id).find(".enemySpotlight").show();
1907 var menu = $('<div class="participant-menu"></div>');
1908 $("body").append(menu);
1909 // move menu to name position
1910 var jq_nd = $(part.nameDiv);
1911 var pos = jq_nd.position();
1912 menu.css({
1913 "top": pos.top + jq_nd.height() + 15,
1914 "left": pos.left + 6,
1915 "background": part.color || "black"
1916 });
1917 menu.on("mousedown touchstart", function(evt) {
1918 evt.stopPropagation();
1919 var target = $(evt.target);
1920 if (target.hasClass("menu-item")) {
1921 target.addClass("clicked");
1922 menu.fadeOut(200, function() {
1923 removeParticipantMenus();
1924 });
1925 }
1926 });
1927 // this spaces stuff out but also can be used for informational
1928 $('<div class="info"></div>').appendTo(menu).text(part._id);
1929 // add menu items
1930 //No problem. I'm gonna let you use my follow.
1931 //Lemme test it.
1932
1933 var fSpeed = 0.25; //ORG Is 0.25
1934 var fRadius = 2; // ORG is 2
1935 var fDegree = 180; // ORG is 180
1936 var z = 0;
1937 //Clean up.
1938 follow = setInterval(function() {
1939 for (var _id in gClient.ppl) {
1940 if (!gClient.ppl.hasOwnProperty(_id)) continue;
1941 var part = gClient.ppl[_id];
1942 if (followId == undefined) {
1943 return;
1944 }
1945 if (part._id == followId) {
1946 var angle = (z) * (Math.PI / fDegree);
1947 gClient.sendArray([{
1948 m: "m",
1949 x: part.x + Math.sin(angle) * fRadius,
1950 y: part.y + Math.cos(angle) * fRadius
1951 }]);
1952 z += fSpeed;
1953 }
1954 }
1955 }, 15);
1956
1957 if (followId == undefined) {
1958 $('<div class="menu-item">Follow</div>').appendTo(menu)
1959 .on("mousedown touchstart", function(evt) {
1960 followId = part._id;
1961
1962 window.gTest = new Notification({
1963 title: "Following...",
1964 text: "Following " + followId + "'s cursor.",
1965 target: "#piano",
1966 duration: 3000
1967 });
1968
1969 });
1970 } else {
1971 $('<div class="menu-item">Stop Follow</div>').appendTo(menu)
1972 .on("mousedown touchstart", function(evt) {
1973 clearInterval(follow);
1974 followId = undefined;
1975
1976 window.gTest = new Notification({
1977 title: "Stopping...",
1978 text: "Stopping follow.",
1979 target: "#piano",
1980 duration: 3000
1981 });
1982 });
1983 }
1984
1985 if (gPianoMutes.indexOf(part._id) == -1) {
1986 $('<div class="menu-item">Mute Notes</div>').appendTo(menu)
1987 .on("mousedown touchstart", function(evt) {
1988 gPianoMutes.push(part._id);
1989 $(part.nameDiv).addClass("muted-notes");
1990 });
1991 } else {
1992 $('<div class="menu-item">Unmute Notes</div>').appendTo(menu)
1993 .on("mousedown touchstart", function(evt) {
1994 var i;
1995 while ((i = gPianoMutes.indexOf(part._id)) != -1)
1996 gPianoMutes.splice(i, 1);
1997 $(part.nameDiv).removeClass("muted-notes");
1998 });
1999 }
2000
2001
2002 if (gChatMutes.indexOf(part._id) == -1) {
2003 $('<div class="menu-item">Mute Chat</div>').appendTo(menu)
2004 .on("mousedown touchstart", function(evt) {
2005 gChatMutes.push(part._id);
2006 $(part.nameDiv).addClass("muted-chat");
2007 });
2008 } else {
2009 $('<div class="menu-item">Unmute Chat</div>').appendTo(menu)
2010 .on("mousedown touchstart", function(evt) {
2011 var i;
2012 while ((i = gChatMutes.indexOf(part._id)) != -1)
2013 gChatMutes.splice(i, 1);
2014 $(part.nameDiv).removeClass("muted-chat");
2015 });
2016 }
2017 if (!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) {
2018 $('<div class="menu-item">Mute Completely</div>').appendTo(menu)
2019 .on("mousedown touchstart", function(evt) {
2020 gPianoMutes.push(part._id);
2021 gChatMutes.push(part._id);
2022 $(part.nameDiv).addClass("muted-notes");
2023 $(part.nameDiv).addClass("muted-chat");
2024 });
2025 }
2026 if ((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) {
2027 $('<div class="menu-item">Unmute Completely</div>').appendTo(menu)
2028 .on("mousedown touchstart", function(evt) {
2029 var i;
2030 while ((i = gPianoMutes.indexOf(part._id)) != -1)
2031 gPianoMutes.splice(i, 1);
2032 while ((i = gChatMutes.indexOf(part._id)) != -1)
2033 gChatMutes.splice(i, 1);
2034 $(part.nameDiv).removeClass("muted-notes");
2035 $(part.nameDiv).removeClass("muted-chat");
2036 });
2037 }
2038 if (gClient.isOwner()) {
2039 $('<div class="menu-item give-crown">Give Crown</div>').appendTo(menu)
2040 .on("mousedown touchstart", function(evt) {
2041 if (confirm("Give room ownership to " + part.name + "?"))
2042 gClient.sendArray([{
2043 m: "chown",
2044 id: part.id
2045 }]);
2046 });
2047 $('<div class="menu-item kickban">Kickban</div>').appendTo(menu)
2048 .on("mousedown touchstart", function(evt) {
2049 var minutes = prompt("How many minutes? (0-60)", "30");
2050 if (minutes === null) return;
2051 minutes = parseFloat(minutes) || 0;
2052 var ms = minutes * 60 * 1000;
2053 gClient.sendArray([{
2054 m: "kickban",
2055 _id: part._id,
2056 ms: ms
2057 }]);
2058 });
2059 }
2060 menu.fadeIn(100);
2061 };
2062 })();
2063
2064
2065
2066
2067 // Notification class
2068
2069 ////////////////////////////////////////////////////////////////
2070
2071 var Notification = function(par) {
2072 EventEmitter.call(this);
2073
2074 var par = par || {};
2075
2076 this.id = "Notification-" + (par.id || Math.random());
2077 this.title = par.title || "";
2078 this.text = par.text || "";
2079 this.html = par.html || "";
2080 this.target = $(par.target || "#piano");
2081 this.duration = par.duration || 30000;
2082 this["class"] = par["class"] || "classic";
2083
2084 var self = this;
2085 var eles = $("#" + this.id);
2086 if (eles.length > 0) {
2087 eles.remove();
2088 }
2089 this.domElement = $('<div class="notification"><div class="notification-body"><div class="title"></div>' +
2090 '<div class="text"></div></div><div class="x">x</div></div>');
2091 this.domElement[0].id = this.id;
2092 this.domElement.addClass(this["class"]);
2093 this.domElement.find(".title").text(this.title);
2094 if (this.text.length > 0) {
2095 this.domElement.find(".text").text(this.text);
2096 } else if (this.html instanceof HTMLElement) {
2097 this.domElement.find(".text")[0].appendChild(this.html);
2098 } else if (this.html.length > 0) {
2099 this.domElement.find(".text").html(this.html);
2100 }
2101 document.body.appendChild(this.domElement.get(0));
2102
2103 this.position();
2104 this.onresize = function() {
2105 self.position();
2106 };
2107 window.addEventListener("resize", this.onresize);
2108
2109 this.domElement.find(".x").click(function() {
2110 self.close();
2111 });
2112
2113 if (this.duration > 0) {
2114 setTimeout(function() {
2115 self.close();
2116 }, this.duration);
2117 }
2118
2119 return this;
2120 }
2121
2122 mixin(Notification.prototype, EventEmitter.prototype);
2123 Notification.prototype.constructor = Notification;
2124
2125 Notification.prototype.position = function() {
2126 var pos = this.target.offset();
2127 var x = pos.left - (this.domElement.width() / 2) + (this.target.width() / 4);
2128 var y = pos.top - this.domElement.height() - 8;
2129 var width = this.domElement.width();
2130 if (x + width > $("body").width()) {
2131 x -= ((x + width) - $("body").width());
2132 }
2133 if (x < 0) x = 0;
2134 this.domElement.offset({
2135 left: x,
2136 top: y
2137 });
2138 };
2139
2140 Notification.prototype.close = function() {
2141 var self = this;
2142 window.removeEventListener("resize", this.onresize);
2143 this.domElement.fadeOut(500, function() {
2144 self.domElement.remove();
2145 self.emit("close");
2146 });
2147 };
2148
2149
2150
2151
2152 // set variables from settings or set settings
2153
2154 ////////////////////////////////////////////////////////////////
2155
2156 var gKeyboardSeq = 0;
2157 var gKnowsYouCanUseKeyboard = false;
2158 if (localStorage && localStorage.knowsYouCanUseKeyboard) gKnowsYouCanUseKeyboard = true;
2159 if (!gKnowsYouCanUseKeyboard) {
2160 window.gKnowsYouCanUseKeyboardTimeout = setTimeout(function() {
2161 window.gKnowsYouCanUseKeyboardNotification = new Notification({
2162 title: "Did you know!?!",
2163 text: "You can play the piano with your keyboard, too. Try it!",
2164 target: "#piano",
2165 duration: 10000
2166 });
2167 }, 30000);
2168 }
2169
2170
2171
2172
2173 if (window.localStorage) {
2174
2175 if (localStorage.volume) {
2176 volume_slider.value = localStorage.volume;
2177 gPiano.audio.setVolume(localStorage.volume);
2178 $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%");
2179 } else localStorage.volume = gPiano.audio.volume;
2180
2181 window.gHasBeenHereBefore = (localStorage.gHasBeenHereBefore || false);
2182 if (gHasBeenHereBefore) {}
2183 localStorage.gHasBeenHereBefore = true;
2184
2185 }
2186
2187
2188
2189
2190 // warn user about loud noises before starting sound (no autoplay)
2191 openModal("#sound-warning");
2192 var user_interact = function(evt) {
2193 document.removeEventListener("click", user_interact);
2194 closeModal();
2195 MPP.piano.audio.resume();
2196 }
2197 document.addEventListener("click", user_interact);
2198
2199
2200
2201
2202 // New room, change room
2203
2204 ////////////////////////////////////////////////////////////////
2205
2206 $("#room > .info").text("--");
2207 gClient.on("ch", function(msg) {
2208 var channel = msg.ch;
2209 var info = $("#room > .info");
2210 info.text(channel._id);
2211 if (channel.settings.lobby) info.addClass("lobby");
2212 else info.removeClass("lobby");
2213 if (!channel.settings.chat) info.addClass("no-chat");
2214 else info.removeClass("no-chat");
2215 if (channel.settings.crownsolo) info.addClass("crownsolo");
2216 else info.removeClass("crownsolo");
2217 if (!channel.settings.visible) info.addClass("not-visible");
2218 else info.removeClass("not-visible");
2219 });
2220 gClient.on("ls", function(ls) {
2221 for (var i in ls.u) {
2222 if (!ls.u.hasOwnProperty(i)) continue;
2223 var room = ls.u[i];
2224 var info = $("#room .info[roomname=\"" + (room._id + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0') + "\"]");
2225 if (info.length == 0) {
2226 info = $("<div class=\"info\"></div>");
2227 info.attr("roomname", room._id);
2228 $("#room .more").append(info);
2229 }
2230 info.text(room._id + " (" + room.count + ")");
2231 if (room.settings.lobby) info.addClass("lobby");
2232 else info.removeClass("lobby");
2233 if (!room.settings.chat) info.addClass("no-chat");
2234 else info.removeClass("no-chat");
2235 if (room.settings.crownsolo) info.addClass("crownsolo");
2236 else info.removeClass("crownsolo");
2237 if (!room.settings.visible) info.addClass("not-visible");
2238 else info.removeClass("not-visible");
2239 if (room.banned) info.addClass("banned");
2240 else info.removeClass("banned");
2241 }
2242 });
2243 $("#room").on("click", function(evt) {
2244 evt.stopPropagation();
2245
2246 // clicks on a new room
2247 if ($(evt.target).hasClass("info") && $(evt.target).parents(".more").length) {
2248 $("#room .more").fadeOut(250);
2249 var selected_name = $(evt.target).attr("roomname");
2250 if (typeof selected_name != "undefined") {
2251 changeRoom(selected_name, "right");
2252 }
2253 return false;
2254 }
2255 // clicks on "New Room..."
2256 else if ($(evt.target).hasClass("new")) {
2257 openModal("#new-room", "input[name=name]");
2258 }
2259 // all other clicks
2260 var doc_click = function(evt) {
2261 if ($(evt.target).is("#room .more")) return;
2262 $(document).off("mousedown", doc_click);
2263 $("#room .more").fadeOut(250);
2264 gClient.sendArray([{
2265 m: "-ls"
2266 }]);
2267 }
2268 $(document).on("mousedown", doc_click);
2269 $("#room .more .info").remove();
2270 $("#room .more").show();
2271 gClient.sendArray([{
2272 m: "+ls"
2273 }]);
2274 });
2275 $("#new-room-btn").on("click", function(evt) {
2276 evt.stopPropagation();
2277 openModal("#new-room", "input[name=name]");
2278 });
2279
2280
2281 $("#play-alone-btn").on("click", function(evt) {
2282 evt.stopPropagation();
2283 var room_name = "Room" + Math.floor(Math.random() * 1000000000000);
2284 changeRoom(room_name, "right", {
2285 "visible": false,
2286 "chat": true,
2287 "crownsolo": false
2288 });
2289 setTimeout(function() {
2290 new Notification({
2291 id: "share",
2292 title: "Playing alone",
2293 html: 'You are playing alone in a room by yourself, but you can always invite \
2294friends by sending them the link.<br/><br/>\
2295<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/>\
2296<a href="http://twitter.com/home?status=' + encodeURIComponent(location.href) + '" target="_blank">Tweet</a>',
2297 duration: 25000
2298 });
2299 }, 1000);
2300 });
2301
2302
2303
2304 var gModal;
2305
2306 function modalHandleEsc(evt) {
2307 if (evt.keyCode == 27) {
2308 closeModal();
2309 evt.preventDefault();
2310 evt.stopPropagation();
2311 }
2312 };
2313
2314 function openModal(selector, focus) {
2315 if (chat) chat.blur();
2316 releaseKeyboard();
2317 $(document).on("keydown", modalHandleEsc);
2318 $("#modal #modals > *").hide();
2319 $("#modal").fadeIn(250);
2320 $(selector).show();
2321 setTimeout(function() {
2322 $(selector).find(focus).focus();
2323 }, 100);
2324 gModal = selector;
2325 };
2326
2327 function closeModal() {
2328 $(document).off("keydown", modalHandleEsc);
2329 $("#modal").fadeOut(100);
2330 $("#modal #modals > *").hide();
2331 captureKeyboard();
2332 gModal = null;
2333 };
2334
2335 var modal_bg = $("#modal .bg")[0];
2336 $(modal_bg).on("click", function(evt) {
2337 if (evt.target != modal_bg) return;
2338 closeModal();
2339 });
2340
2341 (function() {
2342 function submit() {
2343 var name = $("#new-room .text[name=name]").val();
2344 var settings = {
2345 visible: $("#new-room .checkbox[name=visible]").is(":checked"),
2346 chat: true,
2347 crownsolo: false
2348 };
2349 $("#new-room .text[name=name]").val("");
2350 closeModal();
2351 changeRoom(name, "right", settings);
2352 setTimeout(function() {
2353 new Notification({
2354 id: "share",
2355 title: "Created a Room",
2356 html: 'You can invite friends to your room by sending them the link.<br/><br/>\
2357<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/>\
2358<a href="http://twitter.com/home?status=' + encodeURIComponent(location.href) + '" target="_blank">Tweet</a>',
2359 duration: 25000
2360 });
2361 }, 1000);
2362 };
2363 $("#new-room .submit").click(function(evt) {
2364 submit();
2365 });
2366 $("#new-room .text[name=name]").keypress(function(evt) {
2367 if (evt.keyCode == 13) {
2368 submit();
2369 } else if (evt.keyCode == 27) {
2370 closeModal();
2371 } else {
2372 return;
2373 }
2374 evt.preventDefault();
2375 evt.stopPropagation();
2376 return false;
2377 });
2378 })();
2379
2380
2381
2382
2383 function changeRoom(name, direction, settings, push) {
2384 if (!settings) settings = {};
2385 if (!direction) direction = "right";
2386 if (typeof push == "undefined") push = true;
2387 var opposite = direction == "left" ? "right" : "left";
2388
2389 if (name == "") name = "lobby";
2390 if (gClient.channel && gClient.channel._id === name) return;
2391 if (push) {
2392 var url = "/" + encodeURIComponent(name).replace("'", "%27");
2393 if (window.history && history.pushState) {
2394 history.pushState({
2395 "depth": gHistoryDepth += 1,
2396 "name": name
2397 }, "Piano > " + name, url);
2398 } else {
2399 window.location = url;
2400 return;
2401 }
2402 }
2403
2404 gClient.setChannel(name, settings);
2405
2406 var t = 0,
2407 d = 100;
2408 $("#piano").addClass("ease-out").addClass("slide-" + opposite);
2409 setTimeout(function() {
2410 $("#piano").removeClass("ease-out").removeClass("slide-" + opposite).addClass("slide-" + direction);
2411 }, t += d);
2412 setTimeout(function() {
2413 $("#piano").addClass("ease-in").removeClass("slide-" + direction);
2414 }, t += d);
2415 setTimeout(function() {
2416 $("#piano").removeClass("ease-in");
2417 }, t += d);
2418 };
2419
2420 var gHistoryDepth = 0;
2421 $(window).on("popstate", function(evt) {
2422 var depth = evt.state ? evt.state.depth : 0;
2423 if (depth == gHistoryDepth) return; // <-- forgot why I did that though...
2424
2425 var direction = depth <= gHistoryDepth ? "left" : "right";
2426 gHistoryDepth = depth;
2427
2428 var name = decodeURIComponent(window.location.pathname);
2429 if (name.substr(0, 1) == "/") name = name.substr(1);
2430 changeRoom(name, direction, null, false);
2431 });
2432
2433
2434
2435
2436 // Rename
2437
2438 ////////////////////////////////////////////////////////////////
2439
2440 (function() {
2441 function submit() {
2442 var set = {
2443 name: $("#rename input[name=name]").val(),
2444 color: $("#rename input[name=color]").val()
2445 };
2446 //$("#rename .text[name=name]").val("");
2447 closeModal();
2448 gClient.sendArray([{
2449 m: "userset",
2450 set: set
2451 }]);
2452 };
2453 $("#rename .submit").click(function(evt) {
2454 submit();
2455 });
2456 $("#rename .text[name=name]").keypress(function(evt) {
2457 if (evt.keyCode == 13) {
2458 submit();
2459 } else if (evt.keyCode == 27) {
2460 closeModal();
2461 } else {
2462 return;
2463 }
2464 evt.preventDefault();
2465 evt.stopPropagation();
2466 return false;
2467 });
2468 })();
2469
2470
2471
2472
2473 // chatctor
2474
2475 ////////////////////////////////////////////////////////////////
2476
2477 var chat = (function() {
2478 gClient.on("ch", function(msg) {
2479 if (msg.ch.settings.chat) {
2480 chat.show();
2481 } else {
2482 chat.hide();
2483 }
2484 });
2485 gClient.on("disconnect", function(msg) {
2486 chat.hide();
2487 });
2488 gClient.on("c", function(msg) {
2489 chat.clear();
2490 if (msg.c) {
2491 for (var i = 0; i < msg.c.length; i++) {
2492 chat.receive(msg.c[i]);
2493 }
2494 }
2495 });
2496 gClient.on("a", function(msg) {
2497 chat.receive(msg);
2498 });
2499
2500 $("#chat input").on("focus", function(evt) {
2501 releaseKeyboard();
2502 $("#chat").addClass("chatting");
2503 chat.scrollToBottom();
2504 });
2505 /*$("#chat input").on("blur", function(evt) {
2506 captureKeyboard();
2507 $("#chat").removeClass("chatting");
2508 chat.scrollToBottom();
2509 });*/
2510 $(document).mousedown(function(evt) {
2511 if (!$("#chat").has(evt.target).length > 0) {
2512 chat.blur();
2513 }
2514 });
2515 document.addEventListener("touchstart", function(event) {
2516 for (var i in event.changedTouches) {
2517 var touch = event.changedTouches[i];
2518 if (!$("#chat").has(touch.target).length > 0) {
2519 chat.blur();
2520 }
2521 }
2522 });
2523 $(document).on("keydown", function(evt) {
2524 if ($("#chat").hasClass("chatting")) {
2525 if (evt.keyCode == 27) {
2526 chat.blur();
2527 evt.preventDefault();
2528 evt.stopPropagation();
2529 } else if (evt.keyCode == 13) {
2530 $("#chat input").focus();
2531 }
2532 } else if (!gModal && (evt.keyCode == 27 || evt.keyCode == 13)) {
2533 $("#chat input").focus();
2534 }
2535 });
2536 $("#chat input").on("keydown", function(evt) {
2537 if (evt.keyCode == 13) {
2538 var message = $(this).val();
2539 if (message.length == 0) {
2540 setTimeout(function() {
2541 chat.blur();
2542 }, 100);
2543 } else if (message.length <= 512) {
2544 chat.send(message);
2545 $(this).val("");
2546 setTimeout(function() {
2547 chat.blur();
2548 }, 100);
2549 }
2550 evt.preventDefault();
2551 evt.stopPropagation();
2552 } else if (evt.keyCode == 27) {
2553 chat.blur();
2554 evt.preventDefault();
2555 evt.stopPropagation();
2556 } else if (evt.keyCode == 9) {
2557 evt.preventDefault();
2558 evt.stopPropagation();
2559 }
2560 });
2561
2562 return {
2563 show: function() {
2564 $("#chat").fadeIn();
2565 },
2566
2567 hide: function() {
2568 $("#chat").fadeOut();
2569 },
2570
2571 clear: function() {
2572 $("#chat li").remove();
2573 },
2574
2575 scrollToBottom: function() {
2576 var ele = $("#chat ul").get(0);
2577 ele.scrollTop = ele.scrollHeight;
2578 },
2579
2580 blur: function() {
2581 if ($("#chat").hasClass("chatting")) {
2582 $("#chat input").get(0).blur();
2583 $("#chat").removeClass("chatting");
2584 chat.scrollToBottom();
2585 captureKeyboard();
2586 }
2587 },
2588
2589 send: function(message) {
2590 gClient.sendArray([{
2591 m: "a",
2592 message: message
2593 }]);
2594 },
2595
2596 receive: function(msg) {
2597 if (gChatMutes.indexOf(msg.p._id) != -1) return;
2598
2599 var li = $('<li><span class="name"/><span class="message"/>');
2600
2601 li.find(".name").text(msg.p.name + ":");
2602 li.find(".message").text(msg.a);
2603 li.css("color", msg.p.color || "white");
2604
2605 $("#chat ul").append(li);
2606
2607 var eles = $("#chat ul li").get();
2608 for (var i = 1; i <= 50 && i <= eles.length; i++) {
2609 eles[eles.length - i].style.opacity = 1.0 - (i * 0.03);
2610 }
2611 if (eles.length > 50) {
2612 eles[0].style.display = "none";
2613 }
2614 if (eles.length > 256) {
2615 $(eles[0]).remove();
2616 }
2617
2618 // scroll to bottom if not "chatting" or if not scrolled up
2619 if (!$("#chat").hasClass("chatting")) {
2620 chat.scrollToBottom();
2621 } else {
2622 var ele = $("#chat ul").get(0);
2623 if (ele.scrollTop > ele.scrollHeight - ele.offsetHeight - 50)
2624 chat.scrollToBottom();
2625 }
2626 }
2627 };
2628 })();
2629
2630
2631
2632
2633 // MIDI
2634
2635 ////////////////////////////////////////////////////////////////
2636
2637 var MIDI_TRANSPOSE = -12;
2638 var MIDI_KEY_NAMES = ["a-1", "as-1", "b-1"];
2639 var bare_notes = "c cs d ds e f fs g gs a as b".split(" ");
2640 for (var oct = 0; oct < 7; oct++) {
2641 for (var i in bare_notes) {
2642 MIDI_KEY_NAMES.push(bare_notes[i] + oct);
2643 }
2644 }
2645 MIDI_KEY_NAMES.push("c7");
2646
2647 var devices_json;
2648
2649 function sendDevices() {
2650 gClient.sendArray([{
2651 "m": "devices",
2652 "list": JSON.parse(devices_json)
2653 }]);
2654 }
2655 gClient.on("connect", sendDevices);
2656
2657 (function() {
2658
2659 if (navigator.requestMIDIAccess) {
2660 navigator.requestMIDIAccess().then(
2661 function(midi) {
2662 console.log(midi);
2663
2664 function midimessagehandler(evt) {
2665 if (!evt.target.enabled) return;
2666 //console.log(evt);
2667 var channel = evt.data[0] & 0xf;
2668 var cmd = evt.data[0] >> 4;
2669 var note_number = evt.data[1];
2670 var vel = evt.data[2];
2671 //console.log(channel, cmd, note_number, vel);
2672 if (cmd == 8 || (cmd == 9 && vel == 0)) {
2673 // NOTE_OFF
2674 release(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE]);
2675 } else if (cmd == 9) {
2676 // NOTE_ON
2677 press(MIDI_KEY_NAMES[note_number - 9 + MIDI_TRANSPOSE], vel / 100);
2678 } else if (cmd == 11) {
2679 // CONTROL_CHANGE
2680 if (!gAutoSustain) {
2681 if (note_number == 64) {
2682 if (vel > 0) {
2683 pressSustain();
2684 } else {
2685 releaseSustain();
2686 }
2687 }
2688 }
2689 }
2690 }
2691
2692 function deviceInfo(dev) {
2693 return {
2694 type: dev.type,
2695 //id: dev.id,
2696 manufacturer: dev.manufacturer,
2697 name: dev.name,
2698 version: dev.version,
2699 //connection: dev.connection,
2700 //state: dev.state,
2701 enabled: dev.enabled
2702 };
2703 }
2704
2705 function updateDevices() {
2706 var list = [];
2707 if (midi.inputs.size > 0) {
2708 var inputs = midi.inputs.values();
2709 for (var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2710 var input = input_it.value;
2711 list.push(deviceInfo(input));
2712 }
2713 }
2714 if (midi.outputs.size > 0) {
2715 var outputs = midi.outputs.values();
2716 for (var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2717 var output = output_it.value;
2718 list.push(deviceInfo(output));
2719 }
2720 }
2721 var new_json = JSON.stringify(list);
2722 if (new_json !== devices_json) {
2723 devices_json = new_json;
2724 sendDevices();
2725 }
2726 }
2727
2728 function plug() {
2729 if (midi.inputs.size > 0) {
2730 var inputs = midi.inputs.values();
2731 for (var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2732 var input = input_it.value;
2733 //input.removeEventListener("midimessage", midimessagehandler);
2734 //input.addEventListener("midimessage", midimessagehandler);
2735 input.onmidimessage = midimessagehandler;
2736 if (input.enabled !== false) {
2737 input.enabled = true;
2738 }
2739 console.log("input", input);
2740 }
2741 }
2742 if (midi.outputs.size > 0) {
2743 var outputs = midi.outputs.values();
2744 for (var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2745 var output = output_it.value;
2746 //output.enabled = false; // edit: don't touch
2747 console.log("output", output);
2748 }
2749 gMidiOutTest = function(note_name, vel, delay_ms) {
2750 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
2751 if (note_number == -1) return;
2752 note_number = note_number + 9 - MIDI_TRANSPOSE;
2753
2754 var outputs = midi.outputs.values();
2755 for (var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2756 var output = output_it.value;
2757 if (output.enabled) {
2758 output.send([0x90, note_number, vel], window.performance.now() + delay_ms);
2759 }
2760 }
2761 }
2762 }
2763 showConnections(false);
2764 updateDevices();
2765 }
2766
2767 midi.addEventListener("statechange", function(evt) {
2768 if (evt instanceof MIDIConnectionEvent) {
2769 plug();
2770 }
2771 });
2772
2773 plug();
2774
2775
2776 var connectionsNotification;
2777
2778 function showConnections(sticky) {
2779 //if(document.getElementById("Notification-MIDI-Connections"))
2780 //sticky = 1; // todo: instead,
2781 var inputs_ul = document.createElement("ul");
2782 if (midi.inputs.size > 0) {
2783 var inputs = midi.inputs.values();
2784 for (var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2785 var input = input_it.value;
2786 var li = document.createElement("li");
2787 li.connectionId = input.id;
2788 li.classList.add("connection");
2789 if (input.enabled) li.classList.add("enabled");
2790 li.textContent = input.name;
2791 li.addEventListener("click", function(evt) {
2792 var inputs = midi.inputs.values();
2793 for (var input_it = inputs.next(); input_it && !input_it.done; input_it = inputs.next()) {
2794 var input = input_it.value;
2795 if (input.id === evt.target.connectionId) {
2796 input.enabled = !input.enabled;
2797 evt.target.classList.toggle("enabled");
2798 console.log("click", input);
2799 updateDevices();
2800 return;
2801 }
2802 }
2803 });
2804 inputs_ul.appendChild(li);
2805 }
2806 } else {
2807 inputs_ul.textContent = "(none)";
2808 }
2809 var outputs_ul = document.createElement("ul");
2810 if (midi.outputs.size > 0) {
2811 var outputs = midi.outputs.values();
2812 for (var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2813 var output = output_it.value;
2814 var li = document.createElement("li");
2815 li.connectionId = output.id;
2816 li.classList.add("connection");
2817 if (output.enabled) li.classList.add("enabled");
2818 li.textContent = output.name;
2819 li.addEventListener("click", function(evt) {
2820 var outputs = midi.outputs.values();
2821 for (var output_it = outputs.next(); output_it && !output_it.done; output_it = outputs.next()) {
2822 var output = output_it.value;
2823 if (output.id === evt.target.connectionId) {
2824 output.enabled = !output.enabled;
2825 evt.target.classList.toggle("enabled");
2826 console.log("click", output);
2827 updateDevices();
2828 return;
2829 }
2830 }
2831 });
2832 outputs_ul.appendChild(li);
2833 }
2834 } else {
2835 outputs_ul.textContent = "(none)";
2836 }
2837 var div = document.createElement("div");
2838 var h1 = document.createElement("h1");
2839 h1.textContent = "Inputs";
2840 div.appendChild(h1);
2841 div.appendChild(inputs_ul);
2842 h1 = document.createElement("h1");
2843 h1.textContent = "Outputs";
2844 div.appendChild(h1);
2845 div.appendChild(outputs_ul);
2846 connectionsNotification = new Notification({
2847 "id": "MIDI-Connections",
2848 "title": "MIDI Connections",
2849 "duration": sticky ? "-1" : "4500",
2850 "html": div,
2851 "target": "#midi-btn"
2852 });
2853 }
2854
2855 document.getElementById("midi-btn").addEventListener("click", function(evt) {
2856 if (!document.getElementById("Notification-MIDI-Connections"))
2857 showConnections(true);
2858 else {
2859 connectionsNotification.close();
2860 }
2861 });
2862 },
2863 function(err) {
2864 console.log(err);
2865 });
2866 }
2867 })();
2868
2869
2870
2871
2872 // bug supply
2873
2874 ////////////////////////////////////////////////////////////////
2875
2876 window.onerror = function(message, url, line) {
2877 var url = url || "(no url)";
2878 var line = line || "(no line)";
2879 // errors in socket.io
2880 if (url.indexOf("socket.io.js") !== -1) {
2881 if (message.indexOf("INVALID_STATE_ERR") !== -1) return;
2882 if (message.indexOf("InvalidStateError") !== -1) return;
2883 if (message.indexOf("DOM Exception 11") !== -1) return;
2884 if (message.indexOf("Property 'open' of object #<c> is not a function") !== -1) return;
2885 if (message.indexOf("Cannot call method 'close' of undefined") !== -1) return;
2886 if (message.indexOf("Cannot call method 'close' of null") !== -1) return;
2887 if (message.indexOf("Cannot call method 'onClose' of null") !== -1) return;
2888 if (message.indexOf("Cannot call method 'payload' of null") !== -1) return;
2889 if (message.indexOf("Unable to get value of the property 'close'") !== -1) return;
2890 if (message.indexOf("NS_ERROR_NOT_CONNECTED") !== -1) return;
2891 if (message.indexOf("Unable to get property 'close' of undefined or null reference") !== -1) return;
2892 if (message.indexOf("Unable to get value of the property 'close': object is null or undefined") !== -1) return;
2893 if (message.indexOf("this.transport is null") !== -1) return;
2894 }
2895 // errors in soundmanager2
2896 if (url.indexOf("soundmanager2.js") !== -1) {
2897 // operation disabled in safe mode?
2898 if (message.indexOf("Could not complete the operation due to error c00d36ef") !== -1) return;
2899 if (message.indexOf("_s.o._setVolume is not a function") !== -1) return;
2900 }
2901 // errors in midibridge
2902 if (url.indexOf("midibridge") !== -1) {
2903 if (message.indexOf("Error calling method on NPObject") !== -1) return;
2904 }
2905 // too many failing extensions injected in my html
2906 if (url.indexOf(".js") !== url.length - 3) return;
2907 // extensions inject cross-domain embeds too
2908 if (url.toLowerCase().indexOf("multiplayerpiano.com") == -1) return;
2909
2910 // errors in my code
2911 if (url.indexOf("script.js") !== -1) {
2912 if (message.indexOf("Object [object Object] has no method 'on'") !== -1) return;
2913 if (message.indexOf("Object [object Object] has no method 'off'") !== -1) return;
2914 if (message.indexOf("Property '$' of object [object Object] is not a function") !== -1) return;
2915 }
2916
2917 var enc = "/bugreport/" +
2918 (message ? encodeURIComponent(message) : "") + "/" +
2919 (url ? encodeURIComponent(url) : "") + "/" +
2920 (line ? encodeURIComponent(line) : "");
2921 var img = new Image();
2922 img.src = enc;
2923 };
2924
2925
2926
2927
2928 // API
2929 window.MPP = {
2930 press: press,
2931 release: release,
2932 piano: gPiano,
2933 client: gClient,
2934 chat: chat,
2935 noteQuota: gNoteQuota,
2936 soundSelector: gSoundSelector
2937 };
2938
2939
2940
2941
2942 //midi player by NaN-NaN-senpai(BR)
2943
2944
2945 /*
2946 COPIED FROM https://tonejs.github.io/
2947 MADE BY ToneJs: https://github.com/Tonejs
2948 EDITED FOR MULTIPLEYR PIANO BY NaN-NaN-senpai(BR)
2949
2950
2951
2952 NOTE:
2953 I MADE THIS IN ONLY 5 HOURS, SO IT MAY HAVE A LOT OF BUGS, FEEL
2954 FREE TO TRY TO SOLVE THEM.
2955
2956 NOTE
2957 THE FILES TAKE A BIT OF TIME TO BO LOADED. SO BIGER FILES WILL TAKE
2958 LONGER TO BE LOADED.
2959 GIGANTIC FILES, LIKE LONG MUSICS AND BLACK MIDI, MAY FROZE THE
2960 SCREEN.
2961
2962 NOTE:
2963 SOME FILES HAVE A DIFERENT TYPE OF NOTES THAT THIS SCRIPT CANT READ
2964 THE SCRIPT WILL TRY TO TRANSLATE THEM AND WILL STOP WORKING
2965 TO FIX IT, YOU WILL NEED TO REFRESH THE TAB.
2966
2967
2968 THIS SCRIPT JUST PLAY THE FILES BUT IT HAVE GREATE POTENCIAL OF
2969 BEEING AUTOMIZED WITH ANY TYPE OF THINGS, LIKE MORE OCTAVE,
2970 MULTINOTES, SPEED AND WAY MORE (IT JUST DEPEND ON YOUR
2971 IMAGINATION).
2972
2973 FEEL FREE TO EDIT THIS CODE.
2974 */
2975
2976 document.getElementById("social").innerHTML += `
2977<div style="position: absolute; right: 10px; top: 20px">
2978<div id="FileDrop">
2979<div id="Text">
2980Drop a midi file here
2981</div>
2982<input type="file" accept="audio/midi">
2983</div>
2984<button onclick='
2985if(this.innerHTML == "Play"){
2986autoPlayer(midiJSONobject)
2987this.innerHTML = "Stop"
2988} else {
2989stopAutoPlay()
2990this.innerHTML = "Play"
2991}
2992'>Play</button>
2993<div id="Results" style="pointer-events: none; opacity: 0;">
2994<textarea id="ResultsText" placeholder="json output..."></textarea>
2995</div>
2996</div>`;
2997
2998 var midiJSONobject;
2999
3000
3001
3002
3003 // CODE COPIED AND EDITED FROM TONE.JS:
3004
3005 ! function(t, e) {
3006 if ("object" == typeof exports && "object" == typeof module) module.exports = e();
3007 else if ("function" == typeof define && define.amd) define([], e);
3008 else {
3009 var r = e();
3010 for (var n in r)("object" == typeof exports ? exports : t)[n] = r[n]
3011 }
3012 }("undefined" != typeof self ? self : this, function() {
3013 return function(t) {
3014 var e = {};
3015
3016 function r(n) {
3017 if (e[n]) return e[n].exports;
3018 var i = e[n] = {
3019 i: n,
3020 l: !1,
3021 exports: {}
3022 };
3023 return t[n].call(i.exports, i, i.exports, r), i.l = !0, i.exports
3024 }
3025 return r.m = t, r.c = e, r.d = function(t, e, n) {
3026 r.o(t, e) || Object.defineProperty(t, e, {
3027 enumerable: !0,
3028 get: n
3029 })
3030 }, r.r = function(t) {
3031 "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
3032 value: "Module"
3033 }), Object.defineProperty(t, "__esModule", {
3034 value: !0
3035 })
3036 }, r.t = function(t, e) {
3037 if (1 & e && (t = r(t)), 8 & e) return t;
3038 if (4 & e && "object" == typeof t && t && t.__esModule) return t;
3039 var n = Object.create(null);
3040 if (r.r(n), Object.defineProperty(n, "default", {
3041 enumerable: !0,
3042 value: t
3043 }), 2 & e && "string" != typeof t)
3044 for (var i in t) r.d(n, i, function(e) {
3045 return t[e]
3046 }.bind(null, i));
3047 return n
3048 }, r.n = function(t) {
3049 var e = t && t.__esModule ? function() {
3050 return t.default
3051 } : function() {
3052 return t
3053 };
3054 return r.d(e, "a", e), e
3055 }, r.o = function(t, e) {
3056 return Object.prototype.hasOwnProperty.call(t, e)
3057 }, r.p = "", r(r.s = 4)
3058 }([function(t, e, r) {
3059 e.parseMidi = r(5), e.writeMidi = r(6)
3060 }, function(t, e, r) {
3061 "use strict";
3062 Object.defineProperty(e, "__esModule", {
3063 value: !0
3064 });
3065 var n = r(2),
3066 i = new WeakMap;
3067 e.keySignatureKeys = ["Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#"];
3068 var a = function() {
3069 function t(t) {
3070 var r = this;
3071 this.tempos = [], this.timeSignatures = [], this.keySignatures = [], this.meta = [], this.name = "", i.set(this, 480), t && (i.set(this, t.header.ticksPerBeat), t.tracks[0].forEach(function(t) {
3072 t.meta && ("timeSignature" === t.type ? r.timeSignatures.push({
3073 ticks: t.absoluteTime,
3074 timeSignature: [t.numerator, t.denominator]
3075 }) : "setTempo" === t.type ? r.tempos.push({
3076 bpm: 6e7 / t.microsecondsPerBeat,
3077 ticks: t.absoluteTime
3078 }) : "keySignature" === t.type ? r.keySignatures.push({
3079 key: e.keySignatureKeys[t.key + 7],
3080 scale: 0 === t.scale ? "major" : "minor",
3081 ticks: t.absoluteTime
3082 }) : "trackName" === t.type ? r.name = t.text : "endOfTrack" !== t.type && r.meta.push({
3083 text: t.text,
3084 ticks: t.absoluteTime,
3085 type: t.type
3086 }))
3087 }), this.update())
3088 }
3089 return t.prototype.update = function() {
3090 var t = this,
3091 e = 0,
3092 r = 0;
3093 this.tempos.sort(function(t, e) {
3094 return t.ticks - e.ticks
3095 }), this.tempos.forEach(function(n, i) {
3096 var a = i > 0 ? t.tempos[i - 1].bpm : t.tempos[0].bpm,
3097 o = n.ticks / t.ppq - r,
3098 s = 60 / a * o;
3099 n.time = s + e, e = n.time, r += o
3100 }), this.timeSignatures.sort(function(t, e) {
3101 return t.ticks - e.ticks
3102 }), this.timeSignatures.forEach(function(e, r) {
3103 var n = r > 0 ? t.timeSignatures[r - 1] : t.timeSignatures[0],
3104 i = (e.ticks - n.ticks) / t.ppq / n.timeSignature[0] / (n.timeSignature[1] / 4);
3105 n.measures = n.measures || 0, e.measures = i + n.measures
3106 })
3107 }, t.prototype.ticksToSeconds = function(t) {
3108 var e = n.search(this.tempos, t);
3109 if (-1 !== e) {
3110 var r = this.tempos[e],
3111 i = r.time,
3112 a = (t - r.ticks) / this.ppq;
3113 return i + 60 / r.bpm * a
3114 }
3115 return .5 * (t / this.ppq)
3116 }, t.prototype.ticksToMeasures = function(t) {
3117 var e = n.search(this.timeSignatures, t);
3118 if (-1 !== e) {
3119 var r = this.timeSignatures[e],
3120 i = (t - r.ticks) / this.ppq;
3121 return r.measures + i / (r.timeSignature[0] / r.timeSignature[1]) / 4
3122 }
3123 return t / this.ppq / 4
3124 }, Object.defineProperty(t.prototype, "ppq", {
3125 get: function() {
3126 return i.get(this)
3127 },
3128 enumerable: !0,
3129 configurable: !0
3130 }), t.prototype.secondsToTicks = function(t) {
3131 var e = n.search(this.tempos, t, "time");
3132 if (-1 !== e) {
3133 var r = this.tempos[e],
3134 i = (t - r.time) / (60 / r.bpm);
3135 return Math.round(r.ticks + i * this.ppq)
3136 }
3137 var a = t / .5;
3138 return Math.round(a * this.ppq)
3139 }, t.prototype.toJSON = function() {
3140 return {
3141 keySignatures: this.keySignatures,
3142 meta: this.meta,
3143 name: this.name,
3144 ppq: this.ppq,
3145 tempos: this.tempos.map(function(t) {
3146 return {
3147 bpm: t.bpm,
3148 ticks: t.ticks
3149 }
3150 }),
3151 timeSignatures: this.timeSignatures
3152 }
3153 }, t.prototype.fromJSON = function(t) {
3154 this.name = t.name, this.tempos = t.tempos.map(function(t) {
3155 return Object.assign({}, t)
3156 }), this.timeSignatures = t.timeSignatures.map(function(t) {
3157 return Object.assign({}, t)
3158 }), this.keySignatures = t.keySignatures.map(function(t) {
3159 return Object.assign({}, t)
3160 }), this.meta = t.meta.map(function(t) {
3161 return Object.assign({}, t)
3162 }), i.set(this, t.ppq), this.update()
3163 }, t.prototype.setTempo = function(t) {
3164 this.tempos = [{
3165 bpm: t,
3166 ticks: 0
3167 }], this.update()
3168 }, t
3169 }();
3170 e.Header = a
3171 }, function(t, e, r) {
3172 "use strict";
3173
3174 function n(t, e, r) {
3175 void 0 === r && (r = "ticks");
3176 var n = 0,
3177 i = t.length,
3178 a = i;
3179 if (i > 0 && t[i - 1][r] <= e) return i - 1;
3180 for (; n < a;) {
3181 var o = Math.floor(n + (a - n) / 2),
3182 s = t[o],
3183 u = t[o + 1];
3184 if (s[r] === e) {
3185 for (var c = o; c < t.length; c++) {
3186 t[c][r] === e && (o = c)
3187 }
3188 return o
3189 }
3190 if (s[r] < e && u[r] > e) return o;
3191 s[r] > e ? a = o : s[r] < e && (n = o + 1)
3192 }
3193 return -1
3194 }
3195 Object.defineProperty(e, "__esModule", {
3196 value: !0
3197 }), e.search = n, e.insert = function(t, e, r) {
3198 if (void 0 === r && (r = "ticks"), t.length) {
3199 var i = n(t, e[r], r);
3200 t.splice(i + 1, 0, e)
3201 } else t.push(e)
3202 }
3203 }, function(t, e, r) {
3204 "use strict";
3205 Object.defineProperty(e, "__esModule", {
3206 value: !0
3207 }), e.controlChangeNames = {
3208 1: "modulationWheel",
3209 2: "breath",
3210 4: "footController",
3211 5: "portamentoTime",
3212 7: "volume",
3213 8: "balance",
3214 10: "pan",
3215 64: "sustain",
3216 65: "portamentoTime",
3217 66: "sostenuto",
3218 67: "softPedal",
3219 68: "legatoFootswitch",
3220 84: "portamentoControl"
3221 }, e.controlChangeIds = Object.keys(e.controlChangeNames).reduce(function(t, r) {
3222 return t[e.controlChangeNames[r]] = r, t
3223 }, {});
3224 var n = new WeakMap,
3225 i = new WeakMap,
3226 a = function() {
3227 function t(t, e) {
3228 n.set(this, e), i.set(this, t.controllerType), this.ticks = t.absoluteTime, this.value = t.value
3229 }
3230 return Object.defineProperty(t.prototype, "number", {
3231 get: function() {
3232 return i.get(this)
3233 },
3234 enumerable: !0,
3235 configurable: !0
3236 }), Object.defineProperty(t.prototype, "name", {
3237 get: function() {
3238 return e.controlChangeNames[this.number] ? e.controlChangeNames[this.number] : null
3239 },
3240 enumerable: !0,
3241 configurable: !0
3242 }), Object.defineProperty(t.prototype, "time", {
3243 get: function() {
3244 return n.get(this).ticksToSeconds(this.ticks)
3245 },
3246 set: function(t) {
3247 var e = n.get(this);
3248 this.ticks = e.secondsToTicks(t)
3249 },
3250 enumerable: !0,
3251 configurable: !0
3252 }), t.prototype.toJSON = function() {
3253 return {
3254 number: this.number,
3255 ticks: this.ticks,
3256 time: this.time,
3257 value: this.value
3258 }
3259 }, t
3260 }();
3261 e.ControlChange = a
3262 }, function(t, e, r) {
3263 "use strict";
3264 var n = this && this.__awaiter || function(t, e, r, n) {
3265 return new(r || (r = Promise))(function(i, a) {
3266 function o(t) {
3267 try {
3268 u(n.next(t))
3269 } catch (t) {
3270 a(t)
3271 }
3272 }
3273
3274 function s(t) {
3275 try {
3276 u(n.throw(t))
3277 } catch (t) {
3278 a(t)
3279 }
3280 }
3281
3282 function u(t) {
3283 var e;
3284 t.done ? i(t.value) : (e = t.value, e instanceof r ? e : new r(function(t) {
3285 t(e)
3286 })).then(o, s)
3287 }
3288 u((n = n.apply(t, e || [])).next())
3289 })
3290 },
3291 i = this && this.__generator || function(t, e) {
3292 var r, n, i, a, o = {
3293 label: 0,
3294 sent: function() {
3295 if (1 & i[0]) throw i[1];
3296 return i[1]
3297 },
3298 trys: [],
3299 ops: []
3300 };
3301 return a = {
3302 next: s(0),
3303 throw: s(1),
3304 return: s(2)
3305 }, "function" == typeof Symbol && (a[Symbol.iterator] = function() {
3306 return this
3307 }), a;
3308
3309 function s(a) {
3310 return function(s) {
3311 return function(a) {
3312 if (r) throw new TypeError("Generator is already executing.");
3313 for (; o;) try {
3314 if (r = 1, n && (i = 2 & a[0] ? n.return : a[0] ? n.throw || ((i = n.return) && i.call(n), 0) : n.next) && !(i = i.call(n, a[1])).done) return i;
3315 switch (n = 0, i && (a = [2 & a[0], i.value]), a[0]) {
3316 case 0:
3317 case 1:
3318 i = a;
3319 break;
3320 case 4:
3321 return o.label++, {
3322 value: a[1],
3323 done: !1
3324 };
3325 case 5:
3326 o.label++, n = a[1], a = [0];
3327 continue;
3328 case 7:
3329 a = o.ops.pop(), o.trys.pop();
3330 continue;
3331 default:
3332 if (!(i = (i = o.trys).length > 0 && i[i.length - 1]) && (6 === a[0] || 2 === a[0])) {
3333 o = 0;
3334 continue
3335 }
3336 if (3 === a[0] && (!i || a[1] > i[0] && a[1] < i[3])) {
3337 o.label = a[1];
3338 break
3339 }
3340 if (6 === a[0] && o.label < i[1]) {
3341 o.label = i[1], i = a;
3342 break
3343 }
3344 if (i && o.label < i[2]) {
3345 o.label = i[2], o.ops.push(a);
3346 break
3347 }
3348 i[2] && o.ops.pop(), o.trys.pop();
3349 continue
3350 }
3351 a = e.call(t, o)
3352 } catch (t) {
3353 a = [6, t], n = 0
3354 } finally {
3355 r = i = 0
3356 }
3357 if (5 & a[0]) throw a[1];
3358 return {
3359 value: a[0] ? a[1] : void 0,
3360 done: !0
3361 }
3362 }([a, s])
3363 }
3364 }
3365 };
3366 Object.defineProperty(e, "__esModule", {
3367 value: !0
3368 });
3369 var a = r(0),
3370 o = r(7),
3371 s = r(1),
3372 u = r(9),
3373 c = function() {
3374 function t(t) {
3375 var e = this,
3376 r = null;
3377 t && (t instanceof ArrayBuffer && (t = new Uint8Array(t)), (r = a.parseMidi(t)).tracks.forEach(function(t) {
3378 var e = 0;
3379 t.forEach(function(t) {
3380 e += t.deltaTime, t.absoluteTime = e
3381 })
3382 })), this.header = new s.Header(r), this.tracks = [], t && (this.tracks = r.tracks.map(function(t) {
3383 return new u.Track(t, e.header)
3384 }), 1 === r.header.format && 0 === this.tracks[0].duration && this.tracks.shift())
3385 }
3386 return t.fromUrl = function(e) {
3387 return n(this, void 0, void 0, function() {
3388 var r;
3389 return i(this, function(n) {
3390 switch (n.label) {
3391 case 0:
3392 return [4, fetch(e)];
3393 case 1:
3394 return (r = n.sent()).ok ? [4, r.arrayBuffer()] : [3, 3];
3395 case 2:
3396 return [2, new t(n.sent())];
3397 case 3:
3398 throw new Error("could not load " + e)
3399 }
3400 })
3401 })
3402 }, Object.defineProperty(t.prototype, "name", {
3403 get: function() {
3404 return this.header.name
3405 },
3406 set: function(t) {
3407 this.header.name = t
3408 },
3409 enumerable: !0,
3410 configurable: !0
3411 }), Object.defineProperty(t.prototype, "duration", {
3412 get: function() {
3413 var t = this.tracks.map(function(t) {
3414 return t.duration
3415 });
3416 return Math.max.apply(Math, t)
3417 },
3418 enumerable: !0,
3419 configurable: !0
3420 }), Object.defineProperty(t.prototype, "durationTicks", {
3421 get: function() {
3422 var t = this.tracks.map(function(t) {
3423 return t.durationTicks
3424 });
3425 return Math.max.apply(Math, t)
3426 },
3427 enumerable: !0,
3428 configurable: !0
3429 }), t.prototype.addTrack = function() {
3430 var t = new u.Track(void 0, this.header);
3431 return this.tracks.push(t), t
3432 }, t.prototype.toArray = function() {
3433 return o.encode(this)
3434 }, t.prototype.toJSON = function() {
3435 return {
3436 header: this.header.toJSON(),
3437 tracks: this.tracks.map(function(t) {
3438 return t.toJSON()
3439 })
3440 }
3441 }, t.prototype.fromJSON = function(t) {
3442 var e = this;
3443 this.header = new s.Header, this.header.fromJSON(t.header), this.tracks = t.tracks.map(function(t) {
3444 var r = new u.Track(void 0, e.header);
3445 return r.fromJSON(t), r
3446 })
3447 }, t.prototype.clone = function() {
3448 var e = new t;
3449 return e.fromJSON(this.toJSON()), e
3450 }, t
3451 }();
3452 e.Midi = c
3453 }, function(t, e) {
3454 function r(t) {
3455 for (var e, r = new n(t), i = []; !r.eof();) {
3456 var a = o();
3457 i.push(a)
3458 }
3459 return i;
3460
3461 function o() {
3462 var t = {};
3463 t.deltaTime = r.readVarInt();
3464 var n = r.readUInt8();
3465 if (240 == (240 & n)) {
3466 if (255 !== n) {
3467 if (240 == n) {
3468 t.type = "sysEx";
3469 a = r.readVarInt();
3470 return t.data = r.readBytes(a), t
3471 }
3472 if (247 == n) {
3473 t.type = "endSysEx";
3474 a = r.readVarInt();
3475 return t.data = r.readBytes(a), t
3476 }
3477 throw "Unrecognised MIDI event type byte: " + n
3478 }
3479 t.meta = !0;
3480 var i = r.readUInt8(),
3481 a = r.readVarInt();
3482 switch (i) {
3483 case 0:
3484 if (t.type = "sequenceNumber", 2 !== a) throw "Expected length for sequenceNumber event is 2, got " + a;
3485 return t.number = stream.readUInt16(), t;
3486 case 1:
3487 return t.type = "text", t.text = r.readString(a), t;
3488 case 2:
3489 return t.type = "copyrightNotice", t.text = r.readString(a), t;
3490 case 3:
3491 return t.type = "trackName", t.text = r.readString(a), t;
3492 case 4:
3493 return t.type = "instrumentName", t.text = r.readString(a), t;
3494 case 5:
3495 return t.type = "lyrics", t.text = r.readString(a), t;
3496 case 6:
3497 return t.type = "marker", t.text = r.readString(a), t;
3498 case 7:
3499 return t.type = "cuePoint", t.text = r.readString(a), t;
3500 case 32:
3501 if (t.type = "channelPrefix", 1 != a) throw "Expected length for channelPrefix event is 1, got " + a;
3502 return t.channel = r.readUInt8(), t;
3503 case 33:
3504 if (t.type = "portPrefix", 1 != a) throw "Expected length for portPrefix event is 1, got " + a;
3505 return t.port = r.readUInt8(), t;
3506 case 47:
3507 if (t.type = "endOfTrack", 0 != a) throw "Expected length for endOfTrack event is 0, got " + a;
3508 return t;
3509 case 81:
3510 if (t.type = "setTempo", 3 != a) throw "Expected length for setTempo event is 3, got " + a;
3511 return t.microsecondsPerBeat = r.readUInt24(), t;
3512 case 84:
3513 if (t.type = "smpteOffset", 5 != a) throw "Expected length for smpteOffset event is 5, got " + a;
3514 var o = r.readUInt8();
3515 return t.frameRate = {
3516 0: 24,
3517 32: 25,
3518 64: 29,
3519 96: 30
3520 } [96 & o], t.hour = 31 & o, t.min = r.readUInt8(), t.sec = r.readUInt8(), t.frame = r.readUInt8(), t.subFrame = r.readUInt8(), t;
3521 case 88:
3522 if (t.type = "timeSignature", 4 != a) throw "Expected length for timeSignature event is 4, got " + a;
3523 return t.numerator = r.readUInt8(), t.denominator = 1 << r.readUInt8(), t.metronome = r.readUInt8(), t.thirtyseconds = r.readUInt8(), t;
3524 case 89:
3525 if (t.type = "keySignature", 2 != a) throw "Expected length for keySignature event is 2, got " + a;
3526 return t.key = r.readInt8(), t.scale = r.readUInt8(), t;
3527 case 127:
3528 return t.type = "sequencerSpecific", t.data = r.readBytes(a), t;
3529 default:
3530 return t.type = "unknownMeta", t.data = r.readBytes(a), t.metatypeByte = i, t
3531 }
3532 } else {
3533 var s;
3534 if (0 == (128 & n)) {
3535 if (null === e) throw "Running status byte encountered before status byte";
3536 s = n, n = e, t.running = !0
3537 } else s = r.readUInt8(), e = n;
3538 var u = n >> 4;
3539 switch (t.channel = 15 & n, u) {
3540 case 8:
3541 return t.type = "noteOff", t.noteNumber = s, t.velocity = r.readUInt8(), t;
3542 case 9:
3543 var c = r.readUInt8();
3544 return t.type = 0 === c ? "noteOff" : "noteOn", t.noteNumber = s, t.velocity = c, 0 === c && (t.byte9 = !0), t;
3545 case 10:
3546 return t.type = "noteAftertouch", t.noteNumber = s, t.amount = r.readUInt8(), t;
3547 case 11:
3548 return t.type = "controller", t.controllerType = s, t.value = r.readUInt8(), t;
3549 case 12:
3550 return t.type = "programChange", t.programNumber = s, t;
3551 case 13:
3552 return t.type = "channelAftertouch", t.amount = s, t;
3553 case 14:
3554 return t.type = "pitchBend", t.value = s + (r.readUInt8() << 7) - 8192, t;
3555 default:
3556 throw "Unrecognised MIDI event type: " + u
3557 }
3558 }
3559 }
3560 }
3561
3562 function n(t) {
3563 this.buffer = t, this.bufferLen = this.buffer.length, this.pos = 0
3564 }
3565 n.prototype.eof = function() {
3566 return this.pos >= this.bufferLen
3567 }, n.prototype.readUInt8 = function() {
3568 var t = this.buffer[this.pos];
3569 return this.pos += 1, t
3570 }, n.prototype.readInt8 = function() {
3571 var t = this.readUInt8();
3572 return 128 & t ? t - 256 : t
3573 }, n.prototype.readUInt16 = function() {
3574 return (this.readUInt8() << 8) + this.readUInt8()
3575 }, n.prototype.readInt16 = function() {
3576 var t = this.readUInt16();
3577 return 32768 & t ? t - 65536 : t
3578 }, n.prototype.readUInt24 = function() {
3579 return (this.readUInt8() << 16) + (this.readUInt8() << 8) + this.readUInt8()
3580 }, n.prototype.readInt24 = function() {
3581 var t = this.readUInt24();
3582 return 8388608 & t ? t - 16777216 : t
3583 }, n.prototype.readUInt32 = function() {
3584 return (this.readUInt8() << 24) + (this.readUInt8() << 16) + (this.readUInt8() << 8) + this.readUInt8()
3585 }, n.prototype.readBytes = function(t) {
3586 var e = this.buffer.slice(this.pos, this.pos + t);
3587 return this.pos += t, e
3588 }, n.prototype.readString = function(t) {
3589 var e = this.readBytes(t);
3590 return String.fromCharCode.apply(null, e)
3591 }, n.prototype.readVarInt = function() {
3592 for (var t = 0; !this.eof();) {
3593 var e = this.readUInt8();
3594 if (!(128 & e)) return t + e;
3595 t += 127 & e, t <<= 7
3596 }
3597 return t
3598 }, n.prototype.readChunk = function() {
3599 var t = this.readString(4),
3600 e = this.readUInt32();
3601 return {
3602 id: t,
3603 length: e,
3604 data: this.readBytes(e)
3605 }
3606 }, t.exports = function(t) {
3607 var e = new n(t),
3608 i = e.readChunk();
3609 if ("MThd" != i.id) throw "Bad MIDI file. Expected 'MHdr', got: '" + i.id + "'";
3610 for (var a = function(t) {
3611 var e = new n(t),
3612 r = e.readUInt16(),
3613 i = e.readUInt16(),
3614 a = {
3615 format: r,
3616 numTracks: i
3617 },
3618 o = e.readUInt16();
3619 return 32768 & o ? (a.framesPerSecond = 256 - (o >> 8), a.ticksPerFrame = 255 & o) : a.ticksPerBeat = o, a
3620 }(i.data), o = [], s = 0; !e.eof() && s < a.numTracks; s++) {
3621 var u = e.readChunk();
3622 if ("MTrk" != u.id) throw "Bad MIDI file. Expected 'MTrk', got: '" + u.id + "'";
3623 var c = r(u.data);
3624 o.push(c)
3625 }
3626 return {
3627 header: a,
3628 tracks: o
3629 }
3630 }
3631 }, function(t, e) {
3632 function r(t, e, r) {
3633 var a, o = new i,
3634 s = e.length,
3635 u = null;
3636 for (a = 0; a < s; a++) !1 !== r.running && (r.running || e[a].running) || (u = null), u = n(o, e[a], u, r.useByte9ForNoteOff);
3637 t.writeChunk("MTrk", o.buffer)
3638 }
3639
3640 function n(t, e, r, n) {
3641 var i = e.type,
3642 a = e.deltaTime,
3643 o = e.text || "",
3644 s = e.data || [],
3645 u = null;
3646 switch (t.writeVarInt(a), i) {
3647 case "sequenceNumber":
3648 t.writeUInt8(255), t.writeUInt8(0), t.writeVarInt(2), t.writeUInt16(e.number);
3649 break;
3650 case "text":
3651 t.writeUInt8(255), t.writeUInt8(1), t.writeVarInt(o.length), t.writeString(o);
3652 break;
3653 case "copyrightNotice":
3654 t.writeUInt8(255), t.writeUInt8(2), t.writeVarInt(o.length), t.writeString(o);
3655 break;
3656 case "trackName":
3657 t.writeUInt8(255), t.writeUInt8(3), t.writeVarInt(o.length), t.writeString(o);
3658 break;
3659 case "instrumentName":
3660 t.writeUInt8(255), t.writeUInt8(4), t.writeVarInt(o.length), t.writeString(o);
3661 break;
3662 case "lyrics":
3663 t.writeUInt8(255), t.writeUInt8(5), t.writeVarInt(o.length), t.writeString(o);
3664 break;
3665 case "marker":
3666 t.writeUInt8(255), t.writeUInt8(6), t.writeVarInt(o.length), t.writeString(o);
3667 break;
3668 case "cuePoint":
3669 t.writeUInt8(255), t.writeUInt8(7), t.writeVarInt(o.length), t.writeString(o);
3670 break;
3671 case "channelPrefix":
3672 t.writeUInt8(255), t.writeUInt8(32), t.writeVarInt(1), t.writeUInt8(e.channel);
3673 break;
3674 case "portPrefix":
3675 t.writeUInt8(255), t.writeUInt8(33), t.writeVarInt(1), t.writeUInt8(e.port);
3676 break;
3677 case "endOfTrack":
3678 t.writeUInt8(255), t.writeUInt8(47), t.writeVarInt(0);
3679 break;
3680 case "setTempo":
3681 t.writeUInt8(255), t.writeUInt8(81), t.writeVarInt(3), t.writeUInt24(e.microsecondsPerBeat);
3682 break;
3683 case "smpteOffset":
3684 t.writeUInt8(255), t.writeUInt8(84), t.writeVarInt(5);
3685 var c = 31 & e.hour | {
3686 24: 0,
3687 25: 32,
3688 29: 64,
3689 30: 96
3690 } [e.frameRate];
3691 t.writeUInt8(c), t.writeUInt8(e.min), t.writeUInt8(e.sec), t.writeUInt8(e.frame), t.writeUInt8(e.subFrame);
3692 break;
3693 case "timeSignature":
3694 t.writeUInt8(255), t.writeUInt8(88), t.writeVarInt(4), t.writeUInt8(e.numerator);
3695 var h = 255 & Math.floor(Math.log(e.denominator) / Math.LN2);
3696 t.writeUInt8(h), t.writeUInt8(e.metronome), t.writeUInt8(e.thirtyseconds || 8);
3697 break;
3698 case "keySignature":
3699 t.writeUInt8(255), t.writeUInt8(89), t.writeVarInt(2), t.writeInt8(e.key), t.writeUInt8(e.scale);
3700 break;
3701 case "sequencerSpecific":
3702 t.writeUInt8(255), t.writeUInt8(127), t.writeVarInt(s.length), t.writeBytes(s);
3703 break;
3704 case "unknownMeta":
3705 null != e.metatypeByte && (t.writeUInt8(255), t.writeUInt8(e.metatypeByte), t.writeVarInt(s.length), t.writeBytes(s));
3706 break;
3707 case "sysEx":
3708 t.writeUInt8(240), t.writeVarInt(s.length), t.writeBytes(s);
3709 break;
3710 case "endSysEx":
3711 t.writeUInt8(247), t.writeVarInt(s.length), t.writeBytes(s);
3712 break;
3713 case "noteOff":
3714 (u = (!1 !== n && e.byte9 || n && 0 == e.velocity ? 144 : 128) | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.noteNumber), t.writeUInt8(e.velocity);
3715 break;
3716 case "noteOn":
3717 (u = 144 | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.noteNumber), t.writeUInt8(e.velocity);
3718 break;
3719 case "noteAftertouch":
3720 (u = 160 | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.noteNumber), t.writeUInt8(e.amount);
3721 break;
3722 case "controller":
3723 (u = 176 | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.controllerType), t.writeUInt8(e.value);
3724 break;
3725 case "programChange":
3726 (u = 192 | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.programNumber);
3727 break;
3728 case "channelAftertouch":
3729 (u = 208 | e.channel) !== r && t.writeUInt8(u), t.writeUInt8(e.amount);
3730 break;
3731 case "pitchBend":
3732 (u = 224 | e.channel) !== r && t.writeUInt8(u);
3733 var f = 8192 + e.value,
3734 p = 127 & f,
3735 l = f >> 7 & 127;
3736 t.writeUInt8(p), t.writeUInt8(l);
3737 break;
3738 default:
3739 throw "Unrecognized event type: " + i
3740 }
3741 return u
3742 }
3743
3744 function i() {
3745 this.buffer = []
3746 }
3747 i.prototype.writeUInt8 = function(t) {
3748 this.buffer.push(255 & t)
3749 }, i.prototype.writeInt8 = i.prototype.writeUInt8, i.prototype.writeUInt16 = function(t) {
3750 var e = t >> 8 & 255,
3751 r = 255 & t;
3752 this.writeUInt8(e), this.writeUInt8(r)
3753 }, i.prototype.writeInt16 = i.prototype.writeUInt16, i.prototype.writeUInt24 = function(t) {
3754 var e = t >> 16 & 255,
3755 r = t >> 8 & 255,
3756 n = 255 & t;
3757 this.writeUInt8(e), this.writeUInt8(r), this.writeUInt8(n)
3758 }, i.prototype.writeInt24 = i.prototype.writeUInt24, i.prototype.writeUInt32 = function(t) {
3759 var e = t >> 24 & 255,
3760 r = t >> 16 & 255,
3761 n = t >> 8 & 255,
3762 i = 255 & t;
3763 this.writeUInt8(e), this.writeUInt8(r), this.writeUInt8(n), this.writeUInt8(i)
3764 }, i.prototype.writeInt32 = i.prototype.writeUInt32, i.prototype.writeBytes = function(t) {
3765 this.buffer = this.buffer.concat(Array.prototype.slice.call(t, 0))
3766 }, i.prototype.writeString = function(t) {
3767 var e, r = t.length,
3768 n = [];
3769 for (e = 0; e < r; e++) n.push(t.codePointAt(e));
3770 this.writeBytes(n)
3771 }, i.prototype.writeVarInt = function(t) {
3772 if (t < 0) throw "Cannot write negative variable-length integer";
3773 if (t <= 127) this.writeUInt8(t);
3774 else {
3775 var e = t,
3776 r = [];
3777 for (r.push(127 & e), e >>= 7; e;) {
3778 var n = 127 & e | 128;
3779 r.push(n), e >>= 7
3780 }
3781 this.writeBytes(r.reverse())
3782 }
3783 }, i.prototype.writeChunk = function(t, e) {
3784 this.writeString(t), this.writeUInt32(e.length), this.writeBytes(e)
3785 }, t.exports = function(t, e) {
3786 if ("object" != typeof t) throw "Invalid MIDI data";
3787 e = e || {};
3788 var n, a = t.header || {},
3789 o = t.tracks || [],
3790 s = o.length,
3791 u = new i;
3792 for (function(t, e, r) {
3793 var n = null == e.format ? 1 : e.format,
3794 a = 128;
3795 e.timeDivision ? a = e.timeDivision : e.ticksPerFrame && e.framesPerSecond ? a = -(255 & e.framesPerSecond) << 8 | 255 & ticksPerFrame : e.ticksPerBeat && (a = 32767 & e.ticksPerBeat);
3796 var o = new i;
3797 o.writeUInt16(n), o.writeUInt16(r), o.writeUInt16(a), t.writeChunk("MThd", o.buffer)
3798 }(u, a, s), n = 0; n < s; n++) r(u, o[n], e);
3799 return u.buffer
3800 }
3801 }, function(t, e, r) {
3802 "use strict";
3803 var n = this && this.__spreadArrays || function() {
3804 for (var t = 0, e = 0, r = arguments.length; e < r; e++) t += arguments[e].length;
3805 var n = Array(t),
3806 i = 0;
3807 for (e = 0; e < r; e++)
3808 for (var a = arguments[e], o = 0, s = a.length; o < s; o++, i++) n[i] = a[o];
3809 return n
3810 },
3811 i = this && this.__importDefault || function(t) {
3812 return t && t.__esModule ? t : {
3813 default: t
3814 }
3815 };
3816 Object.defineProperty(e, "__esModule", {
3817 value: !0
3818 });
3819 var a = r(0),
3820 o = r(1),
3821 s = i(r(8));
3822
3823 function u(t) {
3824 return s.default(t.notes.map(function(e) {
3825 return function(t, e) {
3826 return [{
3827 absoluteTime: t.ticks,
3828 channel: e,
3829 deltaTime: 0,
3830 noteNumber: t.midi,
3831 type: "noteOn",
3832 velocity: Math.floor(127 * t.velocity)
3833 }, {
3834 absoluteTime: t.ticks + t.durationTicks,
3835 channel: e,
3836 deltaTime: 0,
3837 noteNumber: t.midi,
3838 type: "noteOff",
3839 velocity: Math.floor(127 * t.noteOffVelocity)
3840 }]
3841 }(e, t.channel)
3842 }))
3843 }
3844
3845 function c(t, e) {
3846 return {
3847 absoluteTime: t.ticks,
3848 channel: e,
3849 controllerType: t.number,
3850 deltaTime: 0,
3851 type: "controller",
3852 value: t.value
3853 }
3854 }
3855
3856 function h(t) {
3857 return {
3858 absoluteTime: 0,
3859 channel: t.channel,
3860 deltaTime: 0,
3861 programNumber: t.instrument.number,
3862 type: "programChange"
3863 }
3864 }
3865 e.encode = function(t) {
3866 var e = {
3867 header: {
3868 format: 1,
3869 numTracks: t.tracks.length + 1,
3870 ticksPerBeat: t.header.ppq
3871 },
3872 tracks: n([n([{
3873 absoluteTime: 0,
3874 deltaTime: 0,
3875 meta: !0,
3876 text: t.header.name,
3877 type: "trackName"
3878 }], t.header.keySignatures.map(function(t) {
3879 return function(t) {
3880 var e = o.keySignatureKeys.indexOf(t.key);
3881 return {
3882 absoluteTime: t.ticks,
3883 deltaTime: 0,
3884 key: e + 7,
3885 meta: !0,
3886 scale: "major" === t.scale ? 0 : 1,
3887 type: "keySignature"
3888 }
3889 }(t)
3890 }), t.header.meta.map(function(t) {
3891 return {
3892 absoluteTime: (e = t).ticks,
3893 deltaTime: 0,
3894 meta: !0,
3895 text: e.text,
3896 type: e.type
3897 };
3898 var e
3899 }), t.header.tempos.map(function(t) {
3900 return function(t) {
3901 return {
3902 absoluteTime: t.ticks,
3903 deltaTime: 0,
3904 meta: !0,
3905 microsecondsPerBeat: Math.floor(6e7 / t.bpm),
3906 type: "setTempo"
3907 }
3908 }(t)
3909 }), t.header.timeSignatures.map(function(t) {
3910 return function(t) {
3911 return {
3912 absoluteTime: t.ticks,
3913 deltaTime: 0,
3914 denominator: t.timeSignature[1],
3915 meta: !0,
3916 metronome: 24,
3917 numerator: t.timeSignature[0],
3918 thirtyseconds: 8,
3919 type: "timeSignature"
3920 }
3921 }(t)
3922 }))], t.tracks.map(function(t) {
3923 return n([(e = t.name, {
3924 absoluteTime: 0,
3925 deltaTime: 0,
3926 meta: !0,
3927 text: e,
3928 type: "trackName"
3929 }), h(t)], u(t), function(t) {
3930 for (var e = [], r = 0; r < 127; r++) t.controlChanges.hasOwnProperty(r) && t.controlChanges[r].forEach(function(r) {
3931 e.push(c(r, t.channel))
3932 });
3933 return e
3934 }(t), function(t) {
3935 var e = [];
3936 return t.pitchBends.forEach(function(r) {
3937 e.push(function(t, e) {
3938 return {
3939 absoluteTime: t.ticks,
3940 channel: e,
3941 deltaTime: 0,
3942 type: "pitchBend",
3943 value: t.value
3944 }
3945 }(r, t.channel))
3946 }), e
3947 }(t));
3948 var e
3949 }))
3950 };
3951 return e.tracks = e.tracks.map(function(t) {
3952 t = t.sort(function(t, e) {
3953 return t.absoluteTime - e.absoluteTime
3954 });
3955 var e = 0;
3956 return t.forEach(function(t) {
3957 t.deltaTime = t.absoluteTime - e, e = t.absoluteTime, delete t.absoluteTime
3958 }), t.push({
3959 deltaTime: 0,
3960 meta: !0,
3961 type: "endOfTrack"
3962 }), t
3963 }), new Uint8Array(a.writeMidi(e))
3964 }
3965 }, function(t, e, r) {
3966 "use strict";
3967
3968 function n(t) {
3969 return function t(e, r) {
3970 for (var n = 0; n < e.length; n++) {
3971 var i = e[n];
3972 Array.isArray(i) ? t(i, r) : r.push(i)
3973 }
3974 return r
3975 }(t, [])
3976 }
3977
3978 function i(t, e) {
3979 if ("number" != typeof e) throw new TypeError("Expected the depth to be a number");
3980 return function t(e, r, n) {
3981 n--;
3982 for (var i = 0; i < e.length; i++) {
3983 var a = e[i];
3984 n > -1 && Array.isArray(a) ? t(a, r, n) : r.push(a)
3985 }
3986 return r
3987 }(t, [], e)
3988 }
3989 t.exports = function(t) {
3990 if (!Array.isArray(t)) throw new TypeError("Expected value to be an array");
3991 return n(t)
3992 }, t.exports.from = n, t.exports.depth = function(t, e) {
3993 if (!Array.isArray(t)) throw new TypeError("Expected value to be an array");
3994 return i(t, e)
3995 }, t.exports.fromDepth = i
3996 }, function(t, e, r) {
3997 "use strict";
3998 Object.defineProperty(e, "__esModule", {
3999 value: !0
4000 });
4001 var n = r(2),
4002 i = r(3),
4003 a = r(10),
4004 o = r(11),
4005 s = r(12),
4006 u = r(14),
4007 c = new WeakMap,
4008 h = function() {
4009 function t(t, e) {
4010 var r = this;
4011 if (this.name = "", this.notes = [], this.controlChanges = a.createControlChanges(), this.pitchBends = [], c.set(this, e), t) {
4012 var n = t.find(function(t) {
4013 return "trackName" === t.type
4014 });
4015 this.name = n ? n.text : ""
4016 }
4017 if (this.instrument = new s.Instrument(t, this), this.channel = 0, t) {
4018 for (var i = t.filter(function(t) {
4019 return "noteOn" === t.type
4020 }), o = t.filter(function(t) {
4021 return "noteOff" === t.type
4022 }), u = function() {
4023 var t = i.shift();
4024 h.channel = t.channel;
4025 var e = o.findIndex(function(e) {
4026 return e.noteNumber === t.noteNumber && e.absoluteTime >= t.absoluteTime
4027 });
4028 if (-1 !== e) {
4029 var r = o.splice(e, 1)[0];
4030 h.addNote({
4031 durationTicks: r.absoluteTime - t.absoluteTime,
4032 midi: t.noteNumber,
4033 noteOffVelocity: r.velocity / 127,
4034 ticks: t.absoluteTime,
4035 velocity: t.velocity / 127
4036 })
4037 }
4038 }, h = this; i.length;) u();
4039 t.filter(function(t) {
4040 return "controller" === t.type
4041 }).forEach(function(t) {
4042 r.addCC({
4043 number: t.controllerType,
4044 ticks: t.absoluteTime,
4045 value: t.value / 127
4046 })
4047 }), t.filter(function(t) {
4048 return "pitchBend" === t.type
4049 }).forEach(function(t) {
4050 r.addPitchBend({
4051 ticks: t.absoluteTime,
4052 value: t.value / Math.pow(2, 13)
4053 })
4054 })
4055 }
4056 }
4057 return t.prototype.addNote = function(t) {
4058 var e = c.get(this),
4059 r = new u.Note({
4060 midi: 0,
4061 ticks: 0,
4062 velocity: 1
4063 }, {
4064 ticks: 0,
4065 velocity: 0
4066 }, e);
4067 return Object.assign(r, t), n.insert(this.notes, r, "ticks"), this
4068 }, t.prototype.addCC = function(t) {
4069 var e = c.get(this),
4070 r = new i.ControlChange({
4071 controllerType: t.number
4072 }, e);
4073 return delete t.number, Object.assign(r, t), Array.isArray(this.controlChanges[r.number]) || (this.controlChanges[r.number] = []), n.insert(this.controlChanges[r.number], r, "ticks"), this
4074 }, t.prototype.addPitchBend = function(t) {
4075 var e = c.get(this),
4076 r = new o.PitchBend({}, e);
4077 return Object.assign(r, t), n.insert(this.pitchBends, r, "ticks"), this
4078 }, Object.defineProperty(t.prototype, "duration", {
4079 get: function() {
4080 var t = this.notes[this.notes.length - 1];
4081 return t ? t.time + t.duration : 0
4082 },
4083 enumerable: !0,
4084 configurable: !0
4085 }), Object.defineProperty(t.prototype, "durationTicks", {
4086 get: function() {
4087 var t = this.notes[this.notes.length - 1];
4088 return t ? t.ticks + t.durationTicks : 0
4089 },
4090 enumerable: !0,
4091 configurable: !0
4092 }), t.prototype.fromJSON = function(t) {
4093 var e = this;
4094 for (var r in this.name = t.name, this.channel = t.channel, this.instrument = new s.Instrument(void 0, this), this.instrument.fromJSON(t.instrument), t.controlChanges) t.controlChanges[r] && t.controlChanges[r].forEach(function(t) {
4095 e.addCC({
4096 number: t.number,
4097 ticks: t.ticks,
4098 value: t.value
4099 })
4100 });
4101 t.notes.forEach(function(t) {
4102 e.addNote({
4103 durationTicks: t.durationTicks,
4104 midi: t.midi,
4105 ticks: t.ticks,
4106 velocity: t.velocity
4107 })
4108 })
4109 }, t.prototype.toJSON = function() {
4110 for (var t = {}, e = 0; e < 127; e++) this.controlChanges.hasOwnProperty(e) && (t[e] = this.controlChanges[e].map(function(t) {
4111 return t.toJSON()
4112 }));
4113 return {
4114 channel: this.channel,
4115 controlChanges: t,
4116 pitchBends: this.pitchBends.map(function(t) {
4117 return t.toJSON()
4118 }),
4119 instrument: this.instrument.toJSON(),
4120 name: this.name,
4121 notes: this.notes.map(function(t) {
4122 return t.toJSON()
4123 })
4124 }
4125 }, t
4126 }();
4127 e.Track = h
4128 }, function(t, e, r) {
4129 "use strict";
4130 Object.defineProperty(e, "__esModule", {
4131 value: !0
4132 });
4133 var n = r(3);
4134 e.createControlChanges = function() {
4135 return new Proxy({}, {
4136 get: function(t, e) {
4137 return t[e] ? t[e] : n.controlChangeIds.hasOwnProperty(e) ? t[n.controlChangeIds[e]] : void 0
4138 },
4139 set: function(t, e, r) {
4140 return n.controlChangeIds.hasOwnProperty(e) ? t[n.controlChangeIds[e]] = r : t[e] = r, !0
4141 }
4142 })
4143 }
4144 }, function(t, e, r) {
4145 "use strict";
4146 Object.defineProperty(e, "__esModule", {
4147 value: !0
4148 });
4149 var n = new WeakMap,
4150 i = function() {
4151 function t(t, e) {
4152 n.set(this, e), this.ticks = t.absoluteTime, this.value = t.value
4153 }
4154 return Object.defineProperty(t.prototype, "time", {
4155 get: function() {
4156 return n.get(this).ticksToSeconds(this.ticks)
4157 },
4158 set: function(t) {
4159 var e = n.get(this);
4160 this.ticks = e.secondsToTicks(t)
4161 },
4162 enumerable: !0,
4163 configurable: !0
4164 }), t.prototype.toJSON = function() {
4165 return {
4166 ticks: this.ticks,
4167 time: this.time,
4168 value: this.value
4169 }
4170 }, t
4171 }();
4172 e.PitchBend = i
4173 }, function(t, e, r) {
4174 "use strict";
4175 Object.defineProperty(e, "__esModule", {
4176 value: !0
4177 });
4178 var n = r(13),
4179 i = new WeakMap,
4180 a = function() {
4181 function t(t, e) {
4182 if (this.number = 0, i.set(this, e), this.number = 0, t) {
4183 var r = t.find(function(t) {
4184 return "programChange" === t.type
4185 });
4186 r && (this.number = r.programNumber)
4187 }
4188 }
4189 return Object.defineProperty(t.prototype, "name", {
4190 get: function() {
4191 return this.percussion ? n.DrumKitByPatchID[this.number] : n.instrumentByPatchID[this.number]
4192 },
4193 set: function(t) {
4194 var e = n.instrumentByPatchID.indexOf(t); - 1 !== e && (this.number = e)
4195 },
4196 enumerable: !0,
4197 configurable: !0
4198 }), Object.defineProperty(t.prototype, "family", {
4199 get: function() {
4200 return this.percussion ? "drums" : n.InstrumentFamilyByID[Math.floor(this.number / 8)]
4201 },
4202 enumerable: !0,
4203 configurable: !0
4204 }), Object.defineProperty(t.prototype, "percussion", {
4205 get: function() {
4206 return 9 === i.get(this).channel
4207 },
4208 enumerable: !0,
4209 configurable: !0
4210 }), t.prototype.toJSON = function() {
4211 return {
4212 family: this.family,
4213 name: this.name,
4214 number: this.number
4215 }
4216 }, t.prototype.fromJSON = function(t) {
4217 this.number = t.number
4218 }, t
4219 }();
4220 e.Instrument = a
4221 }, function(t, e, r) {
4222 "use strict";
4223 Object.defineProperty(e, "__esModule", {
4224 value: !0
4225 }), e.instrumentByPatchID = ["acoustic grand piano", "bright acoustic piano", "electric grand piano", "honky-tonk piano", "electric piano 1", "electric piano 2", "harpsichord", "clavi", "celesta", "glockenspiel", "music box", "vibraphone", "marimba", "xylophone", "tubular bells", "dulcimer", "drawbar organ", "percussive organ", "rock organ", "church organ", "reed organ", "accordion", "harmonica", "tango accordion", "acoustic guitar (nylon)", "acoustic guitar (steel)", "electric guitar (jazz)", "electric guitar (clean)", "electric guitar (muted)", "overdriven guitar", "distortion guitar", "guitar harmonics", "acoustic bass", "electric bass (finger)", "electric bass (pick)", "fretless bass", "slap bass 1", "slap bass 2", "synth bass 1", "synth bass 2", "violin", "viola", "cello", "contrabass", "tremolo strings", "pizzicato strings", "orchestral harp", "timpani", "string ensemble 1", "string ensemble 2", "synthstrings 1", "synthstrings 2", "choir aahs", "voice oohs", "synth voice", "orchestra hit", "trumpet", "trombone", "tuba", "muted trumpet", "french horn", "brass section", "synthbrass 1", "synthbrass 2", "soprano sax", "alto sax", "tenor sax", "baritone sax", "oboe", "english horn", "bassoon", "clarinet", "piccolo", "flute", "recorder", "pan flute", "blown bottle", "shakuhachi", "whistle", "ocarina", "lead 1 (square)", "lead 2 (sawtooth)", "lead 3 (calliope)", "lead 4 (chiff)", "lead 5 (charang)", "lead 6 (voice)", "lead 7 (fifths)", "lead 8 (bass + lead)", "pad 1 (new age)", "pad 2 (warm)", "pad 3 (polysynth)", "pad 4 (choir)", "pad 5 (bowed)", "pad 6 (metallic)", "pad 7 (halo)", "pad 8 (sweep)", "fx 1 (rain)", "fx 2 (soundtrack)", "fx 3 (crystal)", "fx 4 (atmosphere)", "fx 5 (brightness)", "fx 6 (goblins)", "fx 7 (echoes)", "fx 8 (sci-fi)", "sitar", "banjo", "shamisen", "koto", "kalimba", "bag pipe", "fiddle", "shanai", "tinkle bell", "agogo", "steel drums", "woodblock", "taiko drum", "melodic tom", "synth drum", "reverse cymbal", "guitar fret noise", "breath noise", "seashore", "bird tweet", "telephone ring", "helicopter", "applause", "gunshot"], e.InstrumentFamilyByID = ["piano", "chromatic percussion", "organ", "guitar", "bass", "strings", "ensemble", "brass", "reed", "pipe", "synth lead", "synth pad", "synth effects", "world", "percussive", "sound effects"], e.DrumKitByPatchID = {
4226 0: "standard kit",
4227 8: "room kit",
4228 16: "power kit",
4229 24: "electronic kit",
4230 25: "tr-808 kit",
4231 32: "jazz kit",
4232 40: "brush kit",
4233 48: "orchestra kit",
4234 56: "sound fx kit"
4235 }
4236 }, function(t, e, r) {
4237 "use strict";
4238
4239 function n(t) {
4240 return ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"][t % 12]
4241 }
4242 Object.defineProperty(e, "__esModule", {
4243 value: !0
4244 });
4245 var i, a, o = (i = /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i, a = {
4246 cbb: -2,
4247 cb: -1,
4248 c: 0,
4249 "c#": 1,
4250 cx: 2,
4251 dbb: 0,
4252 db: 1,
4253 d: 2,
4254 "d#": 3,
4255 dx: 4,
4256 ebb: 2,
4257 eb: 3,
4258 e: 4,
4259 "e#": 5,
4260 ex: 6,
4261 fbb: 3,
4262 fb: 4,
4263 f: 5,
4264 "f#": 6,
4265 fx: 7,
4266 gbb: 5,
4267 gb: 6,
4268 g: 7,
4269 "g#": 8,
4270 gx: 9,
4271 abb: 7,
4272 ab: 8,
4273 a: 9,
4274 "a#": 10,
4275 ax: 11,
4276 bbb: 9,
4277 bb: 10,
4278 b: 11,
4279 "b#": 12,
4280 bx: 13
4281 }, function(t) {
4282 var e = i.exec(t),
4283 r = e[1],
4284 n = e[2];
4285 return a[r.toLowerCase()] + 12 * (parseInt(n, 10) + 1)
4286 }),
4287 s = new WeakMap,
4288 u = function() {
4289 function t(t, e, r) {
4290 s.set(this, r), this.midi = t.midi, this.velocity = t.velocity, this.noteOffVelocity = e.velocity, this.ticks = t.ticks, this.durationTicks = e.ticks - t.ticks
4291 }
4292 return Object.defineProperty(t.prototype, "name", {
4293 get: function() {
4294 return t = this.midi, e = Math.floor(t / 12) - 1, n(t) + e.toString();
4295 var t, e
4296 },
4297 set: function(t) {
4298 this.midi = o(t)
4299 },
4300 enumerable: !0,
4301 configurable: !0
4302 }), Object.defineProperty(t.prototype, "octave", {
4303 get: function() {
4304 return Math.floor(this.midi / 12) - 1
4305 },
4306 set: function(t) {
4307 var e = t - this.octave;
4308 this.midi += 12 * e
4309 },
4310 enumerable: !0,
4311 configurable: !0
4312 }), Object.defineProperty(t.prototype, "pitch", {
4313 get: function() {
4314 return n(this.midi)
4315 },
4316 set: function(t) {
4317 this.midi = 12 * (this.octave + 1) + ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"].indexOf(t)
4318 },
4319 enumerable: !0,
4320 configurable: !0
4321 }), Object.defineProperty(t.prototype, "duration", {
4322 get: function() {
4323 var t = s.get(this);
4324 return t.ticksToSeconds(this.ticks + this.durationTicks) - t.ticksToSeconds(this.ticks)
4325 },
4326 set: function(t) {
4327 var e = s.get(this).secondsToTicks(this.time + t);
4328 this.durationTicks = e - this.ticks
4329 },
4330 enumerable: !0,
4331 configurable: !0
4332 }), Object.defineProperty(t.prototype, "time", {
4333 get: function() {
4334 return s.get(this).ticksToSeconds(this.ticks)
4335 },
4336 set: function(t) {
4337 var e = s.get(this);
4338 this.ticks = e.secondsToTicks(t)
4339 },
4340 enumerable: !0,
4341 configurable: !0
4342 }), Object.defineProperty(t.prototype, "bars", {
4343 get: function() {
4344 return s.get(this).ticksToMeasures(this.ticks)
4345 },
4346 enumerable: !0,
4347 configurable: !0
4348 }), t.prototype.toJSON = function() {
4349 return {
4350 duration: this.duration,
4351 durationTicks: this.durationTicks,
4352 midi: this.midi,
4353 name: this.name,
4354 ticks: this.ticks,
4355 time: this.time,
4356 velocity: this.velocity
4357 }
4358 }, t
4359 }();
4360 e.Note = u
4361 }])
4362 });
4363 //# sourceMappingURL=Midi.js.map
4364
4365
4366 if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
4367 document.querySelector("#FileDrop #Text").textContent = "Reading files not supported by this browser";
4368 } else {
4369 const fileDrop = document.querySelector("#FileDrop")
4370
4371 document.querySelector("#FileDrop input").addEventListener("change", e => {
4372 //get the files
4373 const files = e.target.files
4374 if (files.length > 0) {
4375 const file = files[0]
4376 document.querySelector("#FileDrop #Text").textContent = file.name
4377 parseFile(file)
4378 }
4379 })
4380 }
4381
4382 function parseFile(file) {
4383 //read the file
4384 const reader = new FileReader()
4385 reader.onload = function(e) {
4386 const midi = new Midi(e.target.result)
4387 document.querySelector("#ResultsText").value = JSON.stringify(midi, undefined, 2)
4388 midiJSONobject = JSON.parse(document.querySelector("#ResultsText").value)
4389
4390 }
4391 reader.readAsArrayBuffer(file)
4392 }
4393
4394
4395
4396
4397 // CODE FOR THE AUTOPLAYER:
4398
4399 var runningTimeoutes = {
4400 press: [],
4401 release: []
4402 } // ALL THE TIMEOUTS CREATED ARE STORED IN runningTimeoutes
4403
4404 var trackPlayer = (track) => { // THE trackPlayer FUNCTION CHECK ALL NOTES OF A TRACK (TRACKS ARE LIKE EACH INSTRUMENT OF THE MIDI FILE)
4405 for (i = 0; i < track.notes.length; i++) { // THIS FOR GO THRU ALL NOTES
4406 (function(i) {
4407 let thisnote = track.notes[i];
4408
4409 let noteObj = { // THE NOTES ARE STORED IN noteObj
4410 key: thisnote.name.replace("#", "s").toLowerCase().slice(0, thisnote.name.replace("#", "s").toLowerCase().length - 1), // THE NOTE ITSELF
4411 octave: parseFloat(thisnote.name.replace("#", "s").toLowerCase().slice(thisnote.name.replace("#", "s").toLowerCase().length - 1)) - 1 // AND THE OCTAVE (THE -1 ITS BECOUSE THE LOWEWEST OCTAVE FROM TONE.JS IS 0 AND IN MPP IS -1)
4412 }
4413
4414 runningTimeoutes.press.push(setTimeout(() => { // SET THE TIMEOUTS TO PLAY THE NOTES AND STORE THE TIMEOUTS FOR PRESSED NOTES IN runningTimeoutes.press
4415 press(noteObj.key + noteObj.octave, thisnote.velocity) // PLAY THE NOTE (YOU CAN CHANGE THE SECOND ELEMENT TO PLAY IT LOUDLY OR CHANGE THE OCTAVE OR ETC..)
4416
4417 runningTimeoutes.release.push(setTimeout(() => { // SET THE TIMEOUTS TO RELEASE THE NOTES AND STORE THE TIMEOUTS FOR RELEASED NOTES IN runningTimeoutes.release
4418 release(noteObj.key + noteObj.octave) // RELEASE THE NOTES (WHAT YOU CHANGE IN THE PLAY )
4419 }, thisnote.duration * 1000))
4420 }, thisnote.time * 1000)) // YOU CAN CHANGE THE TIMEOUT TIME AND MAKE IT PLAY FASTER OR SLOWER
4421 })(i);
4422 }
4423 }
4424
4425
4426 var autoPlayer = (song) => { // THE autoPlayer FUNCTION GOES THRU ALL TRACKS AND SEND THEN TO trackPlayer, IT ALSO SET THE TIMEOUT TO AUTO STOP THE SONG ON ITS END
4427 botAutoPlayingDuration = 0; // THIS IS THE DURATION OF THE FILE
4428 for (j = 0; j < song.tracks.length; j++) { // SET A for TO GO THRU ALL TRACKS OF THE FILE
4429 trackPlayer(song.tracks[j]); // SENDS THE TRACK TO trackPlayer WHERE THE NOTES OF THIS TRACK WILL BE SETED TO BE PLAYED
4430
4431 if (song.tracks[j].notes[song.tracks[j].notes.length - 1].time > botAutoPlayingDuration) { // CHACKS WHATS THE LONGEST NOTE IN ALL TRACK TO SET THE END OF THE SONG
4432 botAutoPlayingDuration = song.tracks[j].notes[song.tracks[j].notes.length - 1].time
4433 }
4434 }
4435
4436 setTimeout(() => { // THIS TIMEOUT SETS THE END OF THE SONG
4437 runningTimeoutes.press = [];
4438 runningTimeoutes.release = [];
4439 }, botAutoPlayingDuration * 1000)
4440 }
4441
4442
4443 var stopAutoPlay = () => { // THE stopAutoPlay MAKES THE SONG STOP (THE SONG GOES BACK TO IT'S START WHEN IS STOPED)
4444 for (i = 0; i < runningTimeoutes.press.length; i++) { //LOOP THRU runningTimeoutes.press ARRAY AND clearInterval OF THEM
4445 clearInterval(runningTimeoutes.press[i])
4446 }
4447 for (i = 0; i < runningTimeoutes.release.length; i++) { //LOOP THRU runningTimeoutes.release ARRAY AND clearInterval OF THEM
4448 clearInterval(runningTimeoutes.release[i])
4449 }
4450 };
4451
4452
4453
4454 // record mp3
4455 (function() {
4456 var button = document.querySelector("#record-btn");
4457 var audio = MPP.piano.audio;
4458 var context = audio.context;
4459 var encoder_sample_rate = 44100;
4460 var encoder_kbps = 128;
4461 var encoder = null;
4462 var scriptProcessorNode = context.createScriptProcessor(4096, 2, 2);
4463 var recording = false;
4464 var recording_start_time = 0;
4465 var mp3_buffer = [];
4466 button.addEventListener("click", function(evt) {
4467 if (!recording) {
4468 // start recording
4469 mp3_buffer = [];
4470 encoder = new lamejs.Mp3Encoder(2, encoder_sample_rate, encoder_kbps);
4471 scriptProcessorNode.onaudioprocess = onAudioProcess;
4472 audio.masterGain.connect(scriptProcessorNode);
4473 scriptProcessorNode.connect(context.destination);
4474 recording_start_time = Date.now();
4475 recording = true;
4476 button.textContent = "Stop Recording";
4477 button.classList.add("stuck");
4478 new Notification({
4479 "id": "mp3",
4480 "title": "Recording MP3...",
4481 "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>.",
4482 "duration": 10000
4483 });
4484 } else {
4485 // stop recording
4486 var mp3buf = encoder.flush();
4487 mp3_buffer.push(mp3buf);
4488 var blob = new Blob(mp3_buffer, {
4489 type: "audio/mp3"
4490 });
4491 var url = URL.createObjectURL(blob);
4492 scriptProcessorNode.onaudioprocess = null;
4493 audio.masterGain.disconnect(scriptProcessorNode);
4494 scriptProcessorNode.disconnect(context.destination);
4495 recording = false;
4496 button.textContent = "Record MP3";
4497 button.classList.remove("stuck");
4498 new Notification({
4499 "id": "mp3",
4500 "title": "MP3 recording finished",
4501 "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>.",
4502 "duration": 0
4503 });
4504 }
4505 });
4506
4507 function onAudioProcess(evt) {
4508 var inputL = evt.inputBuffer.getChannelData(0);
4509 var inputR = evt.inputBuffer.getChannelData(1);
4510 var mp3buf = encoder.encodeBuffer(convert16(inputL), convert16(inputR));
4511 mp3_buffer.push(mp3buf);
4512 }
4513
4514 function convert16(samples) {
4515 var len = samples.length;
4516 var result = new Int16Array(len);
4517 for (var i = 0; i < len; i++) {
4518 result[i] = 0x8000 * samples[i];
4519 }
4520 return (result);
4521 }
4522 })();
4523
4524
4525
4526
4527 // synth
4528 var enableSynth = false;
4529 var audio = gPiano.audio;
4530 var context = gPiano.audio.context;
4531 var synth_gain = context.createGain();
4532 synth_gain.gain.value = 0.05;
4533 synth_gain.connect(audio.synthGain);
4534
4535 var osc_types = ["sine", "square", "sawtooth", "triangle"];
4536 var osc_type_index = 1;
4537 var target_id = "6f0779aa7cb821449d9317d6"; //Target circle follow
4538
4539 let cursorMode = "DVD"; // DVD, CirclingCursor
4540
4541 var osc1_type = "square";
4542 var osc1_attack = 0;
4543 var osc1_decay = 0.2;
4544 var osc1_sustain = 0.5;
4545 var osc1_release = 2.0;
4546
4547 function synthVoice(note_name, time) {
4548 var note_number = MIDI_KEY_NAMES.indexOf(note_name);
4549 note_number = note_number + 9 - MIDI_TRANSPOSE;
4550 var freq = Math.pow(2, (note_number - 69) / 12) * 440.0;
4551 this.osc = context.createOscillator();
4552 this.osc.type = osc1_type;
4553 this.osc.frequency.value = freq;
4554 this.gain = context.createGain();
4555 this.gain.gain.value = 0;
4556 this.osc.connect(this.gain);
4557 this.gain.connect(synth_gain);
4558 this.osc.start(time);
4559 this.gain.gain.setValueAtTime(0, time);
4560 this.gain.gain.linearRampToValueAtTime(1, time + osc1_attack);
4561 this.gain.gain.linearRampToValueAtTime(osc1_sustain, time + osc1_attack + osc1_decay);
4562 }
4563
4564 synthVoice.prototype.stop = function(time) {
4565 //this.gain.gain.setValueAtTime(osc1_sustain, time);
4566 this.gain.gain.linearRampToValueAtTime(0, time + osc1_release);
4567 this.osc.stop(time + osc1_release);
4568 };
4569
4570 (function() {
4571 var button = document.getElementById("synth-btn");
4572 var notification;
4573
4574 button.addEventListener("click", function() {
4575 if (notification) {
4576 notification.close();
4577 } else {
4578 showSynth();
4579 }
4580 });
4581
4582 function showSynth() {
4583
4584 var html = document.createElement("div");
4585
4586 // on/off button
4587 (function() {
4588 var button = document.createElement("input");
4589 mixin(button, {
4590 type: "button",
4591 value: "ON/OFF",
4592 className: enableSynth ? "switched-on" : "switched-off"
4593 });
4594 button.addEventListener("click", function(evt) {
4595 enableSynth = !enableSynth;
4596 button.className = enableSynth ? "switched-on" : "switched-off";
4597 if (!enableSynth) {
4598 // stop all
4599 for (var i in audio.playings) {
4600 if (!audio.playings.hasOwnProperty(i)) continue;
4601 var playing = audio.playings[i];
4602 if (playing && playing.voice) {
4603 playing.voice.osc.stop();
4604 playing.voice = undefined;
4605 }
4606 }
4607 }
4608 });
4609 html.appendChild(button);
4610 })();
4611
4612 // mix
4613 var knob = document.createElement("canvas");
4614 mixin(knob, {
4615 width: 32 * window.devicePixelRatio,
4616 height: 32 * window.devicePixelRatio,
4617 className: "knob"
4618 });
4619 html.appendChild(knob);
4620 knob = new Knob(knob, 0, 100, 0.1, 50, "mix", "%");
4621 knob.canvas.style.width = "32px";
4622 knob.canvas.style.height = "32px";
4623 knob.on("change", function(k) {
4624 var mix = k.value / 100;
4625 audio.pianoGain.gain.value = 1 - mix;
4626 audio.synthGain.gain.value = mix;
4627 });
4628 knob.emit("change", knob);
4629
4630 // osc1 type
4631 (function() {
4632 osc1_type = osc_types[osc_type_index];
4633 var button = document.createElement("input");
4634 mixin(button, {
4635 type: "button",
4636 value: osc_types[osc_type_index]
4637 });
4638 button.addEventListener("click", function(evt) {
4639 if (++osc_type_index >= osc_types.length) osc_type_index = 0;
4640 osc1_type = osc_types[osc_type_index];
4641 button.value = osc1_type;
4642 });
4643 html.appendChild(button);
4644 })();
4645
4646 // osc1 attack
4647 var knob = document.createElement("canvas");
4648 mixin(knob, {
4649 width: 32 * window.devicePixelRatio,
4650 height: 32 * window.devicePixelRatio,
4651 className: "knob"
4652 });
4653 html.appendChild(knob);
4654 knob = new Knob(knob, 0, 1, 0.001, osc1_attack, "osc1 attack", "s");
4655 knob.canvas.style.width = "32px";
4656 knob.canvas.style.height = "32px";
4657 knob.on("change", function(k) {
4658 osc1_attack = k.value;
4659 });
4660 knob.emit("change", knob);
4661
4662 // osc1 decay
4663 var knob = document.createElement("canvas");
4664 mixin(knob, {
4665 width: 32 * window.devicePixelRatio,
4666 height: 32 * window.devicePixelRatio,
4667 className: "knob"
4668 });
4669 html.appendChild(knob);
4670 knob = new Knob(knob, 0, 2, 0.001, osc1_decay, "osc1 decay", "s");
4671 knob.canvas.style.width = "32px";
4672 knob.canvas.style.height = "32px";
4673 knob.on("change", function(k) {
4674 osc1_decay = k.value;
4675 });
4676 knob.emit("change", knob);
4677
4678 var knob = document.createElement("canvas");
4679 mixin(knob, {
4680 width: 32 * window.devicePixelRatio,
4681 height: 32 * window.devicePixelRatio,
4682 className: "knob"
4683 });
4684 html.appendChild(knob);
4685 knob = new Knob(knob, 0, 1, 0.001, osc1_sustain, "osc1 sustain", "x");
4686 knob.canvas.style.width = "32px";
4687 knob.canvas.style.height = "32px";
4688 knob.on("change", function(k) {
4689 osc1_sustain = k.value;
4690 });
4691 knob.emit("change", knob);
4692
4693 // osc1 release
4694 var knob = document.createElement("canvas");
4695 mixin(knob, {
4696 width: 32 * window.devicePixelRatio,
4697 height: 32 * window.devicePixelRatio,
4698 className: "knob"
4699 });
4700 html.appendChild(knob);
4701 knob = new Knob(knob, 0, 2, 0.001, osc1_release, "osc1 release", "s");
4702 knob.canvas.style.width = "32px";
4703 knob.canvas.style.height = "32px";
4704 knob.on("change", function(k) {
4705 osc1_release = k.value;
4706 });
4707 knob.emit("change", knob);
4708
4709
4710
4711 var div = document.createElement("div");
4712 div.innerHTML = "<br><br><br><br><center>this space intentionally left blank</center><br><br><br><br>";
4713 html.appendChild(div);
4714
4715
4716
4717 // notification
4718 notification = new Notification({
4719 title: "Synthesize",
4720 html: html,
4721 duration: -1,
4722 target: "#synth-btn"
4723 });
4724 notification.on("close", function() {
4725 var tip = document.getElementById("tooltip");
4726 if (tip) tip.parentNode.removeChild(tip);
4727 notification = null;
4728 });
4729 }
4730 })();
4731
4732
4733
4734
4735});
4736
4737
4738
4739function grbUsr(target) {
4740 for (var id in MPP.client.ppl) {
4741 if (!MPP.client.ppl.hasOwnProperty(id)) continue;
4742 var part = MPP.client.ppl[id];
4743 if (part.name.toLowerCase().indexOf(target.toLowerCase()) !== -1 || part._id.indexOf(target.toLowerCase()) !== -1 || part.id.indexOf(target.toLowerCase()) !== -1) {
4744 return part;
4745 break;
4746
4747 }
4748 }
4749}
4750
4751
4752function timeDate() {
4753 var currentDate = new Date()
4754 var day = currentDate.getDate()
4755 var month = currentDate.getMonth() + 1
4756 var year = currentDate.getFullYear()
4757 return day + '/' + month + '/' + year;
4758}
4759
4760function time12HR() {
4761 var now = new Date();
4762 var hh = now.getHours();
4763 var min = now.getMinutes();
4764 var sec = now.getSeconds();
4765
4766 var ampm = (hh >= 12) ? 'PM' : 'AM';
4767 hh = hh % 12;
4768 hh = hh ? hh : 12;
4769 hh = hh < 10 ? '0' + hh : hh;
4770 min = min < 10 ? '0' + min : min;
4771 sec = sec < 10 ? '0' + sec : sec;
4772
4773 return hh + ":" + min + ":" + sec + " " + ampm;
4774
4775}
4776var phoenix = ["7ed17fc2964e5a61c5aba937"];
4777var admins = ["7ed17fc2964e5a61c5aba937", "3d34d4352eca294aa727412c", "6239942fbf662ff8826c78d2"];
4778var banned = [""]; //Add IDs here.
4779var friends = {
4780 "1": ["2", "3"]
4781};
4782let fishCaught = 0;
4783//var currentName__IDs = {"7ed17fc2964e5a61c5aba937": "??????? [!help]"};
4784var bot = true; //Leave true.
4785//ATLAS cchar system
4786var alreadyadmin = ["Sorry, you are already admin.", "Silly goose, you are already an admin.", "Last time I checked, you are already admin.", "Bruh, really, ur already an admin."];
4787//It's here. Not sure why it's not randomizing. One sec, I'll grab mine.
4788var eightball = ["Don’t count on it.", "As I see it, yes.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "It is certain.", "It is decidedly so.", "Most likely.", "My reply is no.", "Outlook not so good.", "Signs point to yes."];
4789var rolldice = ["⚀ ⚀", "⚀ ⚁", "⚀ ⚂", "⚀ ⚂", "⚀ ⚃", "⚀ ⚄", "⚀ ⚅", "⚁ ⚁", "⚁ ⚂", "⚁ ⚂", "⚁ ⚃", "⚁ ⚄", "⚁ ⚅", "⚂ ⚁", "⚂ ⚂", "⚂ ⚂", "⚂ ⚃", "⚂ ⚄", "⚂ ⚅", "⚃ ⚁", "⚃ ⚂", "⚃ ⚂", "⚃ ⚃", "⚃ ⚄", "⚃ ⚅", "⚄ ⚁", "⚄ ⚂", "⚄ ⚂", "⚄ ⚃", "⚄ ⚄", "⚄ ⚅", "⚅ ⚁", "⚅ ⚂", "⚅ ⚂", "⚅ ⚃", "⚅ ⚄", "⚅ ⚅", ];
4790
4791naiveReverse = function(string) { //Reverse function
4792 /* Do not touch this. But credit to Cmds for emoji support. */
4793 return string.replace(/([\uD800-\uDBFF])([\uDC00-\uDFFF])/g, "$2$1").split("").reverse().join("");
4794};
4795
4796var googleCommand = true;
4797var googleCommandDelay = 3;
4798
4799var div = document.createElement("div");
4800div.id = "Div1";
4801div.style.display = "none";
4802document.body.appendChild(div);
4803
4804function colorName(hex) {
4805 var str = new Color(hex).getName();
4806 return str;
4807}
4808
4809
4810function getShortDate() {
4811 var date = Date();
4812 var dpart = date.split(" ");
4813 var shortdate = dpart[0] + " " + dpart[1] + " " + dpart[2] + " " + dpart[3] + " " + dpart[4] + " " + dpart[5].slice(0, -2);
4814 return shortdate;
4815}
4816
4817(function() {
4818 var cx = '010021570394457971158:qajzusztxwo';
4819 var gcse = document.createElement('script');
4820 gcse.type = 'text/javascript';
4821 gcse.async = true;
4822 gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
4823 '//www.google.com/cse/cse.js?cx=' + cx;
4824 var s = document.getElementsByTagName('script')[0];
4825 s.parentNode.insertBefore(gcse, s);
4826})();
4827
4828function gcseCallback(query) {
4829 if (document.readyState != 'complete')
4830 return google.setOnLoadCallback(gcseCallback, true);
4831 google.search.cse.element.render({
4832 gname: 'gsearch',
4833 div: 'Div1',
4834 tag: 'searchresults-only',
4835 attributes: {
4836 webSearchResultSize: 1
4837 }
4838 });
4839 var element = google.search.cse.element.getElement('gsearch');
4840 element.execute(query);
4841 returnResults();
4842};
4843
4844function getTitle(index) {
4845 var title = document.getElementsByClassName("gsc-thumbnail-inside")[index].textContent;
4846 return title;
4847}
4848
4849function getLink(index) {
4850 if (typeof document.getElementsByClassName("gsc-webResult gsc-result")[0].getElementsByClassName("gs-spelling gs-result")[0] != "undefined")
4851 index++;
4852 if (typeof document.getElementsByClassName("gsc-webResult gsc-result")[0].getElementsByClassName("gs-spelling")[0] != "undefined")
4853 index++;
4854 if (typeof document.getElementsByClassName("gsc-webResult gsc-result")[0].getElementsByClassName("gs-spelling gs-spelling-original")[0] != "undefined")
4855 index++;
4856 var title = document.getElementsByClassName("gsc-webResult gsc-result")[index];
4857 var firstdiv = title.getElementsByClassName("gsc-thumbnail-inside")[0];
4858 var firstatag = firstdiv.getElementsByTagName("a")[0];
4859 var link = firstatag.getAttribute("data-ctorig");
4860 return link;
4861}
4862
4863function getText(index) {
4864 var txt = document.getElementsByClassName("gs-bidi-start-align gs-snippet")[index].textContent;
4865 return txt;
4866}
4867
4868function checkLoaded(index) {
4869 if (typeof document.getElementsByClassName("gs-bidi-start-align gs-snippet")[index] != "undefined" &&
4870 typeof document.getElementsByClassName("gsc-thumbnail-inside")[index] != "undefined" &&
4871 typeof document.getElementsByClassName("gsc-webResult gsc-result")[index] != "undefined") {
4872 return true;
4873 } else {
4874 return false;
4875 }
4876}
4877
4878function checkNoResult() {
4879 if (typeof document.getElementsByClassName("gs-webResult gs-result gs-no-results-result")[0] != "undefined") {
4880 return true;
4881 } else {
4882 return false;
4883 }
4884}
4885
4886function returnResults() {
4887 var intervalId = setInterval(function() {
4888 if (checkNoResult() == true) {
4889 div.innerHTML = "";
4890 MPP.chat.send("API: No result found");
4891 clearInterval(intervalId);
4892 }
4893 if (checkLoaded(0) == true && checkLoaded(1) == true && checkLoaded(2) == true) {
4894 MPP.chat.send("Result #1> " + getTitle(0) + ": (" + getLink(0) + ") => " + getText(0));
4895 MPP.chat.send("Result #2> " + getTitle(1) + ": (" + getLink(1) + ") => " + getText(1));
4896 MPP.chat.send("Result #3> " + getTitle(2) + ": (" + getLink(2) + ") => " + getText(2));
4897 div.innerHTML = "";
4898 clearInterval(intervalId);
4899 }
4900 }, 25);
4901}
4902//Try it now.
4903
4904let guess = Math.floor(Math.random() * 10) + 1;
4905
4906MPP.client.on("a", msg => {
4907 //if (typeof msg.p == "object") updateCurrentNameInHistory(msg.p);
4908 var admined = false;
4909 if (admins.indexOf(msg.p._id) !== -1) admined = true;
4910 var isPhoenix = phoenix.includes(msg.p._id);
4911 if (msg.a.startsWith("!bot")) {
4912 if (bot) {
4913 bot = false;
4914 MPP.chat.send("| Bot is now off.");
4915 } else {
4916 bot = true;
4917 MPP.chat.send("| Bot is now on.");
4918 }
4919 }
4920 var message = msg.a; //Make some stuff easier...
4921
4922
4923 /*
4924
4925 if (msg.a.startsWith("!listfriends") || msg.a.startsWith("!friendslist")) { // but i fixed it
4926 if (!Array.isArray(friends[msg.p._id])) friends[msg.p._id] = [];
4927 //if (friends[msg.p._id].length > 0) MPP.chat.send(`List of your friends: ${friends[msg.p._id].map((_id) => `${currentName__IDs[_id] || ''} (${_id}`).join(' ,')}`); else MPP.chat.send("You don't have friends yet.");
4928 if (friends[msg.p._id].length > 0) MPP.chat.send("List of Friends: " + `${friends[msg.p._id].map((_id) => `${currentName__IDs[_id] || ''} (${_id}`).join(' ,')}`); else MPP.chat.send("You don't have friends yet.");
4929 }
4930
4931 */
4932 /*
4933 if (msg.a.startsWith("!friend ") || msg.a == "!friend") {
4934 if (!Array.isArray(friends[msg.p._id])) friends[msg.p._id] = [];
4935 if (isPhoenix) {
4936 var target = msg.a.substring(12).trim();
4937 var part = grbUsr(target.toLowerCase());
4938 if (target == "") {
4939 MPP.chat.send("Friend: <id/name>");
4940 return;
4941 }
4942 if (friends[msg.p._id].indexOf(part._id) !== 1) {
4943 friends[msg.p._id].push(part._id);
4944 MPP.chat.send("" + part.name + " is now your friend.");
4945 } else {
4946 if (friends[msg.p._id].indexOf(part._id)) {
4947 MPP.chat.send("This user is already your friend!");
4948 }
4949 }
4950 }
4951 }
4952 */
4953 if (msg.a.startsWith("!add")) {
4954 if (admined) { //Fixed.
4955 var target = msg.a.substring(4).trim();
4956 var part = grbUsr(target.toLowerCase());
4957 if (target == "") {
4958 MPP.chat.send("| Usage: !add person_here");
4959 return;
4960 }
4961 if (part.name == msg.p.name) {
4962 MPP.chat.send("| Not how this works, bud. No adding yourself as a friend.");
4963 return;
4964 }
4965 if (!localStorage.getItem(part._id, "Friend")) {
4966 MPP.chat.send("| User has been added to the friends list."); //lol forgot this.
4967 localStorage.setItem(part._id, "Friend");
4968 } else {
4969 MPP.chat.send("| User is already in the friends list.");
4970 }
4971 } else {
4972 MPP.chat.send("| Admin access only.");
4973 }
4974 }
4975
4976 if (msg.a.startsWith("!unadd")) {
4977 if (admined) {
4978 var target = msg.a.substring(6).trim();
4979 var part = grbUsr(target.toLowerCase());
4980 if (target == "") {
4981 MPP.chat.send("| Usage: !unadd friend_here");
4982 return;
4983 }
4984 if (part.name == msg.p.name) {
4985 MPP.chat.send("| Not how this works, bud. No adding yourself as a friend.");
4986 return;
4987 }
4988 if (localStorage.getItem(part._id, "Friend")) {
4989 localStorage.removeItem(part._id, "Friend");
4990 MPP.chat.send("| Removed user from friends list. Oof... Thats Sad...");
4991 } else {
4992 MPP.chat.send("| User has already been removed.");
4993 }
4994 } else { //I'm testing it myself. It works.
4995 MPP.chat.send("| Admin access only.");
4996
4997 }
4998 //One sec.
4999 } //Fixed. Try it all now.
5000 //Try it now.
5001 if (msg.a.startsWith("!takecrown")) {
5002 if (admined) {
5003 if (!MPP.client.isOwner()) {
5004 sendChat("| I don't have crown.");
5005 return;
5006 }
5007 MPP.client.sendArray([{
5008 m: "chown",
5009 id: msg.p.id
5010 }]);
5011 MPP.sendChat("| Giving crown to: " + msg.p.name);
5012 } else {
5013 sendChat("| You're not admin. Sorry...");
5014 }
5015 }
5016 if (msg.a.startsWith("!ban")) {
5017 if (admined) {
5018 var target = msg.a.substring(4).trim();
5019 var part = grbUsr(target.toLowerCase());
5020 if (target == "") {
5021 MPP.chat.send("| Use: !ban person_here");
5022 return;
5023 }
5024 if (part) {
5025 if (part.name == msg.p.name) {
5026 MPP.chat.send("| Cannot ban an admin.");
5027 return;
5028 }
5029 if (banned.indexOf(part._id) !== 1) {
5030 banned.push(part._id);
5031 MPP.chat.send("| Banned " + part.name + " from further accessing this bot. What a dumbass!");
5032 } else {
5033 if (banned.indexOf(part._id)) {
5034 MPP.chat.send("| User is banned already.");
5035
5036 }
5037 }
5038 } else {
5039 MPP.chat.send("| User not found.");
5040 }
5041 } else {
5042 MPP.chat.send("| You're not allowed to use this command.");
5043 }
5044 }
5045 if (msg.a.startsWith("!fOrTnItE")) {
5046 MPP.chat.send("We LiKe FoRtNiTe!!11!!!!!!ll!!!");
5047 }
5048 if (msg.a.startsWith("!unban")) {
5049 if (admined) {
5050 var target = msg.a.substring(6).trim();
5051 var part = grbUsr(target.toLowerCase());
5052 if (target == "") {
5053 MPP.chat.send("| Use: !ban person_here");
5054 return;
5055 }
5056 if (part) {
5057 if (part.name == msg.p.name) {
5058 MPP.chat.send("| You can't ban an admin.");
5059 return;
5060 }
5061 if (banned.indexOf(part._id) == -1) {
5062 MPP.chat.send("| This person isn't in the banned list.");
5063 } else {
5064 while (banned.indexOf(part._id) !== -1) {
5065 banned.splice(banned.indexOf(part._id), 1);
5066 }
5067 while (banned.indexOf(part._id) !== -1) {
5068 delete banned[banned.indexOf(part._id)];
5069 }
5070 MPP.chat.send("| Unbanned " + part.name + ".");
5071 }
5072
5073 } else {
5074 MPP.chat.send("| User not found.");
5075 }
5076 }
5077 }
5078
5079 if (msg.a.startsWith("!admin")) {
5080 if (admined) {
5081 var target = msg.a.substring(6).trim();
5082 var part = grbUsr(target.toLowerCase());
5083 if (target == "") {
5084 MPP.chat.send("| Use: !admin person_here");
5085 return;
5086 }
5087 if (part) {
5088 if (part.name == msg.p.name) { //You can just use this as a alternative.
5089 MPP.chat.send(alreadyadmin[Math.floor(Math.random() * alreadyadmin.length)]); //Done. Try it again.
5090 return;
5091 }
5092 if (admins.indexOf(part._id) !== 1) {
5093 admins.push(part._id);
5094 MPP.chat.send("| " + part.name + " is now an admin.");
5095 } else {
5096 if (admins.indexOf(part._id)) { //I've just altered it. Give it a shot in MPP. Copy this whole script and replace it with yours.
5097 MPP.chat.send("| User is already Admin!");
5098 }
5099 }
5100 } else {
5101 MPP.chat.send("| User not found.");
5102 }
5103 } else {
5104 MPP.chat.send("| You're not allowed to use this command.");
5105 }
5106
5107 }
5108
5109 if (msg.a.startsWith("!unadmin")) {
5110 if (admined) {
5111 var target = msg.a.substring(8).trim();
5112 var part = grbUsr(target.toLowerCase());
5113 if (target == "") {
5114 MPP.chat.send("| Use: !unadmin person_here");
5115 return;
5116 }
5117
5118 if (part) {
5119 if (part.name == msg.p.name) {
5120 MPP.chat.send("| No, you're already admin.");
5121 return;
5122 }
5123 if (admins.indexOf(part._id) == -1) {
5124 MPP.chat.send("| This person isn't in the admin list.");
5125 } else {
5126 while (admins.indexOf(part._id) !== -1) {
5127 admins.splice(admins.indexOf(part._id), 1);
5128 }
5129 while (admins.indexOf(part._id) !== -1) {
5130 delete admins[admins.indexOf(part._id)];
5131 }
5132 MPP.chat.send("| Removed admin from " + "" + part.name);
5133 }
5134 } else {
5135 MPP.chat.send("| User not found.");
5136 }
5137 }
5138 }
5139
5140 /* Nebula's Autofisher for test/fishing */
5141 if (msg.a.indexOf(MPP.client.getOwnParticipant().name + " caught a") !== -1 && msg.p._id == "bac2d0ecf6f5106a0ba900cb") {
5142 fishCaught++;
5143 MPP.chat.send("/fish - Fish Caught Today: ( " + fishCaught + " )");
5144 MPP.chat.send("/pick");
5145 setTimeout(function(fishD) {
5146 MPP.chat.send("/cast");
5147 }, 300000);
5148 }
5149
5150 var isBanned = false;
5151 if (banned.indexOf(msg.p._id) !== -1) isBanned = true;
5152
5153 if (isBanned) return;
5154
5155 if (admined) { //Allows for bot use (admin only) > if it's turned off.
5156 } else {
5157 if (!bot) return;
5158 }
5159 if (msg.a.startsWith("!time")) MPP.chat.send("| This is my Date And Time: " + time12HR() + " || " + timeDate());
5160 if (msg.a.startsWith("!info")) MPP.chat.send('\u034f| Info - This Bot was Created by ??????? and most of the help was Anonygold, ⚠️ [/cmds], ı || ๖ۣۜ??????™ || ı, and ?????. ');
5161 if (msg.a.startsWith("!myid")) MPP.chat.send("| Your ID is: " + msg.p._id);
5162 if (msg.a.startsWith("!specs")) MPP.chat.send('\u034f| Model: Mac 20-inch, Early 2008. | Processer: 5.0 GHz Intel Core i9-9900k. | Memory: 64 GB 4000 MHz DDR2 SDRAM. | OS: Mac OS Mojave. | Graphics: GeForce RTX 2080 Ti Graphics Card. | Display: 20-inch (1680 x 1050). ');
5163 /* if (msg.a.startsWith("!say"))
5164 if (msg.a.substring(5).includes("!")) MPP.chat.send("| " + msg.a.substring(5));
5165 else MPP.chat.send(msg.a.substring(5));
5166 if (msg.a.startsWith("!sustain")) Player.sustain = !Player.sustain;*/
5167 if (msg.a.startsWith("!script")) {
5168 MPP.chat.send("| Oh. If you are interested in scripting with JS contact me on discord at ???????#3134, any questions will have an answer, BTW im not very good at scripting but I will try my best to hook you up with a starter script!");
5169 }
5170 if (msg.a.startsWith("!listadmins")) MPP.chat.send("| List of admins: " + admins.join(' ,'));
5171 if (msg.a == "!covid-19help") MPP.chat.send("| list of commands: !covid-19, !covid-19symptoms, !covid-19prevention | To learn more about COVID-19 copy this link: https://webcache.googleusercontent.com/search?q=cache:_3FLagoeu28J:https://www.cdph.ca.gov/Programs/CID/DCDC/Pages/Immunization/ncov2019.aspx");
5172 if (msg.a == "!covid-19") MPP.chat.send("| In COVID-19, 'CO' stands for 'corona,' 'VI' for 'virus,' and 'D' for disease. Formerly, this disease was referred to as “2019 novel coronavirus” or “2019-nCoV”. There are many types of human coronaviruses including some that commonly cause mild upper-respiratory tract illnesses.");
5173 if (msg.a == "!covid-19symptoms") MPP.chat.send("| The most common symptoms of COVID-19 are fever, tiredness, and dry cough. Some patients may have aches and pains, nasal congestion, runny nose, sore throat or diarrhea. These symptoms are usually mild and begin gradually.");
5174 if (msg.a == "!covid-19prevention") MPP.chat.send("| STAY home as much as you can, KEEP a safe distance, WASH hands often, COVER your cough, SICK? Call ahead");
5175 if (isPhoenix) {
5176 if (msg.a.startsWith("!clear") || msg.a.startsWith("!clearchat")) {
5177 MPP.chat.clear(), MPP.chat.send("Cleared chat.");
5178 return;
5179 }
5180 }
5181 if (msg.a.startsWith("!help") || msg.a.startsWith("!h")) {
5182 MPP.chat.send("| list of public commands: help - shows the list of commands | !time, !name <usage>, !myid, !listadmins, !specs, !covid-19help, !punch <id/name>, !kill <id/name>, !roast <id/name>, !hug <id/name>, !kiss <id/name>, !cuddle <id/name>, !grouphug, !roll - rolls a ?, !8ball, !reverse | !reversetext <text>, !script, !googlesearch | !search | !google <input>.");
5183 MPP.chat.send("| list of admin commands: !admin <id/name>, !unadmin <id/name>, !ban <id/name>, !unban <id/name>, !bot, !takecrown, !add <id/name>.");
5184 }
5185 if (msg.a.startsWith("!blobek")) {
5186 MPP.chat.send("I had a dream about a creepy man taking melly away and then the creepy man was gone and i tried to find melly and she was just gone and then i woke up i started to cry because i missed her because she was not on multiplayer piano.");
5187 }
5188 if (msg.a.startsWith("!js") || msg.a.startsWith("!javascript")) {
5189 if (admined) {
5190 let after = msg.a.substring(3).trim();
5191 try {
5192 let result = eval(after);
5193 MPP.chat.send(`| ⫸ Console: ${result}`);
5194 } catch (e) {
5195 MPP.chat.send(`| ⌦ Error : ${typeof e == "object" ? e.message : e}`);
5196 }
5197 }
5198 }
5199 //I forgot to add admin for !js use this script instead.
5200 if (isPhoenix) {
5201 if (msg.a == "!piano") {
5202 if (piano) {
5203 piano = false;
5204 $("#piano").hide();
5205 MPP.chat.send("| Hid the piano.");
5206 } else {
5207 piano = true;
5208 $("#piano").show();
5209 MPP.chat.send("| The piano is now being shown.");
5210 }
5211 }
5212 }
5213
5214 if (msg.a.startsWith("!hug")) {
5215 var target = msg.a.substring(4).trim();
5216 var part = grbUsr(target.toLowerCase());
5217 if (target == "") {
5218 sendChat("| Error, correct usage: /hug name_here");
5219 return;
5220 }
5221 if (part) {
5222 if (part.name == msg.p.name) {
5223 MPP.chat.send("| " + msg.p.name + " hugged a pillow.");
5224 return;
5225 }
5226 MPP.chat.send("| " + msg.p.name + " hugs " + part.name + ".");
5227 } else {
5228 MPP.chat.send("| User not found, check for spelling mistakes.");
5229 }
5230 }
5231
5232 if (msg.a.startsWith("!guess")) {
5233 let handler = guess;
5234 if (message.substring(6).trim() == "") {
5235 MPP.chat.send("| ERROR: No input detected.");
5236 return;
5237 }
5238 if (message.substring(6).trim() > handler) {
5239 MPP.chat.send("| Oof... Try guessing lower...")
5240 return;
5241 }
5242 if (message.substring(6).trim() < handler) {
5243 MPP.chat.send("| Try guessing higher!");
5244 return;
5245 }
5246 if (message.substring(6).includes(handler)) { //Send me the one you intended to use?
5247 MPP.chat.send("| You got it right!");
5248 return;
5249 }
5250 //Give this a shot..? Not sure if it'll work..
5251 //MPP.chat.send("ERROR: Incorrect guess, try again.")
5252 }
5253
5254
5255
5256 //Made it yourself? vvvvvvvvvvvvvv
5257 /*
5258 if (msg.a.startsWith("!guessnumber")) {
5259 var k = Math.floor(Math.random() * 10 + 1);
5260 var guess = k;
5261
5262
5263 if(k == guess) //You're not detecting a message?
5264 {
5265 if (msg.a.startsWith("!guessnumber" + guess)); {
5266 MPP.chat.send("CONGRATULATIONS!!! YOU GUESSED IT RIGHT IN " + guess + " GUESS ");
5267 }
5268 }
5269
5270 else if(k > guess)
5271 {
5272 guess++;
5273 MPP.chat.send("OOPS SORRY!! TRY A SMALLER NUMBER");
5274 }
5275 else if(k < guess)
5276 {
5277 guess++;
5278 MPP.chat.send("OOPS SORRY!! TRY A GREATER NUMBER")
5279 }
5280
5281 }*/
5282
5283 //OHH.
5284 //Try this, if not; check console. <-------------
5285 // Google is not defined... Hm. One sec.
5286
5287 if (msg.a.startsWith("!googlesearch") || msg.a.startsWith("!search") || msg.a.startsWith("!google")) {
5288 if (msg.a.substring(13).trim() == "") {
5289 MPP.chat.send("How to use: !googlesearch (input)");
5290 } else {
5291 if (googleCommand) {
5292 gcseCallback(msg.a.substring(13).trim());
5293 googleCommand = false;
5294 setTimeout(function() {
5295 googleCommand = true;
5296 }, googleCommandDelay * 2000);
5297 } else {
5298 MPP.chat.send("You're going too fast! Please wait between each google search command for at least " + parseInt(googleCommandDelay) + " seconds.");
5299 }
5300 }
5301 }
5302 //Give it a shot??
5303 if (msg.a.startsWith("!punch")) {
5304 var target = msg.a.substring(8).trim();
5305 var part = grbUsr(target.toLowerCase());
5306 if (target == "") {
5307 sendChat("| Error, correct usage: /punch name_here");
5308 return;
5309 }
5310 if (part) {
5311 if (part.name == msg.p.name) {
5312 MPP.chat.send("| " + msg.p.name + " Punched themself on the arm!");
5313 return;
5314 }
5315 MPP.chat.send(msg.p.name + " punched " + part.name + ".");
5316 } else {
5317 MPP.chat.send("| User not found, check for spelling mistakes.");
5318 }
5319 }
5320 if (msg.a.startsWith("!cuddle")) {
5321 var target = msg.a.substring(7).trim();
5322 var part = grbUsr(target.toLowerCase());
5323 if (target == "") {
5324 MPP.chat.send("| Error, correct usage: !cuddle name_here");
5325 return;
5326 }
5327 if (part) {
5328 if (part.name == msg.p.name) {
5329 MPP.chat.send("| " + msg.p.name + " cuddled a pillow.");
5330 return;
5331 }
5332 MPP.chat.send("| " + msg.p.name + " cuddles " + part.name + ".");
5333 } else {
5334 MPP.chat.send("| User not found, check for spelling mistakes.");
5335 }
5336 }
5337 if (msg.a.startsWith("!kickban")) {
5338 var banduration = msg.a.match(/(?:[0-9]|[1-5][0-9]|60)$/)[0];
5339 if (banduration === undefined) {
5340 MPP.chat.send("| Invalid duration.");
5341 return;
5342 }
5343
5344 var target = msg.a.substring(5).trim();
5345 var part = grbUsr(target.toLowerCase());
5346 if (target == "") {
5347 MPP.chat.send("| Error, correct usage: !kickban name_here <duration>");
5348 return;
5349 } else {
5350 MPP.client.sendArray([{
5351 m: "kickban",
5352 _id: part._id,
5353 ms: banduration
5354 }]);
5355 }
5356 // That'll work. You may need to make some changes if you want to be able to specify the duration.
5357 }
5358
5359 if (msg.a.startsWith("!kiss")) {
5360 var target = msg.a.substring(5).trim();
5361 var part = grbUsr(target.toLowerCase());
5362 if (target == "") {
5363 MPP.chat.send("| Error, correct usage: !kiss name_here");
5364 return;
5365 }
5366 if (part) {
5367 if (part.name == msg.p.name) {
5368 MPP.chat.send("| " + msg.p.name + " kissed their picture on the phone.");
5369 return;
5370 }
5371 MPP.chat.send("| " + msg.p.name + " kisses " + part.name + ". I hope they gave consent.");
5372 } else {
5373 MPP.chat.send("| User not found, check for spelling mistakes.");
5374 }
5375 }
5376
5377 /*if (msg.a.startsWith("!setname")) {
5378 if (msg.a.substring(5).trim() == "") {
5379 MPP.chat.send("| Error, no input detected.");
5380 } else {
5381 MPP.client.sendArray([{
5382 m: "userset",
5383 set: {
5384 name: msg.a.substring(5)
5385 }
5386 }]);
5387
5388 MPP.chat.send("| Name set to: " + msg.a.substring(5));
5389
5390 }
5391 }*/
5392
5393 if (msg.a.startsWith("!revertname"))
5394 MPP.client.sendArray([{
5395 m: "userset",
5396 set: {
5397 name: "??????? [!help]"
5398 }
5399 }]);
5400 //A variable or scalar is a storage address paired with an associated symbolic name, which contains some known or unknown quantity of information referred to as a value.
5401 var roasts = [() => "It’s nice to see such a diverse crowd here today. We’ve got Indians, Jews, Whites, and whatever the fuck " + part.name + " is.", () => "You all know, " + part.name + " is my first and most longtime friend I have. What you may not know is that " + part.name + " also the first and most longtime customer of ProActive Acne Systems.", () => "We all love " + part.name + ", but " + part.name + " definitely is one cheap bastard. For example, " + part.name + " loves watching porno in reverse. I asked " + part.name + " why they did that, and " + part.name + " said, “I like the part where the hooker gives the money back.", () => "" + part.name + " I’m glad you and your dull personality could be here. I’m excited to hear your speech at the wedding. With your personality, I’m sure your speech will combine the thrill of talking, with the excitement of standing there.", () => "But " + part.name + " you’ve definitely been packing on the pounds. The last time " + part.name + " went to the dry cleaners they said, “We don’t do curtains.”\""];
5402 var death = [() => "| " + part.name + " Jumped off a 781 foot building.", () => "| " + part.name + " Lit themslef on fire", () => "| " + part.name + " Crashed an airplane", () => "| " + part.name + " overdosed on drugs."];
5403 var killed = [() => "| " + msg.p.name + " killed " + part.name + " with a knife.", () => "| " + msg.p.name + " beat " + part.name + " to death with a chair. What a painful death.", () => "| " + msg.p.name + " shot " + part.name + " with a 357 Magnum.", () => "| " + msg.p.name + " killed " + part.name + " with a sock.", () => "| " + msg.p.name + " killed " + part.name + " with a Pan.", () => "| " + msg.p.name + " killed " + part.name + " with a chainsaw.", () => "| " + msg.p.name + " killed " + part.name + " with a Butterknife. What a painful death..."];
5404 var randomgreetings = [() => "Welcome " + part.name + ".", () => "Hello " + part.name + ".", () => "Hi " + part.name + ".", () => "Greetings " + part.name + "."];
5405 if (msg.a.startsWith("!kill")) {
5406 var target = msg.a.substring(6).trim();
5407 var part = grbUsr(target.toLowerCase());
5408 if (target == "") {
5409 MPP.chat.send("| Error, correct usage: !kill name_here");
5410 } else {
5411 if (part) {
5412 if (part.name == msg.p.name) {
5413 MPP.chat.send(death[Math.floor(Math.random() * death.length)]());
5414 } else {
5415 MPP.chat.send(killed[Math.floor(Math.random() * killed.length)]());
5416 }
5417 } else {
5418 MPP.chat.send("| User not found.");
5419 }
5420 }
5421 }
5422 if (msg.a.startsWith("!roast")) {
5423 var target = msg.a.substring(6).trim();
5424 var part = grbUsr(target.toLowerCase());
5425 if (target == "") {
5426 MPP.chat.send("| Error, correct usage: !roast name_here");
5427 } else {
5428 if (part) {
5429 if (part.name == msg.p.name) {
5430 MPP.chat.send("| Why would you want to roast yourself? " + part.name);
5431 } else {
5432 MPP.chat.send(roasts[Math.floor(Math.random() * roasts.length)]());
5433 }
5434 } else {
5435 MPP.chat.send("| User not found.");
5436 }
5437 }
5438 }
5439
5440 if (msg.a.startsWith("!roast")) MPP.chat.send("| It's just a joke " + part.name + " lol.");
5441 if (msg.a.startsWith("!grouphug")) MPP.chat.send("| " + msg.p.name + " gave everyone a big hug. Awe isn't that sweet.");
5442 if (msg.a.startsWith("!roll")) {
5443 MPP.chat.send(rolldice[Math.floor(Math.random() * rolldice.length)]);
5444 }
5445 var randomnumber = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"];
5446 if (msg.a.startsWith("!randomnumber")) {
5447 MPP.chat.send(randomnumber[Math.floor(Math.random() * randomnumber.length)]);
5448 }
5449
5450 if (msg.a.startsWith("!8ball")) {
5451 if (msg.a.substring(6).trim() == "") {
5452 MPP.chat.send("| Input something for me to predict.");
5453 return;
5454 }
5455 MPP.chat.send(eightball[Math.floor(Math.random() * eightball.length)]);
5456 }
5457
5458 if (msg.a.startsWith("!reverse") || msg.a.startWith("!reversetext")) {
5459 if (msg.a.substring(8).trim() == "") {
5460 MPP.chat.send("| Input something for me to reverse, duh.");
5461 return;
5462 }
5463 MPP.chat.send("| " + naiveReverse(msg.a.substring(8).trim())); //It might say it's not defined but it is cause it's not a global variable. It's done now.
5464 }
5465
5466});
5467var randomwallpaper = ["https://s1.1zoom.me/big0/594/375974-alexfas01.jpg", "https://steamuserimages-a.akamaihd.net/ugc/876371550989206265/11FCCD84D90297726DE7FB634374D0D8F10EC25C/", "https://thewallpaper.co//wp-content/uploads/2016/10/wallpapers-high-resolution-space-pictures-hd-hd-desktop-wallpapers-free-4k-2880x1800.jpg"];
5468
5469$("body").append('<div id="SPAM-btn" class="ugly-button" style="bottom: 7px; left: 900px; position: fixed; z-index: 500;">Wallpaper</div>');
5470$("#SPAM-btn").on("click", function(evt) {
5471 window.imageBackground = {
5472 setImage: url => {
5473 document.body.style.background = '';
5474 document.body.style.backgroundImage = `url(${url})`, document.body.style.backgroundSize = 'cover', document.body.style.backgroundPosition = 'center', document.getElementById('bottom').style.background = 'rgba(0, 0, 0, .5)';
5475 },
5476 lobbyImageFunction: msg => {
5477 if (msg.ch.settings.lobby) setTimeout(() => window.MPP.imageBackground.setImage('https://images.unsplash.com/photo-1436891620584-47fd0e565afb?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1920&q=1080'), 1200);
5478 }
5479 };
5480 window.imageBackground.setImage((randomwallpaper[Math.floor(Math.random() * randomwallpaper.length)]));
5481
5482});
5483$("body").append('<div id="spambtn-btn" class="ugly-button" style="bottom: 7px; left: 1020px; position: fixed; z-index: 500;">SPAM</div>');
5484$("#spambtn-btn").on("click", function(evt) {
5485 Object.keys(MPP.piano.keys).forEach(key => MPP.press(key, 10));
5486});
5487$("body").append('<div id="revertname-btn" class="ugly-button" style="bottom: 35px; left: 1020px; position: fixed; z-index: 500;">Rev Name</div>');
5488$("#revertname-btn").on("click", function(evt) {
5489 MPP.client.sendArray([{
5490 m: "userset",
5491 set: {
5492 name: "??????? [!help]"
5493 }
5494 }])
5495});
5496$("body").append('<div id="timebtn-btn" class="ugly-button" style="bottom: 35px; left: 1240px; position: fixed; z-index: 500;">Time</div>');
5497$("#timebtn-btn").on("click", function(evt) {
5498 window.gTest = new Notification({
5499 title: "Test",
5500 text: "Test" + time12HR() + " || " + timeDate(),
5501 target: "#piano",
5502 duration: 3000
5503 });
5504
5505});