返回首頁 

Greasy Fork is available in English.

WhatsApp online notifier

Notifies you when a user comes online. This script will only work if have a tab open and navigated to https://web.whatsapp.com/ and have selected a person. The next time this person comes online you should hear a bleeb. *Bleeb*!

  1. // ==UserScript==// @name WhatsApp online notifier// @namespace https://bennyjacobs.nl/userscripts/WhatsApp-Online-Notifier// @description Notifies you when a user comes online. This script will only work if have a tab open and navigated to https://web.whatsapp.com/ and have selected a person. The next time this person comes online you should hear a bleeb. *Bleeb*!// @include https://web.whatsapp.com/// @version 0.0.2// @grant none// ==/UserScript==//// The MIT License (MIT)//// Copyright (c) 2015 Loov//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in all// copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE// SOFTWARE.//(function(jsfx){'use strict';var chr = String.fromCharCode;var TAU = +Math.PI*2;var bitsPerSample = 16|0;var numChannels = 1|0;var sin = Math.sin;var pow = Math.pow;var abs = Math.abs;var EPSILON = 0.000001;jsfx.SampleRate = 0|0;jsfx.Sec = 0|0;jsfx.SetSampleRate = function(sampleRate){jsfx.SampleRate = sampleRate|0;jsfx.Sec = sampleRate|0;};jsfx.SetSampleRate(getDefaultSampleRate());// MAIN API// Creates a new Audio object based on the params// params can be a params generating function or the actual parametersjsfx.Sound = function(params){var processor = new Processor(params, jsfx.DefaultModules);var block = createFloatArray(processor.getSamplesLeft());processor.generate(block);return CreateAudio(block);};// Same as Sounds, but avoids locking the browser for too long// in case you have a large amount of sounds to generatejsfx.Sounds = function(library, ondone, onprogress){var audio = {};var player = {};player._audio = audio;var toLoad = [];// create playing functionsmap_object(library, function(_, name){player[name] = function(){if(typeof audio[name] !== "undefined"){audio[name].currentTime = 0.0;audio[name].play();}};toLoad.push(name);});var loaded = 0, total = toLoad.length;function next(){if(toLoad.length == 0){ondone && ondone(sounds);return;}var name = toLoad.shift();audio[name] = jsfx.Sound(library[name]);loaded++;onprogress && onprogress(name, loaded, total);window.setTimeout(next, 30);}next();return player;}// SoundsImmediate takes a named set of params, and generates multiple// sound objects at once.jsfx.SoundsImmediate = function(library){var audio = {};var player = {};player._audio = audio;map_object(library, function(params, name){audio[name] = jsfx.Sound(params);player[name] = function(){if(typeof audio[name] !== "undefined"){audio[name].currentTime = 0.0;audio[name].play();}};})return player;};if(typeof AudioContext !== "undefined"){// Node creates a new AudioContext ScriptProcessor that outputs the// sound. It will automatically disconnect, unless otherwise specified.jsfx.Node = function(audioContext, params, modules, bufferSize, stayConnected){var node = audioContext.createScriptProcessor(bufferSize, 0, 1);var gen = new Processor(params, modules || jsfx.DefaultModules);node.onaudioprocess = function(ev){var block = ev.outputBuffer.getChannelData(0);gen.generate(block);if(!stayConnected && gen.finished){// we need to do an async disconnect, otherwise Chrome may// glitchsetTimeout(function(){ node.disconnect(); }, 30);}}return node;}// Live creates an managed AudioContext for playing.// This is useful, when you want to use procedurally generated sounds.jsfx.Live = function(library, modules, BufferSize){//TODO: add limit for number of notes played at the same timeBufferSize = BufferSize || 2048;var player = {};var context = new AudioContext();var volume = context.createGain();volume.connect(context.destination);player._context = context;player._volume = volume;map_object(library, function(params, name){player[name] = function(){var node = jsfx.Node(context, params, modules, BufferSize);node.connect(volume);};});player._close = function(){context.close();};player._play = function(params){var node = jsfx.Node(context, params, modules, BufferSize);node.connect(volume);};return player;}} else {jsfx.Live = jsfx.Sounds;}// SOUND GENERATIONjsfx.Module = {};// generatorsjsfx.G = {};var stage = jsfx.stage = {PhaseSpeed : 0,PhaseSpeedMod : 10,Generator : 20,SampleMod : 30,Volume : 40};function byStage(a,b){ return a.stage - b.stage; }jsfx.InitDefaultParams = InitDefaultParams;function InitDefaultParams(params, modules){// setup modulesfor(var i = 0; i < modules.length; i += 1){var M = modules[i];var P = params[M.name] || {};// add missing parametersmap_object(M.params, function(def, name){if(typeof P[name] === 'undefined'){P[name] = def.D;}});params[M.name] = P;}}// Generates a stateful sound effect processor// params can be a function that creates a parameter setjsfx.Processor = Processor;function Processor(params, modules){params = params || {};modules = modules || jsfx.DefaultModules;if(typeof params === 'function'){params = params();} else {params = JSON.parse(JSON.stringify(params))}this.finished = false;this.state = {SampleRate: params.SampleRate || jsfx.SampleRate};// sort modulesmodules = modules.slice();modules.sort(byStage)this.modules = modules;// init missing paramsInitDefaultParams(params, modules);// setup modulesfor(var i = 0; i < this.modules.length; i += 1){var M = this.modules[i];this.modules[i].setup(this.state, params[M.name]);}}Processor.prototype = {//TODO: see whether this can be converted to a modulegenerate: function(block){for(var i = 0|0; i < block.length; i += 1){block[i] = 0;}if(this.finished){ return; }var $ = this.state,N = block.length|0;for(var i = 0; i < this.modules.length; i += 1){var M = this.modules[i];var n = M.process($, block.subarray(0,N))|0;N = Math.min(N, n);}if(N < block.length){this.finished = true;}for(var i = N; i < block.length; i++){block[i] = 0;}},getSamplesLeft: function(){var samples = 0;for(var i = 0; i < this.state.envelopes.length; i += 1){samples += this.state.envelopes[i].N;}if(samples === 0){samples = 3*this.state.SampleRate;}return samples;}};// Frequencyjsfx.Module.Frequency = {name: 'Frequency',params: {Start: { L:30, H:1800, D:440 },Min: { L:30, H:1800, D:30 },Max: { L:30, H:1800, D:1800 },Slide: { L:-1, H:1, D:0 },DeltaSlide: { L:-1, H:1, D:0 },RepeatSpeed: { L:0, H: 3.0, D: 0 },ChangeAmount: { L:-12, H:12, D:0 },ChangeSpeed : { L: 0, H:1, D:0 }},stage: stage.PhaseSpeed,setup: function($, P){var SR = $.SampleRate;$.phaseParams = P;$.phaseSpeed = P.Start * TAU / SR;$.phaseSpeedMax = P.Max * TAU / SR;$.phaseSpeedMin = P.Min * TAU / SR;$.phaseSpeedMin = Math.min($.phaseSpeedMin, $.phaseSpeed);$.phaseSpeedMax = Math.max($.phaseSpeedMax, $.phaseSpeed);$.phaseSlide = 1.0 + pow(P.Slide, 3.0) * 64.0 / SR;$.phaseDeltaSlide = pow(P.DeltaSlide, 3.0) / (SR * 1000);$.repeatTime = 0;$.repeatLimit = Infinity;if(P.RepeatSpeed > 0){$.repeatLimit = P.RepeatSpeed * SR;}$.arpeggiatorTime = 0;$.arpeggiatorLimit = P.ChangeSpeed * SR;if(P.ChangeAmount == 0){$.arpeggiatorLimit = Infinity;}$.arpeggiatorMod = 1 + P.ChangeAmount / 12.0;},process: function($, block){var speed = +$.phaseSpeed,min = +$.phaseSpeedMin,max = +$.phaseSpeedMax,slide = +$.phaseSlide,deltaSlide = +$.phaseDeltaSlide;var repeatTime = $.repeatTime,repeatLimit = $.repeatLimit;var arpTime = $.arpeggiatorTime,arpLimit = $.arpeggiatorLimit,arpMod = $.arpeggiatorMod;for(var i = 0; i < block.length; i++){slide += deltaSlide;speed *= slide;speed = speed < min ? min : speed > max ? max : speed;if(repeatTime > repeatLimit){this.setup($, $.phaseParams);return i + this.process($, block.subarray(i)) - 1;}repeatTime++;if(arpTime > arpLimit){speed *= arpMod;arpTime = 0;arpLimit = Infinity;}arpTime++;block[i] += speed;}$.repeatTime = repeatTime;$.arpeggiatorTime = arpTime;$.arpeggiatorLimit = arpLimit;$.phaseSpeed = speed;$.phaseSlide = slide;return block.length;}};// Vibratojsfx.Module.Vibrato = {name: 'Vibrato',params: {Depth: {L: 0, H:1, D:0},DepthSlide: {L:-1, H:1, D:0},Frequency: {L: 0.01, H:48, D:0},FrequencySlide: {L: -1.00, H: 1, D:0}},stage: stage.PhaseSpeedMod,setup: function($, P){var SR = $.SampleRate;$.vibratoPhase = 0;$.vibratoDepth = P.Depth;$.vibratoPhaseSpeed = P.Frequency * TAU / SR;$.vibratoPhaseSpeedSlide = 1.0 + pow(P.FrequencySlide, 3.0) * 3.0 / SR;$.vibratoDepthSlide = P.DepthSlide / SR;},process: function($, block){var phase = +$.vibratoPhase,depth = +$.vibratoDepth,speed = +$.vibratoPhaseSpeed,slide = +$.vibratoPhaseSpeedSlide,depthSlide = +$.vibratoDepthSlide;if((depth == 0) && (depthSlide <= 0)){return block.length;}for(var i = 0; i < block.length; i++){phase += speed;if(phase > TAU){phase -= TAU};block[i] += block[i] * sin(phase) * depth;speed *= slide;depth += depthSlide;depth = clamp1(depth);}$.vibratoPhase = phase;$.vibratoDepth = depth;$.vibratoPhaseSpeed = speed;return block.length;}};// Generatorjsfx.Module.Generator = {name: 'Generator',params: {// C = chooseFunc: {C: jsfx.G, D:'square'},A: {L: 0, H: 1, D: 0},B: {L: 0, H: 1, D: 0},ASlide: {L: -1, H: 1, D: 0},BSlide: {L: -1, H: 1, D: 0}},stage: stage.Generator,setup: function($, P){$.generatorPhase = 0;if(typeof P.Func === 'string'){$.generator = jsfx.G[P.Func];} else {$.generator = P.Func;}if(typeof $.generator === 'object'){$.generator = $.generator.create();}assert(typeof $.generator === 'function', 'generator must be a function')$.generatorA = P.A;$.generatorASlide = P.ASlide;$.generatorB = P.B;$.generatorBSlide = P.BSlide;},process: function($, block){return $.generator($, block);}};// Karplus Strong algorithm for string soundvar GuitarBufferSize = 1 << 16;jsfx.Module.Guitar = {name: 'Guitar',params: {A: {L:0.0, H:1.0, D: 1},B: {L:0.0, H:1.0, D: 1},C: {L:0.0, H:1.0, D: 1},},stage: stage.Generator,setup: function($, P){$.guitarA = P.A;$.guitarB = P.B;$.guitarC = P.C;$.guitarBuffer = createFloatArray(GuitarBufferSize);$.guitarHead = 0;var B = $.guitarBuffer;for(var i = 0; i < B.length; i++){B[i] = Math.random()*2 - 1;}},process: function($, block){var BS = GuitarBufferSize,BM = BS - 1;var A = +$.guitarA, B = +$.guitarB, C = +$.guitarC;var T = A + B + C;var h = $.guitarHead;var buffer = $.guitarBuffer;for(var i = 0; i < block.length; i++){// buffer sizevar n = (TAU / block[i])|0;n = n > BS ? BS : n;// tailvar t = ((h - n) + BS) & BM;buffer[h] =(buffer[(t-0+BS)&BM]*A +buffer[(t-1+BS)&BM]*B +buffer[(t-2+BS)&BM]*C) / T;block[i] = buffer[h];h = (h + 1) & BM;}$.guitarHead = h;return block.length;}}// Low/High-Pass Filterjsfx.Module.Filter = {name: 'Filter',params: {LP: {L: 0, H:1, D:1},LPSlide: {L:-1, H:1, D:0},LPResonance: {L: 0, H:1, D:0},HP: {L: 0, H:1, D:0},HPSlide: {L:-1, H:1, D:0}},stage: stage.SampleMod + 0,setup: function($, P){$.FilterEnabled = (P.HP > EPSILON) || (P.LP < 1 - EPSILON);$.LPEnabled = P.LP < 1 - EPSILON;$.LP = pow(P.LP, 3.0) / 10;$.LPSlide = 1.0 + P.LPSlide * 100 / $.SampleRate;$.LPPos = 0;$.LPPosSlide = 0;$.LPDamping = 5.0 / (1.0 + pow(P.LPResonance, 2) * 20) * (0.01 + P.LP);$.LPDamping = 1.0 - Math.min($.LPDamping, 0.8);$.HP = pow(P.HP, 2.0) / 10;$.HPPos = 0;$.HPSlide = 1.0 + P.HPSlide * 100 / $.SampleRate;},enabled: function($){return $.FilterEnabled;},process: function($, block){if(!this.enabled($)){ return block.length; }var lp = +$.LP;var lpPos = +$.LPPos;var lpPosSlide = +$.LPPosSlide;var lpSlide = +$.LPSlide;var lpDamping = +$.LPDamping;var lpEnabled = +$.LPEnabled;var hp = +$.HP;var hpPos = +$.HPPos;var hpSlide = +$.HPSlide;for(var i = 0; i < block.length; i++){if((hp > EPSILON) || (hp < -EPSILON)){hp *= hpSlide;hp = hp < EPSILON ? EPSILON: hp > 0.1 ? 0.1 : hp;}var lpPos_ = lpPos;lp *= lpSlide;lp = lp < 0 ? lp = 0 : lp > 0.1 ? 0.1 : lp;var sample = block[i];if(lpEnabled){lpPosSlide += (sample - lpPos) * lp;lpPosSlide *= lpDamping;} else {lpPos = sample;lpPosSlide = 0;}lpPos += lpPosSlide;hpPos += lpPos - lpPos_;hpPos *= 1.0 - hp;block[i] = hpPos;}$.LPPos = lpPos;$.LPPosSlide = lpPosSlide;$.LP = lp;$.HP = hp;$.HPPos = hpPos;return block.length;}};// Phaser Effectvar PhaserBufferSize = 1 << 10;jsfx.Module.Phaser = {name: 'Phaser',params: {Offset: {L:-1, H:1, D:0},Sweep: {L:-1, H:1, D:0}},stage: stage.SampleMod + 1,setup: function($, P){$.phaserBuffer = createFloatArray(PhaserBufferSize);$.phaserPos = 0;$.phaserOffset = pow(P.Offset, 2.0) * (PhaserBufferSize - 4);$.phaserOffsetSlide = pow(P.Sweep, 3.0) * 4000 / $.SampleRate;},enabled: function($){return (abs($.phaserOffsetSlide) > EPSILON) ||(abs($.phaserOffset) > EPSILON);},process: function($, block){if(!this.enabled($)){ return block.length; }var BS = PhaserBufferSize,BM = BS - 1;var buffer = $.phaserBuffer,pos = $.phaserPos|0,offset = +$.phaserOffset,offsetSlide = +$.phaserOffsetSlide;for(var i = 0; i < block.length; i++){offset += offsetSlide;//TODO: check whether this is correctif(offset < 0){offset = -offset;offsetSlide = -offsetSlide;}if(offset > BM){offset = BM;offsetSlide = 0;}buffer[pos] = block[i];var p = (pos - (offset|0) + BS) & BM;block[i] += buffer[p];pos = ((pos + 1) & BM)|0;}$.phaserPos = pos;$.phaserOffset = offset;return block.length;}};// Volume dynamic control with Attack-Sustain-Decay// ATTACK | 0 - Volume + Punch// SUSTAIN | Volume + Punch - Volume// DECAY | Volume - 0jsfx.Module.Volume = {name: 'Volume',params: {Master: { L: 0, H: 1, D: 0.5 },Attack: { L: 0.001, H: 1, D: 0.01 },Sustain: { L: 0, H: 2, D: 0.3 },Punch: { L: 0, H: 3, D: 1.0 },Decay: { L: 0.001, H: 2, D: 1.0 }},stage: stage.Volume,setup: function($, P){var SR = $.SampleRate;var V = P.Master;var VP = V * (1 + P.Punch);$.envelopes = [// S = start volume, E = end volume, N = duration in samples{S: 0, E: V, N: (P.Attack * SR)|0 }, // Attack{S:VP, E: V, N: (P.Sustain * SR)|0 }, // Sustain{S: V, E: 0, N: (P.Decay * SR)|0 } // Decay];// G = volume gradientfor(var i = 0; i < $.envelopes.length; i += 1){var e = $.envelopes[i];e.G = (e.E - e.S) / e.N;}},process: function($, block){var i = 0;while(($.envelopes.length > 0) && (i < block.length)){var E = $.envelopes[0];var vol = E.S,grad = E.G;var N = Math.min(block.length - i, E.N)|0;var end = (i+N)|0;for(; i < end; i += 1){block[i] *= vol;vol += grad;vol = clamp(vol, 0, 10);}E.S = vol;E.N -= N;if(E.N <= 0){$.envelopes.shift();}}return i;}};// PRESETSjsfx.DefaultModules = [jsfx.Module.Frequency,jsfx.Module.Vibrato,jsfx.Module.Generator,jsfx.Module.Filter,jsfx.Module.Phaser,jsfx.Module.Volume];jsfx.DefaultModules.sort(byStage);jsfx.EmptyParams = EmptyParams;function EmptyParams(){return map_object(jsfx.Module, function(){ return {} });}jsfx._RemoveEmptyParams = RemoveEmptyParams;function RemoveEmptyParams(params){for(var name in params){if(Object_keys(params[name]).length == 0){delete params[name];}}};jsfx.Preset = {Reset: function(){return EmptyParams();},Coin: function(){var p = EmptyParams();p.Frequency.Start = runif(880, 660);p.Volume.Sustain = runif(0.1);p.Volume.Decay = runif(0.4, 0.1);p.Volume.Punch = runif(0.3, 0.3);if(runif() < 0.5){p.Frequency.ChangeSpeed = runif(0.15, 0.1);p.Frequency.ChangeAmount = runif(8, 4);}RemoveEmptyParams(p);return p;},Laser: function(){var p = EmptyParams();p.Generator.Func = rchoose(['square', 'saw', 'sine']);if(runif() < 0.33){p.Frequency.Start = runif(880, 440);p.Frequency.Min = runif(0.1);p.Frequency.Slide = runif(0.3, -0.8);} else {p.Frequency.Start = runif(1200, 440);p.Frequency.Min = p.Frequency.Start - runif(880, 440);if(p.Frequency.Min < 110){ p.Frequency.Min = 110; }p.Frequency.Slide = runif(0.3, -1);}if(runif() < 0.5){p.Generator.A = runif(0.5);p.Generator.ASlide = runif(0.2);} else {p.Generator.A = runif(0.5, 0.4);p.Generator.ASlide = runif(0.7);}p.Volume.Sustain = runif(0.2, 0.1);p.Volume.Decay = runif(0.4);if(runif() < 0.5){p.Volume.Punch = runif(0.3);}if(runif() < 0.33){p.Phaser.Offset = runif(0.2);p.Phaser.Sweep = runif(0.2);}if(runif() < 0.5){p.Filter.HP = runif(0.3);}RemoveEmptyParams(p);return p;},Explosion: function(){var p = EmptyParams();p.Generator.Func = 'noise';if(runif() < 0.5){p.Frequency.Start = runif(440, 40);p.Frequency.Slide = runif(0.4, -0.1);} else {p.Frequency.Start = runif(1600, 220);p.Frequency.Slide = runif(-0.2, -0.2);}if(runif() < 0.2){ p.Frequency.Slide = 0; }if(runif() < 0.3){ p.Frequency.RepeatSpeed = runif(0.5, 0.3); }p.Volume.Sustain = runif(0.3, 0.1);p.Volume.Decay = runif(0.5);p.Volume.Punch = runif(0.6, 0.2);if(runif() < 0.5){p.Phaser.Offset = runif(0.9, -0.3);p.Phaser.Sweep = runif(-0.3);}if(runif() < 0.33){p.Frequency.ChangeSpeed = runif(0.3, 0.6);p.Frequency.ChangeAmount = runif(24, -12);}RemoveEmptyParams(p);return p;},Powerup: function(){var p = EmptyParams();if(runif() < 0.5){p.Generator.Func = 'saw';} else {p.Generator.A = runif(0.6);}p.Frequency.Start = runif(220, 440);if(runif() < 0.5){p.Frequency.Slide = runif(0.5, 0.2);p.Frequency.RepeatSpeed = runif(0.4, 0.4);} else {p.Frequency.Slide = runif(0.2, 0.05);if(runif() < 0.5){p.Vibrato.Depth = runif(0.6, 0.1);p.Vibrato.Frequency = runif(30, 10);}}p.Volume.Sustain = runif(0.4);p.Volume.Decay = runif(0.4, 0.1);RemoveEmptyParams(p);return p;},Hit: function(){var p = EmptyParams();p.Generator.Func = rchoose(['square', 'saw', 'noise']);p.Generator.A = runif(0.6);p.Generator.ASlide = runif(1, -0.5);p.Frequency.Start = runif(880, 220);p.Frequency.Slide = -runif(0.4, 0.3);p.Volume.Sustain = runif(0.1);p.Volume.Decay = runif(0.2, 0.1);if(runif() < 0.5){p.Filter.HP = runif(0.3);}RemoveEmptyParams(p);return p;},Jump: function(){var p = EmptyParams();p.Generator.Func = 'square';p.Generator.A = runif(0.6);p.Frequency.Start = runif(330, 330);p.Frequency.Slide = runif(0.4, 0.2);p.Volume.Sustain = runif(0.3, 0.1);p.Volume.Decay = runif(0.2, 0.1);if(runif() < 0.5){p.Filter.HP = runif(0.3);}if(runif() < 0.3){p.Filter.LP = runif(-0.6, 1);}RemoveEmptyParams(p);return p;},Select: function(){var p = EmptyParams();p.Generator.Func = rchoose(['square', 'saw']);p.Generator.A = runif(0.6);p.Frequency.Start = runif(660, 220);p.Volume.Sustain = runif(0.1, 0.1);p.Volume.Decay = runif(0.2);p.Filter.HP = 0.2;RemoveEmptyParams(p);return p;},Lucky: function(){var p = EmptyParams();map_object(p, function(out, moduleName){var defs = jsfx.Module[moduleName].params;map_object(defs, function(def, name){if(def.C){var values = Object_keys(def.C);out[name] = values[(values.length * Math.random()) | 0];} else {out[name] = Math.random() * (def.H - def.L) + def.L;}});});p.Volume.Master = 0.4;p.Filter = {}; // disable filter, as it usually will clip everythingRemoveEmptyParams(p);return p;}};// GENERATORS// uniform noisejsfx.G.unoise = newGenerator("sample = Math.random();");// sine wavejsfx.G.sine = newGenerator("sample = Math.sin(phase);");// saw wavejsfx.G.saw = newGenerator("sample = 2*(phase/TAU - ((phase/TAU + 0.5)|0));");// triangle wavejsfx.G.triangle = newGenerator("sample = Math.abs(4 * ((phase/TAU - 0.25)%1) - 2) - 1;");// square wavejsfx.G.square = newGenerator("var s = Math.sin(phase); sample = s > A ? 1.0 : s < A ? -1.0 : A;");// simple synthjsfx.G.synth = newGenerator("sample = Math.sin(phase) + .5*Math.sin(phase/2) + .3*Math.sin(phase/4);");// STATEFULvar __noiseLast = 0;jsfx.G.noise = newGenerator("if(phase % TAU < 4){__noiseLast = Math.random() * 2 - 1;} sample = __noiseLast;");// Karplus-Strong stringjsfx.G.string = {create: function(){var BS = 1 << 16;var BM = BS-1;var buffer = createFloatArray(BS);for(var i = 0; i < buffer.length; i++){buffer[i] = Math.random()*2-1;}var head = 0;return function($, block){var TAU = Math.PI * 2;var A = +$.generatorA, ASlide = +$.generatorASlide,B = +$.generatorB, BSlide = +$.generatorBSlide;var buf = buffer;for(var i = 0; i < block.length; i++){var phaseSpeed = block[i];var n = (TAU/phaseSpeed)|0;A += ASlide; B += BSlide;A = A < 0 ? 0 : A > 1 ? 1 : A;B = B < 0 ? 0 : B > 1 ? 1 : B;var t = ((head - n) + BS) & BM;var sample = (buf[(t-0+BS)&BM]*1 +buf[(t-1+BS)&BM]*A +buf[(t-2+BS)&BM]*B) / (1+A+B);buf[head] = sample;block[i] = buf[head];head = (head + 1) & BM;}$.generatorA = A;$.generatorB = B;return block.length;}}};// Generates samples using given frequency and generatorfunction newGenerator(line){return new Function("$", "block", "" +"var TAU = Math.PI * 2;\n" +"var sample;\n" +"var phase = +$.generatorPhase,\n"+" A = +$.generatorA, ASlide = +$.generatorASlide,\n"+" B = +$.generatorB, BSlide = +$.generatorBSlide;\n"+"\n"+"for(var i = 0; i < block.length; i++){\n"+" var phaseSpeed = block[i];\n"+" phase += phaseSpeed;\n"+" if(phase > TAU){ phase -= TAU };\n"+" A += ASlide; B += BSlide;\n"+" A = A < 0 ? 0 : A > 1 ? 1 : A;\n"+" B = B < 0 ? 0 : B > 1 ? 1 : B;\n"+line +" block[i] = sample;\n"+"}\n"+"\n"+"$.generatorPhase = phase;\n"+"$.generatorA = A;\n"+"$.generatorB = B;\n"+"return block.length;\n" +"");}// WAVE SUPPORT// Creates an Audio element from audio data [-1.0 .. 1.0]jsfx.CreateAudio = CreateAudio;function CreateAudio(data){if(typeof Float32Array !== "undefined"){assert(data instanceof Float32Array, 'data must be an Float32Array');}var blockAlign = numChannels * bitsPerSample >> 3;var byteRate = jsfx.SampleRate * blockAlign;var output = createByteArray(8 + 36 + data.length * 2);var p = 0;// emits string to outputfunction S(value){for(var i = 0; i < value.length; i += 1){output[p] = value.charCodeAt(i); p++;}}// emits integer value to outputfunction V(value, nBytes){if(nBytes <= 0){ return; }output[p] = value & 0xFF; p++;V(value >> 8, nBytes - 1);}S('RIFF'); V(36 + data.length * 2, 4);S('WAVEfmt '); V(16, 4); V(1, 2);V(numChannels, 2); V(jsfx.SampleRate, 4);V(byteRate, 4); V(blockAlign, 2); V(bitsPerSample, 2);S('data'); V(data.length * 2, 4);CopyFToU8(output.subarray(p), data);return new Audio('data:audio/wav;base64,' + U8ToB64(output));};jsfx.DownloadAsFile = function(audio){assert(audio instanceof Audio, 'input must be an Audio object');document.location.href = audio.src;};// HELPERSjsfx.Util = {};// Copies array of Floats to a Uint8Array with 16bits per samplejsfx.Util.CopyFToU8 = CopyFToU8;function CopyFToU8(into, floats){assert(into.length/2 == floats.length,'the target buffer must be twice as large as the iinput');var k = 0;for(var i = 0; i < floats.length; i++){var v = +floats[i];var a = (v * 0x7FFF)|0;a = a < -0x8000 ? -0x8000 : 0x7FFF < a ? 0x7FFF : a;a += a < 0 ? 0x10000 : 0;into[k] = a & 0xFF; k++;into[k] = a >> 8; k++;}}function U8ToB64(data){var CHUNK = 0x8000;var r###lt = '';for(var start = 0; start < data.length; start += CHUNK){var end = Math.min(start + CHUNK, data.length);r###lt += String.fromCharCode.apply(null, data.subarray(start, end));}return btoa(r###lt);}// uses AudioContext sampleRate or 44100;function getDefaultSampleRate(){if(typeof AudioContext !== 'undefined'){return (new AudioContext()).sampleRate;}return 44100;}// for checking pre/post conditionsfunction assert(condition, message){if(!condition){ throw new Error(message); }}function clamp(v, min, max){v = +v; min = +min; max = +max;if(v < min){ return +min; }if(v > max){ return +max; }return +v;}function clamp1(v){v = +v;if(v < +0.0){ return +0.0; }if(v > +1.0){ return +1.0; }return +v;}function map_object(obj, fn){var r = {};for(var name in obj){if(obj.hasOwnProperty(name)){r[name] = fn(obj[name], name);}}return r;}// uniform randomfunction runif(scale, offset){var a = Math.random();if(scale !== undefined)a *= scale;if(offset !== undefined)a += offset;return a;}function rchoose(gens){return gens[(gens.length*Math.random())|0];}function Object_keys(obj){var r = [];for(var name in obj){ r.push(name); }return r;}jsfx._createFloatArray = createFloatArray;function createFloatArray(N){if(typeof Float32Array === "undefined") {var r = new Array(N);for(var i = 0; i < r.length; i++){r[i] = 0.0;}}return new Float32Array(N);}function createByteArray(N){if(typeof Uint8Array === "undefined") {var r = new Array(N);for(var i = 0; i < r.length; i++){r[i] = 0|0;}}return new Uint8Array(N);}})(this.jsfx = {});var waitForElementId = function(id, cb) {// Travel the node(s) in a recursive fashion.var walk = function(node) {var child, next;if(node.id == id) {cb(node);}switch (node.nodeType) {case 1: // Elementcase 9: // Documentcase 11: // Document fragmentchild = node.firstChild;while (child) {next = child.nextSibling;walk(child);child = next;}break;case 3: // Text nodebreak;default:break;}};var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver);var observer = new MutationObserver(function (mutations) {mutations.forEach(function (mutation) {if(mutation.type == 'childList') {for (var i = 0; i < mutation.addedNodes.length; ++i) {walk(mutation.addedNodes[i]);}} else if (mutation.type == 'characterData') {walk(mutation.target);}});});observer.observe(document, {childList: true,characterData: true,subtree: true,});walk(document.body);}function notifyUser() {var library = {"static": {"Volume":{"Sustain":0.1,"Decay":0.15,"Punch":0.55}},"dynamic": function(){return {"Frequency": { "Start": Math.random()*440 + 220 }};},"coin": jsfx.Preset.Coin};var sfx = jsfx.Live(library);// Let's check if the browser supports notificationsif (!("Notification" in window)) {alert("This browser does not support desktop notification");}// Let's check whether notification permissions have already been grantedelse if (Notification.permission === "granted") {// If it's okay let's create a notificationvar notification = new Notification("User is online");sfx.coin();}// Otherwise, we need to ask the user for permissionelse if (Notification.permission !== 'denied') {Notification.requestPermission(function (permission) {// If the user accepts, let's create a notificationif (permission === "granted") {var notification = new Notification("User is online");sfx.coin();}});}// At last, if the user has denied notifications, and you// want to be respectful there is no need to bother them any more.}waitForElementId("main", function(node) {console.log("Main panel loaded.")var handleTextNode = function(textNode) {if(textNode.textContent == 'online'){console.log("User is online");notifyUser();}};var walk = function(node) {var child, next;switch (node.nodeType) {case 1: // Elementcase 9: // Documentcase 11: // Document fragmentchild = node.firstChild;while (child) {next = child.nextSibling;walk(child);child = next;}break;case 3: // Text nodehandleTextNode(node);break;default:break;}};var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver);var observer = new MutationObserver(function (mutations) {mutations.forEach(function (mutation) {if(mutation.type == 'childList') {for (var i = 0; i < mutation.addedNodes.length; ++i) {walk(mutation.addedNodes[i]);}} else if (mutation.type == 'characterData') {handleTextNode(mutation.target);}});});observer.observe(node, {childList: true,characterData: true,subtree: true,});walk(node);})