Advertisement
Guest User

ds

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