Advertisement
Guest User

sd

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