Advertisement
Guest User

scriptV3

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