diff --git a/script.js b/script.js index 5215322..52e8a8f 100755 --- a/script.js +++ b/script.js @@ -1,599 +1,495 @@ - // 钢琴 -$(function() { - - var test_mode = (window.location.hash && window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i)); - - var gSeeOwnCursor = (window.location.hash && window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i)); - - var gMidiVolumeTest = (window.location.hash && window.location.hash.match(/^(?:#.+)*#midivolumetest(?:#.+)*$/i)); - - var gMidiOutTest; - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function(elt /*, from*/) { - var len = this.length >>> 0; - var from = Number(arguments[1]) || 0; - from = (from < 0) ? Math.ceil(from) : Math.floor(from); - if (from < 0) from += len; - for (; from < len; from++) { - if (from in this && this[from] === elt) return from; - } - return -1; - }; - } - - window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame - || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame - || function (cb) { setTimeout(cb, 1000 / 30); }; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var DEFAULT_VELOCITY = 0.5; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var TIMING_TARGET = 1000; - - - - - - - - - - - - - - - - - - - -// Utility - -//////////////////////////////////////////////////////////////// - - - -var Rect = function(x, y, w, h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; - this.x2 = x + w; - this.y2 = y + h; -}; -Rect.prototype.contains = function(x, y) { - return (x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2); -}; - - - - - - - - - - - - - - - - -// performing translation - -//////////////////////////////////////////////////////////////// - - var Translation = (function() { - var strings = { - "people are playing": { - "pt": "pessoas estão jogando", - "es": "personas están jugando", - "ru": "человек играет", - "fr": "personnes jouent", - "ja": "人が遊んでいる", - "de": "Leute spielen", - "zh": "人在玩", - "nl": "mensen spelen", - "pl": "osób grają", - "hu": "ember játszik" - }, - "New Room...": { - "pt": "Nova Sala ...", - "es": "Nueva sala de...", - "ru": "Новый номер...", - "ja": "新しい部屋", - "zh": "新房间", - "nl": "nieuwe Kamer", - "hu": "új szoba" - }, - "room name": { - "pt": "nome da sala", - "es": "sala de nombre", - "ru": "название комнаты", - "fr": "nom de la chambre", - "ja": "ルーム名", - "de": "Raumnamen", - "zh": "房间名称", - "nl": "kamernaam", - "pl": "nazwa pokój", - "hu": "szoba neve" - }, - "Visible (open to everyone)": { - "pt": "Visível (aberto a todos)", - "es": "Visible (abierto a todo el mundo)", - "ru": "Visible (открытый для всех)", - "fr": "Visible (ouvert à tous)", - "ja": "目に見える(誰にでも開いている)", - "de": "Sichtbar (offen für alle)", - "zh": "可见(向所有人开放)", - "nl": "Zichtbaar (open voor iedereen)", - "pl": "Widoczne (otwarte dla wszystkich)", - "hu": "Látható (nyitott mindenki számára)" - }, - "Enable Chat": { - "pt": "Ativar bate-papo", - "es": "Habilitar chat", - "ru": "Включить чат", - "fr": "Activer discuter", - "ja": "チャットを有効にする", - "de": "aktivieren Sie chatten", - "zh": "启用聊天", - "nl": "Chat inschakelen", - "pl": "Włącz czat", - "hu": "a csevegést" - }, - "Play Alone": { - "pt": "Jogar Sozinho", - "es": "Jugar Solo", - "ru": "Играть в одиночку", - "fr": "Jouez Seul", - "ja": "一人でプレイ", - "de": "Alleine Spielen", - "zh": "独自玩耍", - "nl": "Speel Alleen", - "pl": "Zagraj sam", - "hu": "Játssz egyedül" - } - // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr - // todo: Connecting, Offline mode, input placeholder, Notifications - }; - - var setLanguage = function(lang) { - language = lang - }; - - var getLanguage = function() { - if(window.navigator && navigator.language && navigator.language.length >= 2) { - return navigator.language.substr(0, 2).toLowerCase(); - } else { - return "en"; - } - }; - - var get = function(text, lang) { - if(typeof lang === "undefined") lang = language; - var row = strings[text]; - if(row == undefined) return text; - var string = row[lang]; - if(string == undefined) return text; - return string; - }; - - var perform = function(lang) { - if(typeof lang === "undefined") lang = language; - $(".translate").each(function(i, ele) { - var th = $(this); - if(ele.tagName && ele.tagName.toLowerCase() == "input") { - if(typeof ele.placeholder != "undefined") { - th.attr("placeholder", get(th.attr("placeholder"), lang)) - } - } else { - th.text(get(th.text(), lang)); - } - }); - }; - - var language = getLanguage(); - - return { - setLanguage: setLanguage, - getLanguage: getLanguage, - get: get, - perform: perform - }; - })(); - - Translation.perform(); - - - - - - - - - - - - - - - -// AudioEngine classes - -//////////////////////////////////////////////////////////////// - - var AudioEngine = function() { - }; - - AudioEngine.prototype.init = function(cb) { - this.volume = 0.6; - this.sounds = {}; - this.paused = true; - return this; - }; - - AudioEngine.prototype.load = function(id, url, cb) { - }; - - AudioEngine.prototype.play = function() { - }; - - AudioEngine.prototype.stop = function() { - }; - - AudioEngine.prototype.setVolume = function(vol) { - this.volume = vol; - }; - - AudioEngine.prototype.resume = function() { - this.paused = false; - }; - - - AudioEngineWeb = function() { - this.threshold = 1000; - this.worker = new Worker("/workerTimer.js"); - var self = this; - this.worker.onmessage = function(event) - { - if(event.data.args) - if(event.data.args.action==0) - { - self.actualPlay(event.data.args.id, event.data.args.vol, event.data.args.time, event.data.args.part_id); - } - else - { - self.actualStop(event.data.args.id, event.data.args.time, event.data.args.part_id); - } - } - }; - - AudioEngineWeb.prototype = new AudioEngine(); - - AudioEngineWeb.prototype.init = function(cb) { - AudioEngine.prototype.init.call(this); - - this.context = new AudioContext({latencyHint: 'interactive'}); - - this.masterGain = this.context.createGain(); - this.masterGain.connect(this.context.destination); - this.masterGain.gain.value = this.volume; - - this.limiterNode = this.context.createDynamicsCompressor(); - this.limiterNode.threshold.value = -10; - this.limiterNode.knee.value = 0; - this.limiterNode.ratio.value = 20; - this.limiterNode.attack.value = 0; - this.limiterNode.release.value = 0.1; - this.limiterNode.connect(this.masterGain); - - // for synth mix - this.pianoGain = this.context.createGain(); - this.pianoGain.gain.value = 0.5; - this.pianoGain.connect(this.limiterNode); - this.synthGain = this.context.createGain(); - this.synthGain.gain.value = 0.5; - this.synthGain.connect(this.limiterNode); - - this.playings = {}; - - if(cb) setTimeout(cb, 0); - return this; - }; - - AudioEngineWeb.prototype.load = function(id, url, cb) { - var audio = this; - var req = new XMLHttpRequest(); - req.open("GET", url); - req.responseType = "arraybuffer"; - req.addEventListener("readystatechange", function(evt) { - if(req.readyState !== 4) return; - try { - audio.context.decodeAudioData(req.response, function(buffer) { - audio.sounds[id] = buffer; - if(cb) cb(); - }); - } catch(e) { - /*throw new Error(e.message +$(function () { + var test_mode = + window.location.hash && + window.location.hash.match(/^(?:#.+)*#test(?:#.+)*$/i); + + var gSeeOwnCursor = + window.location.hash && + window.location.hash.match(/^(?:#.+)*#seeowncursor(?:#.+)*$/i); + + var gMidiVolumeTest = + window.location.hash && + window.location.hash.match(/^(?:#.+)*#midivolumetest(?:#.+)*$/i); + + var gMidiOutTest; + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (elt /*, from*/) { + var len = this.length >>> 0; + var from = Number(arguments[1]) || 0; + from = from < 0 ? Math.ceil(from) : Math.floor(from); + if (from < 0) from += len; + for (; from < len; from++) { + if (from in this && this[from] === elt) return from; + } + return -1; + }; + } + + window.requestAnimationFrame = + window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame || + function (cb) { + setTimeout(cb, 1000 / 30); + }; + + var DEFAULT_VELOCITY = 0.5; + + var TIMING_TARGET = 1000; + + // Utility + + //////////////////////////////////////////////////////////////// + + var Rect = function (x, y, w, h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.x2 = x + w; + this.y2 = y + h; + }; + Rect.prototype.contains = function (x, y) { + return x >= this.x && x <= this.x2 && y >= this.y && y <= this.y2; + }; + + // performing translation + + //////////////////////////////////////////////////////////////// + + var Translation = (function () { + var strings = { + "people are playing": { + pt: "pessoas estão jogando", + es: "personas están jugando", + ru: "человек играет", + fr: "personnes jouent", + ja: "人が遊んでいる", + de: "Leute spielen", + zh: "人在玩", + nl: "mensen spelen", + pl: "osób grają", + hu: "ember játszik" + }, + "New Room...": { + pt: "Nova Sala ...", + es: "Nueva sala de...", + ru: "Новый номер...", + ja: "新しい部屋", + zh: "新房间", + nl: "nieuwe Kamer", + hu: "új szoba" + }, + "room name": { + pt: "nome da sala", + es: "sala de nombre", + ru: "название комнаты", + fr: "nom de la chambre", + ja: "ルーム名", + de: "Raumnamen", + zh: "房间名称", + nl: "kamernaam", + pl: "nazwa pokój", + hu: "szoba neve" + }, + "Visible (open to everyone)": { + pt: "Visível (aberto a todos)", + es: "Visible (abierto a todo el mundo)", + ru: "Visible (открытый для всех)", + fr: "Visible (ouvert à tous)", + ja: "目に見える(誰にでも開いている)", + de: "Sichtbar (offen für alle)", + zh: "可见(向所有人开放)", + nl: "Zichtbaar (open voor iedereen)", + pl: "Widoczne (otwarte dla wszystkich)", + hu: "Látható (nyitott mindenki számára)" + }, + "Enable Chat": { + pt: "Ativar bate-papo", + es: "Habilitar chat", + ru: "Включить чат", + fr: "Activer discuter", + ja: "チャットを有効にする", + de: "aktivieren Sie chatten", + zh: "启用聊天", + nl: "Chat inschakelen", + pl: "Włącz czat", + hu: "a csevegést" + }, + "Play Alone": { + pt: "Jogar Sozinho", + es: "Jugar Solo", + ru: "Играть в одиночку", + fr: "Jouez Seul", + ja: "一人でプレイ", + de: "Alleine Spielen", + zh: "独自玩耍", + nl: "Speel Alleen", + pl: "Zagraj sam", + hu: "Játssz egyedül" + } + // todo: it, tr, th, sv, ar, fi, nb, da, sv, he, cs, ko, ro, vi, id, nb, el, sk, bg, lt, sl, hr + // todo: Connecting, Offline mode, input placeholder, Notifications + }; + + var setLanguage = function (lang) { + language = lang; + }; + + var getLanguage = function () { + if ( + window.navigator && + navigator.language && + navigator.language.length >= 2 + ) { + return navigator.language.substr(0, 2).toLowerCase(); + } else { + return "en"; + } + }; + + var get = function (text, lang) { + if (typeof lang === "undefined") lang = language; + var row = strings[text]; + if (row == undefined) return text; + var string = row[lang]; + if (string == undefined) return text; + return string; + }; + + var perform = function (lang) { + if (typeof lang === "undefined") lang = language; + $(".translate").each(function (i, ele) { + var th = $(this); + if (ele.tagName && ele.tagName.toLowerCase() == "input") { + if (typeof ele.placeholder != "undefined") { + th.attr( + "placeholder", + get(th.attr("placeholder"), lang) + ); + } + } else { + th.text(get(th.text(), lang)); + } + }); + }; + + var language = getLanguage(); + + return { + setLanguage: setLanguage, + getLanguage: getLanguage, + get: get, + perform: perform + }; + })(); + + Translation.perform(); + + // AudioEngine classes + + //////////////////////////////////////////////////////////////// + + var AudioEngine = function () {}; + + AudioEngine.prototype.init = function (cb) { + this.volume = 0.6; + this.sounds = {}; + this.paused = true; + return this; + }; + + AudioEngine.prototype.load = function (id, url, cb) {}; + + AudioEngine.prototype.play = function () {}; + + AudioEngine.prototype.stop = function () {}; + + AudioEngine.prototype.setVolume = function (vol) { + this.volume = vol; + }; + + AudioEngine.prototype.resume = function () { + this.paused = false; + }; + + AudioEngineWeb = function () { + this.threshold = 1000; + this.worker = new Worker("/workerTimer.js"); + var self = this; + this.worker.onmessage = function (event) { + if (event.data.args) + if (event.data.args.action == 0) { + self.actualPlay( + event.data.args.id, + event.data.args.vol, + event.data.args.time, + event.data.args.part_id + ); + } else { + self.actualStop( + event.data.args.id, + event.data.args.time, + event.data.args.part_id + ); + } + }; + }; + + AudioEngineWeb.prototype = new AudioEngine(); + + AudioEngineWeb.prototype.init = function (cb) { + AudioEngine.prototype.init.call(this); + + this.context = new AudioContext({ latencyHint: "interactive" }); + + this.masterGain = this.context.createGain(); + this.masterGain.connect(this.context.destination); + this.masterGain.gain.value = this.volume; + + this.limiterNode = this.context.createDynamicsCompressor(); + this.limiterNode.threshold.value = -10; + this.limiterNode.knee.value = 0; + this.limiterNode.ratio.value = 20; + this.limiterNode.attack.value = 0; + this.limiterNode.release.value = 0.1; + this.limiterNode.connect(this.masterGain); + + // for synth mix + this.pianoGain = this.context.createGain(); + this.pianoGain.gain.value = 0.5; + this.pianoGain.connect(this.limiterNode); + this.synthGain = this.context.createGain(); + this.synthGain.gain.value = 0.5; + this.synthGain.connect(this.limiterNode); + + this.playings = {}; + + if (cb) setTimeout(cb, 0); + return this; + }; + + AudioEngineWeb.prototype.load = function (id, url, cb) { + var audio = this; + var req = new XMLHttpRequest(); + req.open("GET", url); + req.responseType = "arraybuffer"; + req.addEventListener("readystatechange", function (evt) { + if (req.readyState !== 4) return; + try { + audio.context.decodeAudioData(req.response, function (buffer) { + audio.sounds[id] = buffer; + if (cb) cb(); + }); + } catch (e) { + /*throw new Error(e.message + " / id: " + id + " / url: " + url + " / status: " + req.status + " / ArrayBuffer: " + (req.response instanceof ArrayBuffer) + " / byteLength: " + (req.response && req.response.byteLength ? req.response.byteLength : "undefined"));*/ - new Notification({id: "audio-download-error", title: "Problem", text: "For some reason, an audio download failed with a status of " + req.status + ". ", - target: "#piano", duration: 10000}); - } - }); - req.send(); - }; + new Notification({ + id: "audio-download-error", + title: "Problem", + text: + "For some reason, an audio download failed with a status of " + + req.status + + ". ", + target: "#piano", + duration: 10000 + }); + } + }); + req.send(); + }; - AudioEngineWeb.prototype.actualPlay = function(id, vol, time, part_id) { //the old play(), but with time insted of delay_ms. - if(this.paused) return; - if(!this.sounds.hasOwnProperty(id)) return; - var source = this.context.createBufferSource(); - source.buffer = this.sounds[id]; - var gain = this.context.createGain(); - gain.gain.value = vol; - source.connect(gain); - gain.connect(this.pianoGain); - source.start(time); - // Patch from ste-art remedies stuttering under heavy load - if(this.playings[id]) { - var playing = this.playings[id]; - playing.gain.gain.setValueAtTime(playing.gain.gain.value, time); - playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2); - playing.source.stop(time + 0.21); - if(enableSynth && playing.voice) { - playing.voice.stop(time); - } - } - this.playings[id] = {"source": source, "gain": gain, "part_id": part_id}; + AudioEngineWeb.prototype.actualPlay = function (id, vol, time, part_id) { + //the old play(), but with time insted of delay_ms. + if (this.paused) return; + if (!this.sounds.hasOwnProperty(id)) return; + var source = this.context.createBufferSource(); + source.buffer = this.sounds[id]; + var gain = this.context.createGain(); + gain.gain.value = vol; + source.connect(gain); + gain.connect(this.pianoGain); + source.start(time); + // Patch from ste-art remedies stuttering under heavy load + if (this.playings[id]) { + var playing = this.playings[id]; + playing.gain.gain.setValueAtTime(playing.gain.gain.value, time); + playing.gain.gain.linearRampToValueAtTime(0.0, time + 0.2); + playing.source.stop(time + 0.21); + if (enableSynth && playing.voice) { + playing.voice.stop(time); + } + } + this.playings[id] = { source: source, gain: gain, part_id: part_id }; - if(enableSynth) { - this.playings[id].voice = new synthVoice(id, time); - } - } - - AudioEngineWeb.prototype.play = function(id, vol, delay_ms, part_id) - { - if(!this.sounds.hasOwnProperty(id)) return; - var time = this.context.currentTime + (delay_ms / 1000); //calculate time on note receive. - var delay = delay_ms - this.threshold; - if(delay<=0) this.actualPlay(id, vol, time, part_id); - else { - 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. - } - } - - AudioEngineWeb.prototype.actualStop = function(id, time, part_id) { - if(this.playings.hasOwnProperty(id) && this.playings[id] && this.playings[id].part_id === part_id) { - var gain = this.playings[id].gain.gain; - gain.setValueAtTime(gain.value, time); - gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16); - gain.linearRampToValueAtTime(0.0, time + 0.4); - this.playings[id].source.stop(time + 0.41); - + if (enableSynth) { + this.playings[id].voice = new synthVoice(id, time); + } + }; - if(this.playings[id].voice) { - this.playings[id].voice.stop(time); - } + AudioEngineWeb.prototype.play = function (id, vol, delay_ms, part_id) { + if (!this.sounds.hasOwnProperty(id)) return; + var time = this.context.currentTime + delay_ms / 1000; //calculate time on note receive. + var delay = delay_ms - this.threshold; + if (delay <= 0) this.actualPlay(id, vol, time, part_id); + else { + 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. + } + }; - this.playings[id] = null; - } - }; + AudioEngineWeb.prototype.actualStop = function (id, time, part_id) { + if ( + this.playings.hasOwnProperty(id) && + this.playings[id] && + this.playings[id].part_id === part_id + ) { + var gain = this.playings[id].gain.gain; + gain.setValueAtTime(gain.value, time); + gain.linearRampToValueAtTime(gain.value * 0.1, time + 0.16); + gain.linearRampToValueAtTime(0.0, time + 0.4); + this.playings[id].source.stop(time + 0.41); - AudioEngineWeb.prototype.stop = function(id, delay_ms, part_id) { - var time = this.context.currentTime + (delay_ms / 1000); - var delay = delay_ms - this.threshold; - if(delay<=0) this.actualStop(id, time, part_id); - else { - this.worker.postMessage({delay:delay,args:{action:1/*stop*/, id:id, time:time, part_id:part_id}}); - } - }; + if (this.playings[id].voice) { + this.playings[id].voice.stop(time); + } - AudioEngineWeb.prototype.setVolume = function(vol) { - AudioEngine.prototype.setVolume.call(this, vol); - this.masterGain.gain.value = this.volume; - }; - - AudioEngineWeb.prototype.resume = function() { - this.paused = false; - this.context.resume(); - }; + this.playings[id] = null; + } + }; + AudioEngineWeb.prototype.stop = function (id, delay_ms, part_id) { + var time = this.context.currentTime + delay_ms / 1000; + var delay = delay_ms - this.threshold; + if (delay <= 0) this.actualStop(id, time, part_id); + else { + this.worker.postMessage({ + delay: delay, + args: { + action: 1 /*stop*/, + id: id, + time: time, + part_id: part_id + } + }); + } + }; + AudioEngineWeb.prototype.setVolume = function (vol) { + AudioEngine.prototype.setVolume.call(this, vol); + this.masterGain.gain.value = this.volume; + }; + AudioEngineWeb.prototype.resume = function () { + this.paused = false; + this.context.resume(); + }; + // Renderer classes + //////////////////////////////////////////////////////////////// + var Renderer = function () {}; + Renderer.prototype.init = function (piano) { + this.piano = piano; + this.resize(); + return this; + }; + Renderer.prototype.resize = function (width, height) { + if (typeof width == "undefined") + width = $(this.piano.rootElement).width(); + if (typeof height == "undefined") height = Math.floor(width * 0.2); + $(this.piano.rootElement).css({ + height: height + "px", + marginTop: Math.floor($(window).height() / 2 - height / 2) + "px" + }); + this.width = width * window.devicePixelRatio; + this.height = height * window.devicePixelRatio; + }; + Renderer.prototype.visualize = function (key, color) {}; + var CanvasRenderer = function () { + Renderer.call(this); + }; + CanvasRenderer.prototype = new Renderer(); + CanvasRenderer.prototype.init = function (piano) { + this.canvas = document.createElement("canvas"); + this.ctx = this.canvas.getContext("2d"); + piano.rootElement.appendChild(this.canvas); + Renderer.prototype.init.call(this, piano); // calls resize() + // create render loop + var self = this; + var render = function () { + self.redraw(); + requestAnimationFrame(render); + }; + requestAnimationFrame(render); + // add event listeners + var mouse_down = false; + var last_key = null; + $(piano.rootElement).mousedown(function (event) { + mouse_down = true; + //event.stopPropagation(); + event.preventDefault(); - - - - - - - - - - -// Renderer classes - -//////////////////////////////////////////////////////////////// - - var Renderer = function() { - }; - - Renderer.prototype.init = function(piano) { - this.piano = piano; - this.resize(); - return this; - }; - - Renderer.prototype.resize = function(width, height) { - if(typeof width == "undefined") width = $(this.piano.rootElement).width(); - if(typeof height == "undefined") height = Math.floor(width * 0.2); - $(this.piano.rootElement).css({"height": height + "px", marginTop: Math.floor($(window).height() / 2 - height / 2) + "px"}); - this.width = width * window.devicePixelRatio; - this.height = height * window.devicePixelRatio; - }; - - Renderer.prototype.visualize = function(key, color) { - }; - - - - - var CanvasRenderer = function() { - Renderer.call(this); - }; - - CanvasRenderer.prototype = new Renderer(); - - CanvasRenderer.prototype.init = function(piano) { - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - piano.rootElement.appendChild(this.canvas); - - Renderer.prototype.init.call(this, piano); // calls resize() - - // create render loop - var self = this; - var render = function() { - self.redraw(); - requestAnimationFrame(render); - }; - requestAnimationFrame(render); - - // add event listeners - var mouse_down = false; - var last_key = null; - $(piano.rootElement).mousedown(function(event) { - mouse_down = true; - //event.stopPropagation(); - event.preventDefault(); - - var pos = CanvasRenderer.translateMouseEvent(event); - var hit = self.getHit(pos.x, pos.y); - if(hit) { - press(hit.key.note, hit.v); - last_key = hit.key; - } - }); - piano.rootElement.addEventListener("touchstart", function(event) { - mouse_down = true; - //event.stopPropagation(); - event.preventDefault(); - for(var i in event.changedTouches) { - var pos = CanvasRenderer.translateMouseEvent(event.changedTouches[i]); - var hit = self.getHit(pos.x, pos.y); - if(hit) { - press(hit.key.note, hit.v); - last_key = hit.key; - } - } - }, false); - $(window).mouseup(function(event) { - if(last_key) { - release(last_key.note); - } - mouse_down = false; - last_key = null; - }); - /*$(piano.rootElement).mousemove(function(event) { + var pos = CanvasRenderer.translateMouseEvent(event); + var hit = self.getHit(pos.x, pos.y); + if (hit) { + press(hit.key.note, hit.v); + last_key = hit.key; + } + }); + piano.rootElement.addEventListener( + "touchstart", + function (event) { + mouse_down = true; + //event.stopPropagation(); + event.preventDefault(); + for (var i in event.changedTouches) { + var pos = CanvasRenderer.translateMouseEvent( + event.changedTouches[i] + ); + var hit = self.getHit(pos.x, pos.y); + if (hit) { + press(hit.key.note, hit.v); + last_key = hit.key; + } + } + }, + false + ); + $(window).mouseup(function (event) { + if (last_key) { + release(last_key.note); + } + mouse_down = false; + last_key = null; + }); + /*$(piano.rootElement).mousemove(function(event) { if(!mouse_down) return; var pos = CanvasRenderer.translateMouseEvent(event); var hit = self.getHit(pos.x, pos.y); @@ -603,885 +499,1007 @@ Rect.prototype.contains = function(x, y) { } });*/ - return this; - }; - - CanvasRenderer.prototype.resize = function(width, height) { - Renderer.prototype.resize.call(this, width, height); - if(this.width < 52 * 2) this.width = 52 * 2; - if(this.height < this.width * 0.2) this.height = Math.floor(this.width * 0.2); - this.canvas.width = this.width; - this.canvas.height = this.height; - this.canvas.style.width = this.width / window.devicePixelRatio + "px"; - this.canvas.style.height = this.height / window.devicePixelRatio + "px"; - - // calculate key sizes - this.whiteKeyWidth = Math.floor(this.width / 52); - this.whiteKeyHeight = Math.floor(this.height * 0.9); - this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75); - this.blackKeyHeight = Math.floor(this.height * 0.5); - - this.blackKeyOffset = Math.floor(this.whiteKeyWidth - (this.blackKeyWidth / 2)); - this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015); - - this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7); - this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8); - this.whiteBlipX = Math.floor((this.whiteKeyWidth - this.whiteBlipWidth) / 2); - this.whiteBlipY = Math.floor(this.whiteKeyHeight - this.whiteBlipHeight * 1.2); - this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7); - this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8); - this.blackBlipY = Math.floor(this.blackKeyHeight - this.blackBlipHeight * 1.2); - this.blackBlipX = Math.floor((this.blackKeyWidth - this.blackBlipWidth) / 2); - - // prerender white key - this.whiteKeyRender = document.createElement("canvas"); - this.whiteKeyRender.width = this.whiteKeyWidth; - this.whiteKeyRender.height = this.height + 10; - var ctx = this.whiteKeyRender.getContext("2d"); - if(ctx.createLinearGradient) { - var gradient = ctx.createLinearGradient(0, 0, 0, this.whiteKeyHeight); - gradient.addColorStop(0, "#eee"); - gradient.addColorStop(0.75, "#fff"); - gradient.addColorStop(1, "#dad4d4"); - ctx.fillStyle = gradient; - } else { - ctx.fillStyle = "#fff"; - } - ctx.strokeStyle = "#000"; - ctx.lineJoin = "round"; - ctx.lineCap = "round"; - ctx.lineWidth = 10; - ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); - ctx.lineWidth = 4; - ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); - - // prerender black key - this.blackKeyRender = document.createElement("canvas"); - this.blackKeyRender.width = this.blackKeyWidth + 10; - this.blackKeyRender.height = this.blackKeyHeight + 10; - var ctx = this.blackKeyRender.getContext("2d"); - if(ctx.createLinearGradient) { - var gradient = ctx.createLinearGradient(0, 0, 0, this.blackKeyHeight); - gradient.addColorStop(0, "#000"); - gradient.addColorStop(1, "#444"); - ctx.fillStyle = gradient; - } else { - ctx.fillStyle = "#000"; - } - ctx.strokeStyle = "#222"; - ctx.lineJoin = "round"; - ctx.lineCap = "round"; - ctx.lineWidth = 8; - ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); - ctx.lineWidth = 4; - ctx.fillRect(ctx.lineWidth / 2, ctx.lineWidth / 2, this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); - - // prerender shadows - this.shadowRender = []; - var y = -this.canvas.height * 2; - for(var j = 0; j < 2; j++) { - var canvas = document.createElement("canvas"); - this.shadowRender[j] = canvas; - canvas.width = this.canvas.width; - canvas.height = this.canvas.height; - var ctx = canvas.getContext("2d"); - var sharp = j ? true : false; - ctx.lineJoin = "round"; - ctx.lineCap = "round"; - ctx.lineWidth = 1; - ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; - ctx.shadowBlur = this.keyMovement * 3; - ctx.shadowOffsetY = -y + this.keyMovement; - if(sharp) { - ctx.shadowOffsetX = this.keyMovement; - } else { - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = -y + this.keyMovement; - } - for(var i in this.piano.keys) { - if(!this.piano.keys.hasOwnProperty(i)) continue; - var key = this.piano.keys[i]; - if(key.sharp != sharp) continue; - - if(key.sharp) { - ctx.fillRect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2, - y + ctx.lineWidth / 2, - this.blackKeyWidth - ctx.lineWidth, this.blackKeyHeight - ctx.lineWidth); - } else { - ctx.fillRect(this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2, - y + ctx.lineWidth / 2, - this.whiteKeyWidth - ctx.lineWidth, this.whiteKeyHeight - ctx.lineWidth); - } - } - } - - // update key rects - for(var i in this.piano.keys) { - if(!this.piano.keys.hasOwnProperty(i)) continue; - var key = this.piano.keys[i]; - if(key.sharp) { - key.rect = new Rect(this.blackKeyOffset + this.whiteKeyWidth * key.spatial, 0, - this.blackKeyWidth, this.blackKeyHeight); - } else { - key.rect = new Rect(this.whiteKeyWidth * key.spatial, 0, - this.whiteKeyWidth, this.whiteKeyHeight); - } - } - }; - - CanvasRenderer.prototype.visualize = function(key, color) { - key.timePlayed = Date.now(); - key.blips.push({"time": key.timePlayed, "color": color}); - }; - - CanvasRenderer.prototype.redraw = function() { - var now = Date.now(); - var timeLoadedEnd = now - 1000; - var timePlayedEnd = now - 100; - var timeBlipEnd = now - 1000; - - this.ctx.save(); - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - // draw all keys - for(var j = 0; j < 2; j++) { - this.ctx.globalAlpha = 1.0; - this.ctx.drawImage(this.shadowRender[j], 0, 0); - var sharp = j ? true : false; - for(var i in this.piano.keys) { - if(!this.piano.keys.hasOwnProperty(i)) continue; - var key = this.piano.keys[i]; - if(key.sharp != sharp) continue; - - if(!key.loaded) { - this.ctx.globalAlpha = 0.2; - } else if(key.timeLoaded > timeLoadedEnd) { - this.ctx.globalAlpha = ((now - key.timeLoaded) / 1000) * 0.8 + 0.2; - } else { - this.ctx.globalAlpha = 1.0; - } - var y = 0; - if(key.timePlayed > timePlayedEnd) { - y = Math.floor(this.keyMovement - (((now - key.timePlayed) / 100) * this.keyMovement)); - } - var x = Math.floor(key.sharp ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial - : this.whiteKeyWidth * key.spatial); - var image = key.sharp ? this.blackKeyRender : this.whiteKeyRender; - this.ctx.drawImage(image, x, y); - - // render blips - if(key.blips.length) { - var alpha = this.ctx.globalAlpha; - var w, h; - if(key.sharp) { - x += this.blackBlipX; - y = this.blackBlipY; - w = this.blackBlipWidth; - h = this.blackBlipHeight; - } else { - x += this.whiteBlipX; - y = this.whiteBlipY; - w = this.whiteBlipWidth; - h = this.whiteBlipHeight; - } - for(var b = 0; b < key.blips.length; b++) { - var blip = key.blips[b]; - if(blip.time > timeBlipEnd) { - this.ctx.fillStyle = blip.color; - this.ctx.globalAlpha = alpha - ((now - blip.time) / 1000); - this.ctx.fillRect(x, y, w, h); - } else { - key.blips.splice(b, 1); - --b; - } - y -= Math.floor(h * 1.1); - } - } - } - } - this.ctx.restore(); - }; - - CanvasRenderer.prototype.renderNoteLyrics = function() { - // render lyric - for(var part_id in this.noteLyrics) { - if(!this.noteLyrics.hasOwnProperty(i)) continue; - var lyric = this.noteLyrics[part_id]; - var lyric_x = x; - var lyric_y = this.whiteKeyHeight + 1; - this.ctx.fillStyle = key.lyric.color; - var alpha = this.ctx.globalAlpha; - this.ctx.globalAlpha = alpha - ((now - key.lyric.time) / 1000); - this.ctx.fillRect(x, y, 10, 10); - } - }; - - CanvasRenderer.prototype.getHit = function(x, y) { - for(var j = 0; j < 2; j++) { - var sharp = j ? false : true; // black keys first - for(var i in this.piano.keys) { - if(!this.piano.keys.hasOwnProperty(i)) continue; - var key = this.piano.keys[i]; - if(key.sharp != sharp) continue; - if(key.rect.contains(x, y)) { - var v = y / (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight); - v += 0.25; - v *= DEFAULT_VELOCITY; - if(v > 1.0) v = 1.0; - return {"key": key, "v": v}; - } - } - } - return null; - }; - - - CanvasRenderer.isSupported = function() { - var canvas = document.createElement("canvas"); - return !!(canvas.getContext && canvas.getContext("2d")); - }; - - CanvasRenderer.translateMouseEvent = function(evt) { - var element = evt.target; - var offx = 0; - var offy = 0; - do { - if(!element) break; // wtf, wtf? - offx += element.offsetLeft; - offy += element.offsetTop; - } while(element = element.offsetParent); - return { - x: (evt.pageX - offx) * window.devicePixelRatio, - y: (evt.pageY - offy) * window.devicePixelRatio - } - }; - - - - - - - - - - - -// Soundpack Stuff by electrashave ♥ - -//////////////////////////////////////////////////////////////// - - function SoundSelector(piano) { - this.initialized = false; - this.keys = piano.keys; - this.loading = {}; - this.notification; - this.packs = []; - this.piano = piano; - this.soundSelection = localStorage.soundSelection ? localStorage.soundSelection : "MPP Classic"; - this.addPack({name: "MPP Classic", keys: Object.keys(this.piano.keys), ext: ".mp3", url: "/sounds/mppclassic/"}); - } - - SoundSelector.prototype.addPack = function(pack, load) { - var self = this; - self.loading[pack.url || pack] = true; - function add(obj) { - var added = false; - for (var i = 0; self.packs.length > i; i++) { - if (obj.name == self.packs[i].name) { - added = true; - break; - } - } - - if (added) return console.warn("Sounds already added!!"); //no adding soundpacks twice D:< - - if (obj.url.substr(obj.url.length-1) != "/") obj.url = obj.url + "/"; - var html = document.createElement("li"); - html.classList = "pack"; - html.innerText = obj.name + " (" + obj.keys.length + " keys)"; - html.onclick = function() { - self.loadPack(obj.name); - self.notification.close(); - }; - obj.html = html; - self.packs.push(obj); - self.packs.sort(function(a, b) { - if(a.name < b.name) return -1; - if(a.name > b.name) return 1; - return 0; - }); - if (load) self.loadPack(obj.name); - delete self.loading[obj.url]; - } - - if (typeof pack == "string") { - $.getJSON(pack + "/info.json").done(function(json) { - json.url = pack; - add(json); - }); - } else add(pack); //validate packs?? - }; - - SoundSelector.prototype.addPacks = function(packs) { - for (var i = 0; packs.length > i; i++) this.addPack(packs[i]); - }; - - SoundSelector.prototype.init = function() { - var self = this; - if (self.initialized) return console.warn("Sound selector already initialized!"); - - if (!!Object.keys(self.loading).length) return setTimeout(function() { - self.init(); - }, 250); - - $("#sound-btn").on("click", function() { - if (document.getElementById("Notification-Sound-Selector") != null) - return self.notification.close(); - var html = document.createElement("ul"); - //$(html).append("
Current Sound: " + self.soundSelection + "
"); - - for (var i = 0; self.packs.length > i; i++) { - var pack = self.packs[i]; - if (pack.name == self.soundSelection) pack.html.classList = "pack enabled"; - else pack.html.classList = "pack"; - html.appendChild(pack.html); - } - - self.notification = new Notification({title: "Sound Selector", html: html, id: "Sound-Selector", duration: -1, target: "#sound-btn"}); - }); - self.initialized = true; - self.loadPack(self.soundSelection, true); - }; - - SoundSelector.prototype.loadPack = function(pack, f) { - for (var i = 0; this.packs.length > i; i++) { - var p = this.packs[i]; - if (p.name == pack) { - pack = p; - break; - } - } - if (typeof pack == "string") { - console.warn("Sound pack does not exist! Loading default pack..."); - return this.loadPack("MPP Classic"); - } - - if (pack.name == this.soundSelection && !f) return; - if (pack.keys.length != Object.keys(this.piano.keys).length) { - this.piano.keys = {}; - for (var i = 0; pack.keys.length > i; i++) this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]]; - this.piano.renderer.resize(); - } - - var self = this; - for (var i in this.piano.keys) { - if (!this.piano.keys.hasOwnProperty(i)) continue; - (function() { - var key = self.piano.keys[i]; - key.loaded = false; - self.piano.audio.load(key.note, pack.url + key.note + pack.ext, function() { - key.loaded = true; - key.timeLoaded = Date.now(); - }); - })(); - } - if(localStorage) localStorage.soundSelection = pack.name; - this.soundSelection = pack.name; - }; - - SoundSelector.prototype.removePack = function(name) { - var found = false; - for (var i = 0; this.packs.length > i; i++) { - var pack = this.packs[i]; - if (pack.name == name) { - this.packs.splice(i, 1); - if (pack.name == this.soundSelection) this.loadPack(this.packs[0].name); //add mpp default if none? - break; - } - } - if (!found) console.warn("Sound pack not found!"); - }; - - - - - - - - - - - -// Pianoctor - -//////////////////////////////////////////////////////////////// - - var PianoKey = function(note, octave) { - this.note = note + octave; - this.baseNote = note; - this.octave = octave; - this.sharp = note.indexOf("s") != -1; - this.loaded = false; - this.timeLoaded = 0; - this.domElement = null; - this.timePlayed = 0; - this.blips = []; - }; - - var Piano = function(rootElement) { - - var piano = this; - piano.rootElement = rootElement; - piano.keys = {}; - - var white_spatial = 0; - var black_spatial = 0; - var black_it = 0; - var black_lut = [2, 1, 2, 1, 1]; - var addKey = function(note, octave) { - var key = new PianoKey(note, octave); - piano.keys[key.note] = key; - if(key.sharp) { - key.spatial = black_spatial; - black_spatial += black_lut[black_it % 5]; - ++black_it; - } else { - key.spatial = white_spatial; - ++white_spatial; - } - } - if(test_mode) { - addKey("c", 2); - } else { - addKey("a", -1); - addKey("as", -1); - addKey("b", -1); - var notes = "c cs d ds e f fs g gs a as b".split(" "); - for(var oct = 0; oct < 7; oct++) { - for(var i in notes) { - addKey(notes[i], oct); - } - } - addKey("c", 7); - } - - - this.renderer = new CanvasRenderer().init(this); - - window.addEventListener("resize", function() { - piano.renderer.resize(); - }); - - - window.AudioContext = window.AudioContext || window.webkitAudioContext || undefined; - var audio_engine = AudioEngineWeb; - this.audio = new audio_engine().init(); - }; - - Piano.prototype.play = function(note, vol, participant, delay_ms, lyric) { - if(!this.keys.hasOwnProperty(note) || !participant) return; - var key = this.keys[note]; - if(key.loaded) this.audio.play(key.note, vol, delay_ms, participant.id); - if(gMidiOutTest) gMidiOutTest(key.note, vol * 100, delay_ms); - var self = this; - setTimeout(function() { - self.renderer.visualize(key, participant.color); - if(lyric) { - - } - var jq_namediv = $(participant.nameDiv); - jq_namediv.addClass("play"); - setTimeout(function() { - jq_namediv.removeClass("play"); - }, 30); - }, delay_ms || 0); - }; - - Piano.prototype.stop = function(note, participant, delay_ms) { - if(!this.keys.hasOwnProperty(note)) return; - var key = this.keys[note]; - if(key.loaded) this.audio.stop(key.note, delay_ms, participant.id); - if(gMidiOutTest) gMidiOutTest(key.note, 0, delay_ms); - }; - - var gPiano = new Piano(document.getElementById("piano")); - - var gSoundSelector = new SoundSelector(gPiano); - gSoundSelector.addPacks(["/sounds/Emotional_2.0/", "/sounds/Harp/", "/sounds/Music_Box/", "/sounds/Vintage_Upright/", "/sounds/Steinway_Grand/", "/sounds/Emotional/", "/sounds/Untitled/"]); - gSoundSelector.init(); - - - - - - - - var gAutoSustain = false; - var gSustain = false; - - var gHeldNotes = {}; - var gSustainedNotes = {}; - - - function press(id, vol) { - if(!gClient.preventsPlaying() && gNoteQuota.spend(1)) { - gHeldNotes[id] = true; - gSustainedNotes[id] = true; - gPiano.play(id, vol !== undefined ? vol : DEFAULT_VELOCITY, gClient.getOwnParticipant(), 0); - gClient.startNote(id, vol); - } - } - - function release(id) { - if(gHeldNotes[id]) { - gHeldNotes[id] = false; - if((gAutoSustain || gSustain) && !enableSynth) { - gSustainedNotes[id] = true; - } else { - if(gNoteQuota.spend(1)) { - gPiano.stop(id, gClient.getOwnParticipant(), 0); - gClient.stopNote(id); - gSustainedNotes[id] = false; - } - } - } - } - - function pressSustain() { - gSustain = true; - } - - function releaseSustain() { - gSustain = false; - if(!gAutoSustain) { - for(var id in gSustainedNotes) { - if(gSustainedNotes.hasOwnProperty(id) && gSustainedNotes[id] && !gHeldNotes[id]) { - gSustainedNotes[id] = false; - if(gNoteQuota.spend(1)) { - gPiano.stop(id, gClient.getOwnParticipant(), 0); - gClient.stopNote(id); - } - } - } - } - } - - - - - - - - - -// internet science - -//////////////////////////////////////////////////////////////// - - var channel_id = decodeURIComponent(window.location.hash); - if(channel_id.substr(0, 1) == "#") channel_id = channel_id.substr(1); - if(channel_id == "") channel_id = "lobby"; - - var wssport = window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8080; - var gClient = new Client("wss://" + window.location.hostname + ":" + wssport); - gClient.setChannel(channel_id); - gClient.start(); - - gClient.on("disconnect", function(evt) { - console.log(evt); - }); - - // Setting status - (function() { - gClient.on("status", function(status) { - $("#status").text(status); - }); - gClient.on("count", function(count) { - if(count > 0) { - $("#status").html(''+count+' '+(count==1? 'person is' : 'people are')+' playing'); - document.title = "Piano (" + count + ")"; - } else { - document.title = "Multiplayer Piano"; - } - }); - })(); - - // Handle changes to participants - (function() { - gClient.on("participant added", function(part) { - - part.displayX = 150; - part.displayY = 50; - - // add nameDiv - var div = document.createElement("div"); - div.className = "name"; - div.participantId = part.id; - div.textContent = part.name || ""; - div.style.backgroundColor = part.color || "#777"; - if(gClient.participantId === part.id) { - $(div).addClass("me"); - } - if(gClient.channel && gClient.channel.crown && gClient.channel.crown.participantId === part.id) { - $(div).addClass("owner"); - } - if(gPianoMutes.indexOf(part._id) !== -1) { - $(part.nameDiv).addClass("muted-notes"); - } - if(gChatMutes.indexOf(part._id) !== -1) { - $(part.nameDiv).addClass("muted-chat"); - } - div.style.display = "none"; - part.nameDiv = $("#names")[0].appendChild(div); - $(part.nameDiv).fadeIn(2000); - - // sort names - var arr = $("#names .name"); - arr.sort(function(a, b) { - a = a.style.backgroundColor; // todo: sort based on user id instead - b = b.style.backgroundColor; - if (a > b) return 1; - else if (a < b) return -1; - else return 0; - }); - $("#names").html(arr); - - // add cursorDiv - if(gClient.participantId !== part.id || gSeeOwnCursor) { - var div = document.createElement("div"); - div.className = "cursor"; - div.style.display = "none"; - part.cursorDiv = $("#cursors")[0].appendChild(div); - $(part.cursorDiv).fadeIn(2000); - - var div = document.createElement("div"); - div.className = "name"; - div.style.backgroundColor = part.color || "#777" - div.textContent = part.name || ""; - part.cursorDiv.appendChild(div); - - } else { - part.cursorDiv = undefined; - } - }); - gClient.on("participant removed", function(part) { - // remove nameDiv - var nd = $(part.nameDiv); - var cd = $(part.cursorDiv); - cd.fadeOut(2000); - nd.fadeOut(2000, function() { - nd.remove(); - cd.remove(); - part.nameDiv = undefined; - part.cursorDiv = undefined; - }); - }); - gClient.on("participant update", function(part) { - var name = part.name || ""; - var color = part.color || "#777"; - part.nameDiv.style.backgroundColor = color; - part.nameDiv.textContent = name; - $(part.cursorDiv) - .find(".name") - .text(name) - .css("background-color", color); - }); - gClient.on("ch", function(msg) { - for(var id in gClient.ppl) { - if(gClient.ppl.hasOwnProperty(id)) { - var part = gClient.ppl[id]; - if(part.id === gClient.participantId) { - $(part.nameDiv).addClass("me"); - } else { - $(part.nameDiv).removeClass("me"); - } - if(msg.ch.crown && msg.ch.crown.participantId === part.id) { - $(part.nameDiv).addClass("owner"); - $(part.cursorDiv).addClass("owner"); - } else { - $(part.nameDiv).removeClass("owner"); - $(part.cursorDiv).removeClass("owner"); - } - if(gPianoMutes.indexOf(part._id) !== -1) { - $(part.nameDiv).addClass("muted-notes"); - } else { - $(part.nameDiv).removeClass("muted-notes"); - } - if(gChatMutes.indexOf(part._id) !== -1) { - $(part.nameDiv).addClass("muted-chat"); - } else { - $(part.nameDiv).removeClass("muted-chat"); - } - } - } - }); - function updateCursor(msg) { - const part = gClient.ppl[msg.id]; - if (part && part.cursorDiv) { - part.cursorDiv.style.left = msg.x + "%"; - part.cursorDiv.style.top = msg.y + "%"; - } - } - gClient.on("m", updateCursor); - gClient.on("participant added", updateCursor); - })(); - - - // Handle changes to crown - (function() { - var jqcrown = $('').appendTo(document.body).hide(); - var jqcountdown = $('').appendTo(jqcrown); - var countdown_interval; - jqcrown.click(function() { - gClient.sendArray([{m: "chown", id: gClient.participantId}]); - }); - gClient.on("ch", function(msg) { - if(msg.ch.crown) { - var crown = msg.ch.crown; - if(!crown.participantId || !gClient.ppl[crown.participantId]) { - var land_time = crown.time + 2000 - gClient.serverTimeOffset; - var avail_time = crown.time + 15000 - gClient.serverTimeOffset; - jqcountdown.text(""); - jqcrown.show(); - if(land_time - Date.now() <= 0) { - jqcrown.css({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}); - } else { - jqcrown.css({"left": crown.startPos.x + "%", "top": crown.startPos.y + "%"}); - jqcrown.addClass("spin"); - jqcrown.animate({"left": crown.endPos.x + "%", "top": crown.endPos.y + "%"}, 2000, "linear", function() { - jqcrown.removeClass("spin"); - }); - } - clearInterval(countdown_interval); - countdown_interval = setInterval(function() { - var time = Date.now(); - if(time >= land_time) { - var ms = avail_time - time; - if(ms > 0) { - jqcountdown.text(Math.ceil(ms / 1000) + "s"); - } else { - jqcountdown.text(""); - clearInterval(countdown_interval); - } - } - }, 1000); - } else { - jqcrown.hide(); - } - } else { - jqcrown.hide(); - } - }); - gClient.on("disconnect", function() { - jqcrown.fadeOut(2000); - }); - })(); - - - // Playing notes - gClient.on("n", function(msg) { - var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now(); - var participant = gClient.findParticipantById(msg.p); - if(gPianoMutes.indexOf(participant._id) !== -1) - return; - for(var i = 0; i < msg.n.length; i++) { - var note = msg.n[i]; - var ms = t + (note.d || 0); - if(ms < 0) { - ms = 0; - } - else if(ms > 10000) continue; - if(note.s) { - gPiano.stop(note.n, participant, ms); - } else { - var vel = (typeof note.v !== "undefined")? parseFloat(note.v) : DEFAULT_VELOCITY; - if(!vel) vel = 0; - else if(vel < 0) vel = 0; - else if (vel > 1) vel = 1; - gPiano.play(note.n, vel, participant, ms); - if(enableSynth) { - gPiano.stop(note.n, participant, ms + 1000); - } - } - } - }); - - // Send cursor updates - var mx = 0, last_mx = -10, my = 0, last_my = -10; - setInterval(function() { - if(Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) { - last_mx = mx; - last_my = my; - gClient.sendArray([{m: "m", x: mx, y: my}]); - if(gSeeOwnCursor) { - gClient.emit("m", { m: "m", id: gClient.participantId, x: mx, y: my }); - } - var part = gClient.getOwnParticipant(); - if(part) { - part.x = mx; - part.y = my; - } - } - }, 50); - $(document).mousemove(function(event) { - mx = ((event.pageX / $(window).width()) * 100).toFixed(2); - my = ((event.pageY / $(window).height()) * 100).toFixed(2); - }); - - - // Room settings button - (function() { - gClient.on("ch", function(msg) { - if(gClient.isOwner()) { - $("#room-settings-btn").show(); - } else { - $("#room-settings-btn").hide(); - } - }); - $("#room-settings-btn").click(function(evt) { - if(gClient.channel && gClient.isOwner()) { - var settings = gClient.channel.settings; - openModal("#room-settings"); - setTimeout(function() { - $("#room-settings .checkbox[name=visible]").prop("checked", settings.visible); - $("#room-settings .checkbox[name=chat]").prop("checked", settings.chat); - $("#room-settings .checkbox[name=crownsolo]").prop("checked", settings.crownsolo); - $("#room-settings input[name=color]").val(settings.color); - }, 100); - } - }); - $("#room-settings .submit").click(function() { - var settings = { - visible: $("#room-settings .checkbox[name=visible]").is(":checked"), - chat: $("#room-settings .checkbox[name=chat]").is(":checked"), - crownsolo: $("#room-settings .checkbox[name=crownsolo]").is(":checked"), - color: $("#room-settings input[name=color]").val() - }; - gClient.setChannelSettings(settings); - closeModal(); - }); - $("#room-settings .drop-crown").click(function() { - closeModal(); - if(confirm("This will drop the crown...!")) - gClient.sendArray([{m: "chown"}]); - }); - })(); - - // Handle notifications - gClient.on("notification", function(msg) { - new Notification(msg); - }); - - // Don't foget spin - gClient.on("ch", function(msg) { - var chidlo = msg.ch._id.toLowerCase(); - if(chidlo === "spin" || chidlo.substr(-5) === "/spin") { - $("#piano").addClass("spin"); - } else { - $("#piano").removeClass("spin"); - } - }); - - /*function eb() { + return this; + }; + + CanvasRenderer.prototype.resize = function (width, height) { + Renderer.prototype.resize.call(this, width, height); + if (this.width < 52 * 2) this.width = 52 * 2; + if (this.height < this.width * 0.2) + this.height = Math.floor(this.width * 0.2); + this.canvas.width = this.width; + this.canvas.height = this.height; + this.canvas.style.width = this.width / window.devicePixelRatio + "px"; + this.canvas.style.height = this.height / window.devicePixelRatio + "px"; + + // calculate key sizes + this.whiteKeyWidth = Math.floor(this.width / 52); + this.whiteKeyHeight = Math.floor(this.height * 0.9); + this.blackKeyWidth = Math.floor(this.whiteKeyWidth * 0.75); + this.blackKeyHeight = Math.floor(this.height * 0.5); + + this.blackKeyOffset = Math.floor( + this.whiteKeyWidth - this.blackKeyWidth / 2 + ); + this.keyMovement = Math.floor(this.whiteKeyHeight * 0.015); + + this.whiteBlipWidth = Math.floor(this.whiteKeyWidth * 0.7); + this.whiteBlipHeight = Math.floor(this.whiteBlipWidth * 0.8); + this.whiteBlipX = Math.floor( + (this.whiteKeyWidth - this.whiteBlipWidth) / 2 + ); + this.whiteBlipY = Math.floor( + this.whiteKeyHeight - this.whiteBlipHeight * 1.2 + ); + this.blackBlipWidth = Math.floor(this.blackKeyWidth * 0.7); + this.blackBlipHeight = Math.floor(this.blackBlipWidth * 0.8); + this.blackBlipY = Math.floor( + this.blackKeyHeight - this.blackBlipHeight * 1.2 + ); + this.blackBlipX = Math.floor( + (this.blackKeyWidth - this.blackBlipWidth) / 2 + ); + + // prerender white key + this.whiteKeyRender = document.createElement("canvas"); + this.whiteKeyRender.width = this.whiteKeyWidth; + this.whiteKeyRender.height = this.height + 10; + var ctx = this.whiteKeyRender.getContext("2d"); + if (ctx.createLinearGradient) { + var gradient = ctx.createLinearGradient( + 0, + 0, + 0, + this.whiteKeyHeight + ); + gradient.addColorStop(0, "#eee"); + gradient.addColorStop(0.75, "#fff"); + gradient.addColorStop(1, "#dad4d4"); + ctx.fillStyle = gradient; + } else { + ctx.fillStyle = "#fff"; + } + ctx.strokeStyle = "#000"; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + ctx.lineWidth = 10; + ctx.strokeRect( + ctx.lineWidth / 2, + ctx.lineWidth / 2, + this.whiteKeyWidth - ctx.lineWidth, + this.whiteKeyHeight - ctx.lineWidth + ); + ctx.lineWidth = 4; + ctx.fillRect( + ctx.lineWidth / 2, + ctx.lineWidth / 2, + this.whiteKeyWidth - ctx.lineWidth, + this.whiteKeyHeight - ctx.lineWidth + ); + + // prerender black key + this.blackKeyRender = document.createElement("canvas"); + this.blackKeyRender.width = this.blackKeyWidth + 10; + this.blackKeyRender.height = this.blackKeyHeight + 10; + var ctx = this.blackKeyRender.getContext("2d"); + if (ctx.createLinearGradient) { + var gradient = ctx.createLinearGradient( + 0, + 0, + 0, + this.blackKeyHeight + ); + gradient.addColorStop(0, "#000"); + gradient.addColorStop(1, "#444"); + ctx.fillStyle = gradient; + } else { + ctx.fillStyle = "#000"; + } + ctx.strokeStyle = "#222"; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + ctx.lineWidth = 8; + ctx.strokeRect( + ctx.lineWidth / 2, + ctx.lineWidth / 2, + this.blackKeyWidth - ctx.lineWidth, + this.blackKeyHeight - ctx.lineWidth + ); + ctx.lineWidth = 4; + ctx.fillRect( + ctx.lineWidth / 2, + ctx.lineWidth / 2, + this.blackKeyWidth - ctx.lineWidth, + this.blackKeyHeight - ctx.lineWidth + ); + + // prerender shadows + this.shadowRender = []; + var y = -this.canvas.height * 2; + for (var j = 0; j < 2; j++) { + var canvas = document.createElement("canvas"); + this.shadowRender[j] = canvas; + canvas.width = this.canvas.width; + canvas.height = this.canvas.height; + var ctx = canvas.getContext("2d"); + var sharp = j ? true : false; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + ctx.lineWidth = 1; + ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; + ctx.shadowBlur = this.keyMovement * 3; + ctx.shadowOffsetY = -y + this.keyMovement; + if (sharp) { + ctx.shadowOffsetX = this.keyMovement; + } else { + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = -y + this.keyMovement; + } + for (var i in this.piano.keys) { + if (!this.piano.keys.hasOwnProperty(i)) continue; + var key = this.piano.keys[i]; + if (key.sharp != sharp) continue; + + if (key.sharp) { + ctx.fillRect( + this.blackKeyOffset + + this.whiteKeyWidth * key.spatial + + ctx.lineWidth / 2, + y + ctx.lineWidth / 2, + this.blackKeyWidth - ctx.lineWidth, + this.blackKeyHeight - ctx.lineWidth + ); + } else { + ctx.fillRect( + this.whiteKeyWidth * key.spatial + ctx.lineWidth / 2, + y + ctx.lineWidth / 2, + this.whiteKeyWidth - ctx.lineWidth, + this.whiteKeyHeight - ctx.lineWidth + ); + } + } + } + + // update key rects + for (var i in this.piano.keys) { + if (!this.piano.keys.hasOwnProperty(i)) continue; + var key = this.piano.keys[i]; + if (key.sharp) { + key.rect = new Rect( + this.blackKeyOffset + this.whiteKeyWidth * key.spatial, + 0, + this.blackKeyWidth, + this.blackKeyHeight + ); + } else { + key.rect = new Rect( + this.whiteKeyWidth * key.spatial, + 0, + this.whiteKeyWidth, + this.whiteKeyHeight + ); + } + } + }; + + CanvasRenderer.prototype.visualize = function (key, color) { + key.timePlayed = Date.now(); + key.blips.push({ time: key.timePlayed, color: color }); + }; + + CanvasRenderer.prototype.redraw = function () { + var now = Date.now(); + var timeLoadedEnd = now - 1000; + var timePlayedEnd = now - 100; + var timeBlipEnd = now - 1000; + + this.ctx.save(); + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + // draw all keys + for (var j = 0; j < 2; j++) { + this.ctx.globalAlpha = 1.0; + this.ctx.drawImage(this.shadowRender[j], 0, 0); + var sharp = j ? true : false; + for (var i in this.piano.keys) { + if (!this.piano.keys.hasOwnProperty(i)) continue; + var key = this.piano.keys[i]; + if (key.sharp != sharp) continue; + + if (!key.loaded) { + this.ctx.globalAlpha = 0.2; + } else if (key.timeLoaded > timeLoadedEnd) { + this.ctx.globalAlpha = + ((now - key.timeLoaded) / 1000) * 0.8 + 0.2; + } else { + this.ctx.globalAlpha = 1.0; + } + var y = 0; + if (key.timePlayed > timePlayedEnd) { + y = Math.floor( + this.keyMovement - + ((now - key.timePlayed) / 100) * this.keyMovement + ); + } + var x = Math.floor( + key.sharp + ? this.blackKeyOffset + this.whiteKeyWidth * key.spatial + : this.whiteKeyWidth * key.spatial + ); + var image = key.sharp + ? this.blackKeyRender + : this.whiteKeyRender; + this.ctx.drawImage(image, x, y); + + // render blips + if (key.blips.length) { + var alpha = this.ctx.globalAlpha; + var w, h; + if (key.sharp) { + x += this.blackBlipX; + y = this.blackBlipY; + w = this.blackBlipWidth; + h = this.blackBlipHeight; + } else { + x += this.whiteBlipX; + y = this.whiteBlipY; + w = this.whiteBlipWidth; + h = this.whiteBlipHeight; + } + for (var b = 0; b < key.blips.length; b++) { + var blip = key.blips[b]; + if (blip.time > timeBlipEnd) { + this.ctx.fillStyle = blip.color; + this.ctx.globalAlpha = + alpha - (now - blip.time) / 1000; + this.ctx.fillRect(x, y, w, h); + } else { + key.blips.splice(b, 1); + --b; + } + y -= Math.floor(h * 1.1); + } + } + } + } + this.ctx.restore(); + }; + + CanvasRenderer.prototype.renderNoteLyrics = function () { + // render lyric + for (var part_id in this.noteLyrics) { + if (!this.noteLyrics.hasOwnProperty(i)) continue; + var lyric = this.noteLyrics[part_id]; + var lyric_x = x; + var lyric_y = this.whiteKeyHeight + 1; + this.ctx.fillStyle = key.lyric.color; + var alpha = this.ctx.globalAlpha; + this.ctx.globalAlpha = alpha - (now - key.lyric.time) / 1000; + this.ctx.fillRect(x, y, 10, 10); + } + }; + + CanvasRenderer.prototype.getHit = function (x, y) { + for (var j = 0; j < 2; j++) { + var sharp = j ? false : true; // black keys first + for (var i in this.piano.keys) { + if (!this.piano.keys.hasOwnProperty(i)) continue; + var key = this.piano.keys[i]; + if (key.sharp != sharp) continue; + if (key.rect.contains(x, y)) { + var v = + y / + (key.sharp ? this.blackKeyHeight : this.whiteKeyHeight); + v += 0.25; + v *= DEFAULT_VELOCITY; + if (v > 1.0) v = 1.0; + return { key: key, v: v }; + } + } + } + return null; + }; + + CanvasRenderer.isSupported = function () { + var canvas = document.createElement("canvas"); + return !!(canvas.getContext && canvas.getContext("2d")); + }; + + CanvasRenderer.translateMouseEvent = function (evt) { + var element = evt.target; + var offx = 0; + var offy = 0; + do { + if (!element) break; // wtf, wtf? + offx += element.offsetLeft; + offy += element.offsetTop; + } while ((element = element.offsetParent)); + return { + x: (evt.pageX - offx) * window.devicePixelRatio, + y: (evt.pageY - offy) * window.devicePixelRatio + }; + }; + + // Soundpack Stuff by electrashave ♥ + + //////////////////////////////////////////////////////////////// + + function SoundSelector(piano) { + this.initialized = false; + this.keys = piano.keys; + this.loading = {}; + this.notification; + this.packs = []; + this.piano = piano; + this.soundSelection = localStorage.soundSelection + ? localStorage.soundSelection + : "MPP Classic"; + this.addPack({ + name: "MPP Classic", + keys: Object.keys(this.piano.keys), + ext: ".mp3", + url: "/sounds/mppclassic/" + }); + } + + SoundSelector.prototype.addPack = function (pack, load) { + var self = this; + self.loading[pack.url || pack] = true; + function add(obj) { + var added = false; + for (var i = 0; self.packs.length > i; i++) { + if (obj.name == self.packs[i].name) { + added = true; + break; + } + } + + if (added) return console.warn("Sounds already added!!"); //no adding soundpacks twice D:< + + if (obj.url.substr(obj.url.length - 1) != "/") + obj.url = obj.url + "/"; + var html = document.createElement("li"); + html.classList = "pack"; + html.innerText = obj.name + " (" + obj.keys.length + " keys)"; + html.onclick = function () { + self.loadPack(obj.name); + self.notification.close(); + }; + obj.html = html; + self.packs.push(obj); + self.packs.sort(function (a, b) { + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + if (load) self.loadPack(obj.name); + delete self.loading[obj.url]; + } + + if (typeof pack == "string") { + $.getJSON(pack + "/info.json").done(function (json) { + json.url = pack; + add(json); + }); + } else add(pack); //validate packs?? + }; + + SoundSelector.prototype.addPacks = function (packs) { + for (var i = 0; packs.length > i; i++) this.addPack(packs[i]); + }; + + SoundSelector.prototype.init = function () { + var self = this; + if (self.initialized) + return console.warn("Sound selector already initialized!"); + + if (!!Object.keys(self.loading).length) + return setTimeout(function () { + self.init(); + }, 250); + + $("#sound-btn").on("click", function () { + if (document.getElementById("Notification-Sound-Selector") != null) + return self.notification.close(); + var html = document.createElement("ul"); + //$(html).append("Current Sound: " + self.soundSelection + "
"); + + for (var i = 0; self.packs.length > i; i++) { + var pack = self.packs[i]; + if (pack.name == self.soundSelection) + pack.html.classList = "pack enabled"; + else pack.html.classList = "pack"; + html.appendChild(pack.html); + } + + self.notification = new Notification({ + title: "Sound Selector", + html: html, + id: "Sound-Selector", + duration: -1, + target: "#sound-btn" + }); + }); + self.initialized = true; + self.loadPack(self.soundSelection, true); + }; + + SoundSelector.prototype.loadPack = function (pack, f) { + for (var i = 0; this.packs.length > i; i++) { + var p = this.packs[i]; + if (p.name == pack) { + pack = p; + break; + } + } + if (typeof pack == "string") { + console.warn("Sound pack does not exist! Loading default pack..."); + return this.loadPack("MPP Classic"); + } + + if (pack.name == this.soundSelection && !f) return; + if (pack.keys.length != Object.keys(this.piano.keys).length) { + this.piano.keys = {}; + for (var i = 0; pack.keys.length > i; i++) + this.piano.keys[pack.keys[i]] = this.keys[pack.keys[i]]; + this.piano.renderer.resize(); + } + + var self = this; + for (var i in this.piano.keys) { + if (!this.piano.keys.hasOwnProperty(i)) continue; + (function () { + var key = self.piano.keys[i]; + key.loaded = false; + self.piano.audio.load( + key.note, + pack.url + key.note + pack.ext, + function () { + key.loaded = true; + key.timeLoaded = Date.now(); + } + ); + })(); + } + if (localStorage) localStorage.soundSelection = pack.name; + this.soundSelection = pack.name; + }; + + SoundSelector.prototype.removePack = function (name) { + var found = false; + for (var i = 0; this.packs.length > i; i++) { + var pack = this.packs[i]; + if (pack.name == name) { + this.packs.splice(i, 1); + if (pack.name == this.soundSelection) + this.loadPack(this.packs[0].name); //add mpp default if none? + break; + } + } + if (!found) console.warn("Sound pack not found!"); + }; + + // Pianoctor + + //////////////////////////////////////////////////////////////// + + var PianoKey = function (note, octave) { + this.note = note + octave; + this.baseNote = note; + this.octave = octave; + this.sharp = note.indexOf("s") != -1; + this.loaded = false; + this.timeLoaded = 0; + this.domElement = null; + this.timePlayed = 0; + this.blips = []; + }; + + var Piano = function (rootElement) { + var piano = this; + piano.rootElement = rootElement; + piano.keys = {}; + + var white_spatial = 0; + var black_spatial = 0; + var black_it = 0; + var black_lut = [2, 1, 2, 1, 1]; + var addKey = function (note, octave) { + var key = new PianoKey(note, octave); + piano.keys[key.note] = key; + if (key.sharp) { + key.spatial = black_spatial; + black_spatial += black_lut[black_it % 5]; + ++black_it; + } else { + key.spatial = white_spatial; + ++white_spatial; + } + }; + if (test_mode) { + addKey("c", 2); + } else { + addKey("a", -1); + addKey("as", -1); + addKey("b", -1); + var notes = "c cs d ds e f fs g gs a as b".split(" "); + for (var oct = 0; oct < 7; oct++) { + for (var i in notes) { + addKey(notes[i], oct); + } + } + addKey("c", 7); + } + + this.renderer = new CanvasRenderer().init(this); + + window.addEventListener("resize", function () { + piano.renderer.resize(); + }); + + window.AudioContext = + window.AudioContext || window.webkitAudioContext || undefined; + var audio_engine = AudioEngineWeb; + this.audio = new audio_engine().init(); + }; + + Piano.prototype.play = function (note, vol, participant, delay_ms, lyric) { + if (!this.keys.hasOwnProperty(note) || !participant) return; + var key = this.keys[note]; + if (key.loaded) + this.audio.play(key.note, vol, delay_ms, participant.id); + if (gMidiOutTest) gMidiOutTest(key.note, vol * 100, delay_ms); + var self = this; + setTimeout(function () { + self.renderer.visualize(key, participant.color); + if (lyric) { + } + var jq_namediv = $(participant.nameDiv); + jq_namediv.addClass("play"); + setTimeout(function () { + jq_namediv.removeClass("play"); + }, 30); + }, delay_ms || 0); + }; + + Piano.prototype.stop = function (note, participant, delay_ms) { + if (!this.keys.hasOwnProperty(note)) return; + var key = this.keys[note]; + if (key.loaded) this.audio.stop(key.note, delay_ms, participant.id); + if (gMidiOutTest) gMidiOutTest(key.note, 0, delay_ms); + }; + + var gPiano = new Piano(document.getElementById("piano")); + + var gSoundSelector = new SoundSelector(gPiano); + gSoundSelector.addPacks([ + "/sounds/Emotional_2.0/", + "/sounds/Harp/", + "/sounds/Music_Box/", + "/sounds/Vintage_Upright/", + "/sounds/Steinway_Grand/", + "/sounds/Emotional/", + "/sounds/Untitled/" + ]); + gSoundSelector.init(); + + var gAutoSustain = false; + var gSustain = false; + + var gHeldNotes = {}; + var gSustainedNotes = {}; + + function press(id, vol) { + if (!gClient.preventsPlaying() && gNoteQuota.spend(1)) { + gHeldNotes[id] = true; + gSustainedNotes[id] = true; + gPiano.play( + id, + vol !== undefined ? vol : DEFAULT_VELOCITY, + gClient.getOwnParticipant(), + 0 + ); + gClient.startNote(id, vol); + } + } + + function release(id) { + if (gHeldNotes[id]) { + gHeldNotes[id] = false; + if ((gAutoSustain || gSustain) && !enableSynth) { + gSustainedNotes[id] = true; + } else { + if (gNoteQuota.spend(1)) { + gPiano.stop(id, gClient.getOwnParticipant(), 0); + gClient.stopNote(id); + gSustainedNotes[id] = false; + } + } + } + } + + function pressSustain() { + gSustain = true; + } + + function releaseSustain() { + gSustain = false; + if (!gAutoSustain) { + for (var id in gSustainedNotes) { + if ( + gSustainedNotes.hasOwnProperty(id) && + gSustainedNotes[id] && + !gHeldNotes[id] + ) { + gSustainedNotes[id] = false; + if (gNoteQuota.spend(1)) { + gPiano.stop(id, gClient.getOwnParticipant(), 0); + gClient.stopNote(id); + } + } + } + } + } + + // internet science + + //////////////////////////////////////////////////////////////// + + var channel_id = decodeURIComponent(window.location.hash); + if (channel_id.substr(0, 1) == "#") channel_id = channel_id.substr(1); + if (channel_id == "") channel_id = "lobby"; + + var wssport = + window.location.hostname == "www.multiplayerpiano.com" ? 443 : 8443; + var gClient = new Client( + "wss://" + window.location.hostname + ":" + wssport + ); + gClient.setChannel(channel_id); + gClient.start(); + + gClient.on("disconnect", function (evt) { + console.log(evt); + }); + + // Setting status + (function () { + gClient.on("status", function (status) { + $("#status").text(status); + }); + gClient.on("count", function (count) { + if (count > 0) { + $("#status").html( + '' + + count + + " " + + (count == 1 ? "person is" : "people are") + + " playing" + ); + document.title = "Piano (" + count + ")"; + } else { + document.title = "Multiplayer Piano"; + } + }); + })(); + + // Handle changes to participants + (function () { + gClient.on("participant added", function (part) { + part.displayX = 150; + part.displayY = 50; + + // add nameDiv + var div = document.createElement("div"); + div.className = "name"; + div.participantId = part.id; + div.textContent = part.name || ""; + div.style.backgroundColor = part.color || "#777"; + if (gClient.participantId === part.id) { + $(div).addClass("me"); + } + if ( + gClient.channel && + gClient.channel.crown && + gClient.channel.crown.participantId === part.id + ) { + $(div).addClass("owner"); + } + if (gPianoMutes.indexOf(part._id) !== -1) { + $(part.nameDiv).addClass("muted-notes"); + } + if (gChatMutes.indexOf(part._id) !== -1) { + $(part.nameDiv).addClass("muted-chat"); + } + div.style.display = "none"; + part.nameDiv = $("#names")[0].appendChild(div); + $(part.nameDiv).fadeIn(2000); + + // sort names + var arr = $("#names .name"); + arr.sort(function (a, b) { + a = a.style.backgroundColor; // todo: sort based on user id instead + b = b.style.backgroundColor; + if (a > b) return 1; + else if (a < b) return -1; + else return 0; + }); + $("#names").html(arr); + + // add cursorDiv + if (gClient.participantId !== part.id || gSeeOwnCursor) { + var div = document.createElement("div"); + div.className = "cursor"; + div.style.display = "none"; + part.cursorDiv = $("#cursors")[0].appendChild(div); + $(part.cursorDiv).fadeIn(2000); + + var div = document.createElement("div"); + div.className = "name"; + div.style.backgroundColor = part.color || "#777"; + div.textContent = part.name || ""; + part.cursorDiv.appendChild(div); + } else { + part.cursorDiv = undefined; + } + }); + gClient.on("participant removed", function (part) { + // remove nameDiv + var nd = $(part.nameDiv); + var cd = $(part.cursorDiv); + cd.fadeOut(2000); + nd.fadeOut(2000, function () { + nd.remove(); + cd.remove(); + part.nameDiv = undefined; + part.cursorDiv = undefined; + }); + }); + gClient.on("participant update", function (part) { + var name = part.name || ""; + var color = part.color || "#777"; + part.nameDiv.style.backgroundColor = color; + part.nameDiv.textContent = name; + $(part.cursorDiv) + .find(".name") + .text(name) + .css("background-color", color); + }); + gClient.on("ch", function (msg) { + for (var id in gClient.ppl) { + if (gClient.ppl.hasOwnProperty(id)) { + var part = gClient.ppl[id]; + if (part.id === gClient.participantId) { + $(part.nameDiv).addClass("me"); + } else { + $(part.nameDiv).removeClass("me"); + } + if ( + msg.ch.crown && + msg.ch.crown.participantId === part.id + ) { + $(part.nameDiv).addClass("owner"); + $(part.cursorDiv).addClass("owner"); + } else { + $(part.nameDiv).removeClass("owner"); + $(part.cursorDiv).removeClass("owner"); + } + if (gPianoMutes.indexOf(part._id) !== -1) { + $(part.nameDiv).addClass("muted-notes"); + } else { + $(part.nameDiv).removeClass("muted-notes"); + } + if (gChatMutes.indexOf(part._id) !== -1) { + $(part.nameDiv).addClass("muted-chat"); + } else { + $(part.nameDiv).removeClass("muted-chat"); + } + } + } + }); + function updateCursor(msg) { + const part = gClient.ppl[msg.id]; + if (part && part.cursorDiv) { + part.cursorDiv.style.left = msg.x + "%"; + part.cursorDiv.style.top = msg.y + "%"; + } + } + gClient.on("m", updateCursor); + gClient.on("participant added", updateCursor); + })(); + + // Handle changes to crown + (function () { + var jqcrown = $('') + .appendTo(document.body) + .hide(); + var jqcountdown = $("").appendTo(jqcrown); + var countdown_interval; + jqcrown.click(function () { + gClient.sendArray([{ m: "chown", id: gClient.participantId }]); + }); + gClient.on("ch", function (msg) { + if (msg.ch.crown) { + var crown = msg.ch.crown; + if (!crown.participantId || !gClient.ppl[crown.participantId]) { + var land_time = + crown.time + 2000 - gClient.serverTimeOffset; + var avail_time = + crown.time + 15000 - gClient.serverTimeOffset; + jqcountdown.text(""); + jqcrown.show(); + if (land_time - Date.now() <= 0) { + jqcrown.css({ + left: crown.endPos.x + "%", + top: crown.endPos.y + "%" + }); + } else { + jqcrown.css({ + left: crown.startPos.x + "%", + top: crown.startPos.y + "%" + }); + jqcrown.addClass("spin"); + jqcrown.animate( + { + left: crown.endPos.x + "%", + top: crown.endPos.y + "%" + }, + 2000, + "linear", + function () { + jqcrown.removeClass("spin"); + } + ); + } + clearInterval(countdown_interval); + countdown_interval = setInterval(function () { + var time = Date.now(); + if (time >= land_time) { + var ms = avail_time - time; + if (ms > 0) { + jqcountdown.text(Math.ceil(ms / 1000) + "s"); + } else { + jqcountdown.text(""); + clearInterval(countdown_interval); + } + } + }, 1000); + } else { + jqcrown.hide(); + } + } else { + jqcrown.hide(); + } + }); + gClient.on("disconnect", function () { + jqcrown.fadeOut(2000); + }); + })(); + + // Playing notes + gClient.on("n", function (msg) { + var t = msg.t - gClient.serverTimeOffset + TIMING_TARGET - Date.now(); + var participant = gClient.findParticipantById(msg.p); + if (gPianoMutes.indexOf(participant._id) !== -1) return; + for (var i = 0; i < msg.n.length; i++) { + var note = msg.n[i]; + var ms = t + (note.d || 0); + if (ms < 0) { + ms = 0; + } else if (ms > 10000) continue; + if (note.s) { + gPiano.stop(note.n, participant, ms); + } else { + var vel = + typeof note.v !== "undefined" + ? parseFloat(note.v) + : DEFAULT_VELOCITY; + if (!vel) vel = 0; + else if (vel < 0) vel = 0; + else if (vel > 1) vel = 1; + gPiano.play(note.n, vel, participant, ms); + if (enableSynth) { + gPiano.stop(note.n, participant, ms + 1000); + } + } + } + }); + + // Send cursor updates + var mx = 0, + last_mx = -10, + my = 0, + last_my = -10; + setInterval(function () { + if (Math.abs(mx - last_mx) > 0.1 || Math.abs(my - last_my) > 0.1) { + last_mx = mx; + last_my = my; + gClient.sendArray([{ m: "m", x: mx, y: my }]); + if (gSeeOwnCursor) { + gClient.emit("m", { + m: "m", + id: gClient.participantId, + x: mx, + y: my + }); + } + var part = gClient.getOwnParticipant(); + if (part) { + part.x = mx; + part.y = my; + } + } + }, 50); + $(document).mousemove(function (event) { + mx = ((event.pageX / $(window).width()) * 100).toFixed(2); + my = ((event.pageY / $(window).height()) * 100).toFixed(2); + }); + + // Room settings button + (function () { + gClient.on("ch", function (msg) { + if (gClient.isOwner()) { + $("#room-settings-btn").show(); + } else { + $("#room-settings-btn").hide(); + } + }); + $("#room-settings-btn").click(function (evt) { + if (gClient.channel && gClient.isOwner()) { + var settings = gClient.channel.settings; + openModal("#room-settings"); + setTimeout(function () { + $("#room-settings .checkbox[name=visible]").prop( + "checked", + settings.visible + ); + $("#room-settings .checkbox[name=chat]").prop( + "checked", + settings.chat + ); + $("#room-settings .checkbox[name=crownsolo]").prop( + "checked", + settings.crownsolo + ); + $("#room-settings input[name=color]").val(settings.color); + }, 100); + } + }); + $("#room-settings .submit").click(function () { + var settings = { + visible: $("#room-settings .checkbox[name=visible]").is( + ":checked" + ), + chat: $("#room-settings .checkbox[name=chat]").is(":checked"), + crownsolo: $("#room-settings .checkbox[name=crownsolo]").is( + ":checked" + ), + color: $("#room-settings input[name=color]").val() + }; + gClient.setChannelSettings(settings); + closeModal(); + }); + $("#room-settings .drop-crown").click(function () { + closeModal(); + if (confirm("This will drop the crown...!")) + gClient.sendArray([{ m: "chown" }]); + }); + })(); + + // Handle notifications + gClient.on("notification", function (msg) { + new Notification(msg); + }); + + // Don't foget spin + gClient.on("ch", function (msg) { + var chidlo = msg.ch._id.toLowerCase(); + if (chidlo === "spin" || chidlo.substr(-5) === "/spin") { + $("#piano").addClass("spin"); + } else { + $("#piano").removeClass("spin"); + } + }); + + /*function eb() { if(gClient.channel && gClient.channel._id.toLowerCase() === "test/fishing") { ebsprite.start(gClient); } else { @@ -1493,1742 +1511,1890 @@ Rect.prototype.contains = function(x, y) { eb(); }*/ - // Crownsolo notice - gClient.on("ch", function(msg) { - let notice = ""; - let has_notice = false; - if(msg.ch.settings.crownsolo) { - has_notice = true; - notice += 'This room is set to "only the owner can play."
'; - } - if(msg.ch.settings['no cussing']){ - has_notice = true; - notice += 'This room is set to "no cussing."
'; - } - let notice_div = $("#room-notice"); - if(has_notice) { - notice_div.html(notice); - if(notice_div.is(':hidden')) notice_div.fadeIn(1000); - } else { - if(notice_div.is(':visible')) notice_div.fadeOut(1000); - } - }); - gClient.on("disconnect", function() { - $("#room-notice").fadeOut(1000); - }); - - - // Background color - (function() { - var old_color1 = new Color("#000000"); - var old_color2 = new Color("#000000"); - function setColor(hex, hex2) { - var color1 = new Color(hex); - var color2 = new Color(hex2 || hex); - if(!hex2) - color2.add(-0x40, -0x40, -0x40); - - var bottom = document.getElementById("bottom"); - - var duration = 500; - var step = 0; - var steps = 30; - var step_ms = duration / steps; - var difference = new Color(color1.r, color1.g, color1.b); - difference.r -= old_color1.r; - difference.g -= old_color1.g; - difference.b -= old_color1.b; - var inc1 = new Color(difference.r / steps, difference.g / steps, difference.b / steps); - difference = new Color(color2.r, color2.g, color2.b); - difference.r -= old_color2.r; - difference.g -= old_color2.g; - difference.b -= old_color2.b; - var inc2 = new Color(difference.r / steps, difference.g / steps, difference.b / steps); - var iv; - iv = setInterval(function() { - old_color1.add(inc1.r, inc1.g, inc1.b); - old_color2.add(inc2.r, inc2.g, inc2.b); - document.body.style.background = "radial-gradient(ellipse at center, "+old_color1.toHexa()+" 0%,"+old_color2.toHexa()+" 100%)"; - bottom.style.background = old_color2.toHexa(); - if(++step >= steps) { - clearInterval(iv); - old_color1 = color1; - old_color2 = color2; - document.body.style.background = "radial-gradient(ellipse at center, "+color1.toHexa()+" 0%,"+color2.toHexa()+" 100%)"; - bottom.style.background = color2.toHexa(); - } - }, step_ms); - } - - function setColorToDefault() { - setColor("#000000", "#000000"); - } - - setColorToDefault(); - - gClient.on("ch", function(ch) { - if(ch.ch.settings) { - if(ch.ch.settings.color) { - setColor(ch.ch.settings.color, ch.ch.settings.color2); - } else { - setColorToDefault(); - } - } - }); - })(); - - - - - - var gPianoMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "").split(',').filter(v => v); - var gChatMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "").split(',').filter(v => v); - - - - - - - - - - - - - - - - - - - - var volume_slider = document.getElementById("volume-slider"); - volume_slider.value = gPiano.audio.volume; - $("#volume-label").text("Volume: " + Math.floor(gPiano.audio.volume * 100) + "%"); - volume_slider.addEventListener("input", function(evt) { - var v = +volume_slider.value; - gPiano.audio.setVolume(v); - if (window.localStorage) localStorage.volume = v; - $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%"); - }); - - - - - var Note = function(note, octave) { - this.note = note; - this.octave = octave || 0; - }; - - - - var n = function(a, b) { return {note: new Note(a, b), held: false}; }; - var key_binding = { - 65: n("gs"), - 90: n("a"), - 83: n("as"), - 88: n("b"), - 67: n("c", 1), - 70: n("cs", 1), - 86: n("d", 1), - 71: n("ds", 1), - 66: n("e", 1), - 78: n("f", 1), - 74: n("fs", 1), - 77: n("g", 1), - 75: n("gs", 1), - 188: n("a", 1), - 76: n("as", 1), - 190: n("b", 1), - 191: n("c", 2), - 222: n("cs", 2), - - 49: n("gs", 1), - 81: n("a", 1), - 50: n("as", 1), - 87: n("b", 1), - 69: n("c", 2), - 52: n("cs", 2), - 82: n("d", 2), - 53: n("ds", 2), - 84: n("e", 2), - 89: n("f", 2), - 55: n("fs", 2), - 85: n("g", 2), - 56: n("gs", 2), - 73: n("a", 2), - 57: n("as", 2), - 79: n("b", 2), - 80: n("c", 3), - 189: n("cs", 3), - 173: n("cs", 3), // firefox why - 219: n("d", 3), - 187: n("ds", 3), - 61: n("ds", 3), // firefox why - 221: n("e", 3) - }; - - var capsLockKey = false; - - var transpose_octave = 0; - - function handleKeyDown(evt) { - //console.log(evt); - var code = parseInt(evt.keyCode); - if(key_binding[code] !== undefined) { - var binding = key_binding[code]; - if(!binding.held) { - binding.held = true; - - var note = binding.note; - var octave = 1 + note.octave + transpose_octave; - if(evt.shiftKey) ++octave; - else if(capsLockKey || evt.ctrlKey) --octave; - note = note.note + octave; - var vol = velocityFromMouseY(); - press(note, vol); - } - - if(++gKeyboardSeq == 3) { - gKnowsYouCanUseKeyboard = true; - if(window.gKnowsYouCanUseKeyboardTimeout) clearTimeout(gKnowsYouCanUseKeyboardTimeout); - if(localStorage) localStorage.knowsYouCanUseKeyboard = true; - if(window.gKnowsYouCanUseKeyboardNotification) gKnowsYouCanUseKeyboardNotification.close(); - } - - evt.preventDefault(); - evt.stopPropagation(); - return false; - } else if(code == 20) { // Caps Lock - capsLockKey = true; - evt.preventDefault(); - } else if(code === 0x20) { // Space Bar - pressSustain(); - evt.preventDefault(); - } else if((code === 38 || code === 39) && transpose_octave < 3) { - ++transpose_octave; - } else if((code === 40 || code === 37) && transpose_octave > -2) { - --transpose_octave; - } else if(code == 9) { // Tab (don't tab away from the piano) - evt.preventDefault(); - } else if(code == 8) { // Backspace (don't navigate Back) - gAutoSustain = !gAutoSustain; - evt.preventDefault(); - } - }; - - function handleKeyUp(evt) { - var code = parseInt(evt.keyCode); - if(key_binding[code] !== undefined) { - var binding = key_binding[code]; - if(binding.held) { - binding.held = false; - - var note = binding.note; - var octave = 1 + note.octave + transpose_octave; - if(evt.shiftKey) ++octave; - else if(capsLockKey || evt.ctrlKey) --octave; - note = note.note + octave; - release(note); - } - - evt.preventDefault(); - evt.stopPropagation(); - return false; - } else if(code == 20) { // Caps Lock - capsLockKey = false; - evt.preventDefault(); - } else if(code === 0x20) { // Space Bar - releaseSustain(); - evt.preventDefault(); - } - }; - - function handleKeyPress(evt) { - evt.preventDefault(); - evt.stopPropagation(); - if(evt.keyCode == 27 || evt.keyCode == 13) { - //$("#chat input").focus(); - } - return false; - }; - - var recapListener = function(evt) { - captureKeyboard(); - }; - - function captureKeyboard() { - $("#piano").off("mousedown", recapListener); - $("#piano").off("touchstart", recapListener); - $(document).on("keydown", handleKeyDown ); - $(document).on("keyup", handleKeyUp); - $(window).on("keypress", handleKeyPress ); - }; - - function releaseKeyboard() { - $(document).off("keydown", handleKeyDown ); - $(document).off("keyup", handleKeyUp); - $(window).off("keypress", handleKeyPress ); - $("#piano").on("mousedown", recapListener); - $("#piano").on("touchstart", recapListener); - }; - - captureKeyboard(); - - - var velocityFromMouseY = function() { - return 0.1 + (my / 100) * 0.6; - }; - - - - - - // NoteQuota - var gNoteQuota = (function() { - var last_rat = 0; - var nqjq = $("#quota .value"); - setInterval(function() { - gNoteQuota.tick(); - }, 2000); - return new NoteQuota(function(points) { - // update UI - var rat = (points / this.max) * 100; - if(rat <= last_rat) - nqjq.stop(true, true).css("width", rat.toFixed(0) + "%"); - else - nqjq.stop(true, true).animate({"width": rat.toFixed(0) + "%"}, 2000, "linear"); - last_rat = rat; - }); - })(); - gClient.on("nq", function(nq_params) { - gNoteQuota.setParams(nq_params); - }); - gClient.on("disconnect", function() { - gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE); - }); - - - - // click participant names - (function() { - var ele = document.getElementById("names"); - var touchhandler = function(e) { - var target_jq = $(e.target); - if(target_jq.hasClass("name")) { - target_jq.addClass("play"); - if(e.target.participantId == gClient.participantId) { - openModal("#rename", "input[name=name]"); - setTimeout(function() { - $("#rename input[name=name]").val(gClient.ppl[gClient.participantId].name); - $("#rename input[name=color]").val(gClient.ppl[gClient.participantId].color); - }, 100); - } else if(e.target.participantId) { - var id = e.target.participantId; - var part = gClient.ppl[id] || null; - if(part) { - participantMenu(part); - e.stopPropagation(); - } - } - } - }; - ele.addEventListener("mousedown", touchhandler); - ele.addEventListener("touchstart", touchhandler); - var releasehandler = function(e) { - $("#names .name").removeClass("play"); - }; - document.body.addEventListener("mouseup", releasehandler); - document.body.addEventListener("touchend", releasehandler); - - var removeParticipantMenus = function() { - $(".participant-menu").remove(); - $(".participantSpotlight").hide(); - document.removeEventListener("mousedown", removeParticipantMenus); - document.removeEventListener("touchstart", removeParticipantMenus); - }; - - var participantMenu = function(part) { - if(!part) return; - removeParticipantMenus(); - document.addEventListener("mousedown", removeParticipantMenus); - document.addEventListener("touchstart", removeParticipantMenus); - $("#" + part.id).find(".enemySpotlight").show(); - var menu = $(''); - $("body").append(menu); - // move menu to name position - var jq_nd = $(part.nameDiv); - var pos = jq_nd.position(); - menu.css({ - "top": pos.top + jq_nd.height() + 15, - "left": pos.left + 6, - "background": part.color || "black" - }); - menu.on("mousedown touchstart", function(evt) { - evt.stopPropagation(); - var target = $(evt.target); - if(target.hasClass("menu-item")) { - target.addClass("clicked"); - menu.fadeOut(200, function() { - removeParticipantMenus(); - }); - } - }); - // this spaces stuff out but also can be used for informational - $('').appendTo(menu).text(part._id); - // add menu items - if(gPianoMutes.indexOf(part._id) == -1) { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - gPianoMutes.push(part._id); - if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); - $(part.nameDiv).addClass("muted-notes"); - }); - } else { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - var i; - while((i = gPianoMutes.indexOf(part._id)) != -1) - gPianoMutes.splice(i, 1); - if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); - $(part.nameDiv).removeClass("muted-notes"); - }); - } - if(gChatMutes.indexOf(part._id) == -1) { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - gChatMutes.push(part._id); - if(localStorage) localStorage.chatMutes = gChatMutes.join(','); - $(part.nameDiv).addClass("muted-chat"); - }); - } else { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - var i; - while((i = gChatMutes.indexOf(part._id)) != -1) - gChatMutes.splice(i, 1); - if(localStorage) localStorage.chatMutes = gChatMutes.join(','); - $(part.nameDiv).removeClass("muted-chat"); - }); - } - if(!(gPianoMutes.indexOf(part._id) >= 0) || !(gChatMutes.indexOf(part._id) >= 0)) { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - gPianoMutes.push(part._id); - if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); - gChatMutes.push(part._id); - if(localStorage) localStorage.chatMutes = gChatMutes.join(','); - $(part.nameDiv).addClass("muted-notes"); - $(part.nameDiv).addClass("muted-chat"); - }); - } - if((gPianoMutes.indexOf(part._id) >= 0) || (gChatMutes.indexOf(part._id) >= 0)) { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - var i; - while((i = gPianoMutes.indexOf(part._id)) != -1) - gPianoMutes.splice(i, 1); - while((i = gChatMutes.indexOf(part._id)) != -1) - gChatMutes.splice(i, 1); - if(localStorage) localStorage.pianoMutes = gPianoMutes.join(','); - if(localStorage) localStorage.chatMutes = gChatMutes.join(','); - $(part.nameDiv).removeClass("muted-notes"); - $(part.nameDiv).removeClass("muted-chat"); - }); - } - if(gClient.isOwner()) { - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - if(confirm("Give room ownership to "+part.name+"?")) - gClient.sendArray([{m: "chown", id: part.id}]); - }); - $(' ').appendTo(menu) - .on("mousedown touchstart", function(evt) { - var minutes = prompt("How many minutes? (0-60)", "30"); - if(minutes === null) return; - minutes = parseFloat(minutes) || 0; - var ms = minutes * 60 * 1000; - gClient.sendArray([{m: "kickban", _id: part._id, ms: ms}]); - }); - } - menu.fadeIn(100); - }; - })(); - - - - - - - - - - - - - - - - -// Notification class - -//////////////////////////////////////////////////////////////// - - var Notification = function(par) { - if(this instanceof Notification === false) throw("yeet"); - EventEmitter.call(this); - - var par = par || {}; - - this.id = "Notification-" + (par.id || Math.random()); - this.title = par.title || ""; - this.text = par.text || ""; - this.html = par.html || ""; - this.target = $(par.target || "#piano"); - this.duration = par.duration || 30000; - this["class"] = par["class"] || "classic"; - - var self = this; - var eles = $("#" + this.id); - if(eles.length > 0) { - eles.remove(); - } - this.domElement = $('This room is set to "only the owner can play."
'; + } + if (msg.ch.settings["no cussing"]) { + has_notice = true; + notice += 'This room is set to "no cussing."
'; + } + let notice_div = $("#room-notice"); + if (has_notice) { + notice_div.html(notice); + if (notice_div.is(":hidden")) notice_div.fadeIn(1000); + } else { + if (notice_div.is(":visible")) notice_div.fadeOut(1000); + } + }); + gClient.on("disconnect", function () { + $("#room-notice").fadeOut(1000); + }); + + // Background color + (function () { + var old_color1 = new Color("#000000"); + var old_color2 = new Color("#000000"); + function setColor(hex, hex2) { + var color1 = new Color(hex); + var color2 = new Color(hex2 || hex); + if (!hex2) color2.add(-0x40, -0x40, -0x40); + + var bottom = document.getElementById("bottom"); + + var duration = 500; + var step = 0; + var steps = 30; + var step_ms = duration / steps; + var difference = new Color(color1.r, color1.g, color1.b); + difference.r -= old_color1.r; + difference.g -= old_color1.g; + difference.b -= old_color1.b; + var inc1 = new Color( + difference.r / steps, + difference.g / steps, + difference.b / steps + ); + difference = new Color(color2.r, color2.g, color2.b); + difference.r -= old_color2.r; + difference.g -= old_color2.g; + difference.b -= old_color2.b; + var inc2 = new Color( + difference.r / steps, + difference.g / steps, + difference.b / steps + ); + var iv; + iv = setInterval(function () { + old_color1.add(inc1.r, inc1.g, inc1.b); + old_color2.add(inc2.r, inc2.g, inc2.b); + document.body.style.background = + "radial-gradient(ellipse at center, " + + old_color1.toHexa() + + " 0%," + + old_color2.toHexa() + + " 100%)"; + bottom.style.background = old_color2.toHexa(); + if (++step >= steps) { + clearInterval(iv); + old_color1 = color1; + old_color2 = color2; + document.body.style.background = + "radial-gradient(ellipse at center, " + + color1.toHexa() + + " 0%," + + color2.toHexa() + + " 100%)"; + bottom.style.background = color2.toHexa(); + } + }, step_ms); + } + + function setColorToDefault() { + setColor("#000000", "#000000"); + } + + setColorToDefault(); + + gClient.on("ch", function (ch) { + if (ch.ch.settings) { + if (ch.ch.settings.color) { + setColor(ch.ch.settings.color, ch.ch.settings.color2); + } else { + setColorToDefault(); + } + } + }); + })(); + + var gPianoMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "") + .split(",") + .filter(v => v); + var gChatMutes = (localStorage.pianoMutes ? localStorage.pianoMutes : "") + .split(",") + .filter(v => v); + + var volume_slider = document.getElementById("volume-slider"); + volume_slider.value = gPiano.audio.volume; + $("#volume-label").text( + "Volume: " + Math.floor(gPiano.audio.volume * 100) + "%" + ); + volume_slider.addEventListener("input", function (evt) { + var v = +volume_slider.value; + gPiano.audio.setVolume(v); + if (window.localStorage) localStorage.volume = v; + $("#volume-label").text("Volume: " + Math.floor(v * 100) + "%"); + }); + + var Note = function (note, octave) { + this.note = note; + this.octave = octave || 0; + }; + + var n = function (a, b) { + return { note: new Note(a, b), held: false }; + }; + var key_binding = { + 65: n("gs"), + 90: n("a"), + 83: n("as"), + 88: n("b"), + 67: n("c", 1), + 70: n("cs", 1), + 86: n("d", 1), + 71: n("ds", 1), + 66: n("e", 1), + 78: n("f", 1), + 74: n("fs", 1), + 77: n("g", 1), + 75: n("gs", 1), + 188: n("a", 1), + 76: n("as", 1), + 190: n("b", 1), + 191: n("c", 2), + 222: n("cs", 2), + + 49: n("gs", 1), + 81: n("a", 1), + 50: n("as", 1), + 87: n("b", 1), + 69: n("c", 2), + 52: n("cs", 2), + 82: n("d", 2), + 53: n("ds", 2), + 84: n("e", 2), + 89: n("f", 2), + 55: n("fs", 2), + 85: n("g", 2), + 56: n("gs", 2), + 73: n("a", 2), + 57: n("as", 2), + 79: n("b", 2), + 80: n("c", 3), + 189: n("cs", 3), + 173: n("cs", 3), // firefox why + 219: n("d", 3), + 187: n("ds", 3), + 61: n("ds", 3), // firefox why + 221: n("e", 3) + }; + + var capsLockKey = false; + + var transpose_octave = 0; + + function handleKeyDown(evt) { + //console.log(evt); + var code = parseInt(evt.keyCode); + if (key_binding[code] !== undefined) { + var binding = key_binding[code]; + if (!binding.held) { + binding.held = true; + + var note = binding.note; + var octave = 1 + note.octave + transpose_octave; + if (evt.shiftKey) ++octave; + else if (capsLockKey || evt.ctrlKey) --octave; + note = note.note + octave; + var vol = velocityFromMouseY(); + press(note, vol); + } + + if (++gKeyboardSeq == 3) { + gKnowsYouCanUseKeyboard = true; + if (window.gKnowsYouCanUseKeyboardTimeout) + clearTimeout(gKnowsYouCanUseKeyboardTimeout); + if (localStorage) localStorage.knowsYouCanUseKeyboard = true; + if (window.gKnowsYouCanUseKeyboardNotification) + gKnowsYouCanUseKeyboardNotification.close(); + } + + evt.preventDefault(); + evt.stopPropagation(); + return false; + } else if (code == 20) { + // Caps Lock + capsLockKey = true; + evt.preventDefault(); + } else if (code === 0x20) { + // Space Bar + pressSustain(); + evt.preventDefault(); + } else if ((code === 38 || code === 39) && transpose_octave < 3) { + ++transpose_octave; + } else if ((code === 40 || code === 37) && transpose_octave > -2) { + --transpose_octave; + } else if (code == 9) { + // Tab (don't tab away from the piano) + evt.preventDefault(); + } else if (code == 8) { + // Backspace (don't navigate Back) + gAutoSustain = !gAutoSustain; + evt.preventDefault(); + } + } + + function handleKeyUp(evt) { + var code = parseInt(evt.keyCode); + if (key_binding[code] !== undefined) { + var binding = key_binding[code]; + if (binding.held) { + binding.held = false; + + var note = binding.note; + var octave = 1 + note.octave + transpose_octave; + if (evt.shiftKey) ++octave; + else if (capsLockKey || evt.ctrlKey) --octave; + note = note.note + octave; + release(note); + } + + evt.preventDefault(); + evt.stopPropagation(); + return false; + } else if (code == 20) { + // Caps Lock + capsLockKey = false; + evt.preventDefault(); + } else if (code === 0x20) { + // Space Bar + releaseSustain(); + evt.preventDefault(); + } + } + + function handleKeyPress(evt) { + evt.preventDefault(); + evt.stopPropagation(); + if (evt.keyCode == 27 || evt.keyCode == 13) { + //$("#chat input").focus(); + } + return false; + } + + var recapListener = function (evt) { + captureKeyboard(); + }; + + function captureKeyboard() { + $("#piano").off("mousedown", recapListener); + $("#piano").off("touchstart", recapListener); + $(document).on("keydown", handleKeyDown); + $(document).on("keyup", handleKeyUp); + $(window).on("keypress", handleKeyPress); + } + + function releaseKeyboard() { + $(document).off("keydown", handleKeyDown); + $(document).off("keyup", handleKeyUp); + $(window).off("keypress", handleKeyPress); + $("#piano").on("mousedown", recapListener); + $("#piano").on("touchstart", recapListener); + } + + captureKeyboard(); + + var velocityFromMouseY = function () { + return 0.1 + (my / 100) * 0.6; + }; + + // NoteQuota + var gNoteQuota = (function () { + var last_rat = 0; + var nqjq = $("#quota .value"); + setInterval(function () { + gNoteQuota.tick(); + }, 2000); + return new NoteQuota(function (points) { + // update UI + var rat = (points / this.max) * 100; + if (rat <= last_rat) + nqjq.stop(true, true).css("width", rat.toFixed(0) + "%"); + else + nqjq.stop(true, true).animate( + { width: rat.toFixed(0) + "%" }, + 2000, + "linear" + ); + last_rat = rat; + }); + })(); + gClient.on("nq", function (nq_params) { + gNoteQuota.setParams(nq_params); + }); + gClient.on("disconnect", function () { + gNoteQuota.setParams(NoteQuota.PARAMS_OFFLINE); + }); + + // click participant names + (function () { + var ele = document.getElementById("names"); + var touchhandler = function (e) { + var target_jq = $(e.target); + if (target_jq.hasClass("name")) { + target_jq.addClass("play"); + if (e.target.participantId == gClient.participantId) { + openModal("#rename", "input[name=name]"); + setTimeout(function () { + $("#rename input[name=name]").val( + gClient.ppl[gClient.participantId].name + ); + $("#rename input[name=color]").val( + gClient.ppl[gClient.participantId].color + ); + }, 100); + } else if (e.target.participantId) { + var id = e.target.participantId; + var part = gClient.ppl[id] || null; + if (part) { + participantMenu(part); + e.stopPropagation(); + } + } + } + }; + ele.addEventListener("mousedown", touchhandler); + ele.addEventListener("touchstart", touchhandler); + var releasehandler = function (e) { + $("#names .name").removeClass("play"); + }; + document.body.addEventListener("mouseup", releasehandler); + document.body.addEventListener("touchend", releasehandler); + + var removeParticipantMenus = function () { + $(".participant-menu").remove(); + $(".participantSpotlight").hide(); + document.removeEventListener("mousedown", removeParticipantMenus); + document.removeEventListener("touchstart", removeParticipantMenus); + }; + + var participantMenu = function (part) { + if (!part) return; + removeParticipantMenus(); + document.addEventListener("mousedown", removeParticipantMenus); + document.addEventListener("touchstart", removeParticipantMenus); + $("#" + part.id) + .find(".enemySpotlight") + .show(); + var menu = $(''); + $("body").append(menu); + // move menu to name position + var jq_nd = $(part.nameDiv); + var pos = jq_nd.position(); + menu.css({ + top: pos.top + jq_nd.height() + 15, + left: pos.left + 6, + background: part.color || "black" + }); + menu.on("mousedown touchstart", function (evt) { + evt.stopPropagation(); + var target = $(evt.target); + if (target.hasClass("menu-item")) { + target.addClass("clicked"); + menu.fadeOut(200, function () { + removeParticipantMenus(); + }); + } + }); + // this spaces stuff out but also can be used for informational + $('').appendTo(menu).text(part._id); + // add menu items + if (gPianoMutes.indexOf(part._id) == -1) { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + gPianoMutes.push(part._id); + if (localStorage) + localStorage.pianoMutes = gPianoMutes.join(","); + $(part.nameDiv).addClass("muted-notes"); + }); + } else { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + var i; + while ((i = gPianoMutes.indexOf(part._id)) != -1) + gPianoMutes.splice(i, 1); + if (localStorage) + localStorage.pianoMutes = gPianoMutes.join(","); + $(part.nameDiv).removeClass("muted-notes"); + }); + } + if (gChatMutes.indexOf(part._id) == -1) { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + gChatMutes.push(part._id); + if (localStorage) + localStorage.chatMutes = gChatMutes.join(","); + $(part.nameDiv).addClass("muted-chat"); + }); + } else { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + var i; + while ((i = gChatMutes.indexOf(part._id)) != -1) + gChatMutes.splice(i, 1); + if (localStorage) + localStorage.chatMutes = gChatMutes.join(","); + $(part.nameDiv).removeClass("muted-chat"); + }); + } + if ( + !(gPianoMutes.indexOf(part._id) >= 0) || + !(gChatMutes.indexOf(part._id) >= 0) + ) { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + gPianoMutes.push(part._id); + if (localStorage) + localStorage.pianoMutes = gPianoMutes.join(","); + gChatMutes.push(part._id); + if (localStorage) + localStorage.chatMutes = gChatMutes.join(","); + $(part.nameDiv).addClass("muted-notes"); + $(part.nameDiv).addClass("muted-chat"); + }); + } + if ( + gPianoMutes.indexOf(part._id) >= 0 || + gChatMutes.indexOf(part._id) >= 0 + ) { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + var i; + while ((i = gPianoMutes.indexOf(part._id)) != -1) + gPianoMutes.splice(i, 1); + while ((i = gChatMutes.indexOf(part._id)) != -1) + gChatMutes.splice(i, 1); + if (localStorage) + localStorage.pianoMutes = gPianoMutes.join(","); + if (localStorage) + localStorage.chatMutes = gChatMutes.join(","); + $(part.nameDiv).removeClass("muted-notes"); + $(part.nameDiv).removeClass("muted-chat"); + }); + } + if (gClient.isOwner()) { + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + if ( + confirm("Give room ownership to " + part.name + "?") + ) + gClient.sendArray([{ m: "chown", id: part.id }]); + }); + $(' ') + .appendTo(menu) + .on("mousedown touchstart", function (evt) { + var minutes = prompt("How many minutes? (0-60)", "30"); + if (minutes === null) return; + minutes = parseFloat(minutes) || 0; + var ms = minutes * 60 * 1000; + gClient.sendArray([ + { m: "kickban", _id: part._id, ms: ms } + ]); + }); + } + menu.fadeIn(100); + }; + })(); + + // Notification class + + //////////////////////////////////////////////////////////////// + + var Notification = function (par) { + if (this instanceof Notification === false) throw "yeet"; + EventEmitter.call(this); + + var par = par || {}; + + this.id = "Notification-" + (par.id || Math.random()); + this.title = par.title || ""; + this.text = par.text || ""; + this.html = par.html || ""; + this.target = $(par.target || "#piano"); + this.duration = par.duration || 30000; + this["class"] = par["class"] || "classic"; + + var self = this; + var eles = $("#" + this.id); + if (eles.length > 0) { + eles.remove(); + } + this.domElement = $( + '