Advertisement
Guest User

scriptV3.1

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