Advertisement
Guest User

Script.js V4

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