├── .gitattributes ├── .gitignore ├── docs ├── screenshot.gif ├── newton │ └── index.html ├── wrecking │ └── index.html └── index.html ├── tsconfig.json ├── web_typings ├── gcc │ └── gcc.d.ts ├── sss │ └── sss.d.ts ├── ppe │ └── ppe.d.ts └── pag │ └── pag.d.ts ├── package.json ├── webpack.config.js ├── src ├── newton │ └── index.ts ├── wrecking │ └── index.ts └── lark-matter │ └── index.ts ├── LICENSE.txt ├── README.md └── web_modules ├── pag └── index.js ├── ppe └── index.js ├── gcc └── index.js └── sss └── index.js /.gitattributes: -------------------------------------------------------------------------------- 1 | web_modules/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /docs/screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/lark-matter/HEAD/docs/screenshot.gif -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs" 5 | }, 6 | "exclude": [ 7 | "node_modules", 8 | "web_modules" 9 | ] 10 | } -------------------------------------------------------------------------------- /docs/newton/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lark-matter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/wrecking/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lark-matter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /web_typings/gcc/gcc.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'gcc' { 2 | function capture(element: any); 3 | function captureSvg(svgElm: any); 4 | function end(): HTMLImageElement; 5 | function setOptions(_options: Options); 6 | 7 | export let options: Options; 8 | 9 | interface Options { 10 | scale?: number; 11 | durationSec?: number; 12 | keyCode?: number; 13 | capturingFps?: number; 14 | appFps?: number; 15 | isAppendingImgElement?: boolean; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lark-matter", 3 | "version": "1.0.0", 4 | "description": "Pixel art style matter.js renderer plugin", 5 | "author": "ABA", 6 | "license": "MIT", 7 | "scripts": { 8 | "wds": "webpack-dev-server --inline", 9 | "build": "webpack", 10 | "watch": "webpack --watch" 11 | }, 12 | "devDependencies": { 13 | "@types/matter-js": "^0.9.32", 14 | "awesome-typescript-loader": "^3.0.4-rc.0", 15 | "typescript": "^2.2.1", 16 | "webpack": "^2.2.1", 17 | "webpack-dev-server": "^2.3.0" 18 | }, 19 | "dependencies": {} 20 | } -------------------------------------------------------------------------------- /web_typings/sss/sss.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'sss' { 2 | function init(_seed?: number, tempo?: number, fps?: number); 3 | function setSeed(_seed?: number); 4 | function play(name?: string, mult?: number, params?: any, volume?: number); 5 | function setVolume(volume: number); 6 | function setQuantize(_quantize: number); 7 | function playBgm 8 | (name?: string, interval?: number, params?: any, tracksNum?: number, volume?: number); 9 | function stopBgm(); 10 | function update(): number; 11 | function reset(); 12 | function playEmpty(); 13 | function playParam(param: any); 14 | const Preset; 15 | let playInterval: number 16 | } 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: { 3 | LarkMatter: ['./src/lark-matter/index.ts'], 4 | wrecking: ['./src/wrecking/index.ts', './src/lark-matter/index.ts'], 5 | newton: ['./src/newton/index.ts', './src/lark-matter/index.ts'] 6 | }, 7 | output: { 8 | path: __dirname + '/docs', 9 | filename: '[name]/index.js', 10 | library: ['[name]'], 11 | libraryTarget: 'umd' 12 | }, 13 | resolve: { 14 | extensions: ['.ts', '.js'], 15 | modules: ['node_modules', 'web_modules'] 16 | }, 17 | //devtool: 'source-map', 18 | devServer: { 19 | contentBase: 'docs' 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.ts$/, 25 | exclude: /(node_modules|web_modules)/, 26 | loader: 'awesome-typescript-loader' 27 | } 28 | ] 29 | }, 30 | externals: { 31 | "matter-js": "Matter" 32 | } 33 | }; -------------------------------------------------------------------------------- /src/newton/index.ts: -------------------------------------------------------------------------------- 1 | import * as Matter from 'matter-js'; 2 | import * as LarkMatter from '../lark-matter/index'; 3 | 4 | window.onload = () => { 5 | (Matter).use('lark-matter'); 6 | init(); 7 | } 8 | 9 | function init() { 10 | var engine = Matter.Engine.create(), 11 | world = engine.world; 12 | var render = Matter.Render.create({ 13 | element: document.body, 14 | engine: engine, 15 | options: { 16 | width: Math.min(document.documentElement.clientWidth, 800), 17 | height: Math.min(document.documentElement.clientHeight, 600), 18 | } 19 | }); 20 | Matter.Render.run(render); 21 | var runner = (Matter.Runner).create(); 22 | Matter.Runner.run(runner, engine); 23 | var cradle = (Matter.Composites).newtonsCradle(280, 100, 5, 30, 200); 24 | Matter.World.add(world, cradle); 25 | Matter.Body.translate(cradle.bodies[0], { x: -180, y: -100 }); 26 | cradle = (Matter.Composites).newtonsCradle(280, 380, 7, 20, 140); 27 | Matter.World.add(world, cradle); 28 | Matter.Body.translate(cradle.bodies[0], { x: -140, y: -100 }); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ABA Games 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /web_typings/ppe/ppe.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ppe' { 2 | function emit(patternName: string, 3 | x: number, y: number, angle?: number, 4 | emitOptions?: EmitOptions, 5 | pool?: ParticlePool); 6 | function update(); 7 | function getParticles(); 8 | function setSeed(seed?: number); 9 | function reset(); 10 | function clear(); 11 | function clearPools(); 12 | function setOptions(_options: Options); 13 | 14 | let options: Options; 15 | 16 | interface Particle { 17 | pos: Vector; 18 | size: number; 19 | color: Color; 20 | } 21 | 22 | class ParticlePool { 23 | constructor(canvas?: HTMLCanvasElement); 24 | getParticles(): Particle[]; 25 | clear(): void; 26 | } 27 | 28 | interface Vector { 29 | x: number; 30 | y: number; 31 | } 32 | 33 | interface Color { 34 | r: number; 35 | g: number; 36 | b: number; 37 | getStyle(): string; 38 | } 39 | 40 | interface Options { 41 | scaleRatio?: number; 42 | canvas?: HTMLCanvasElement; 43 | isLimitingColors?: boolean; 44 | } 45 | 46 | interface EmitOptions { 47 | sizeScale?: number; 48 | countScale?: number; 49 | hue?: number; 50 | velX?: number; 51 | velY?: number; 52 | speed?: number; 53 | slowdownRatio?: number; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /web_typings/pag/pag.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'pag' { 2 | function generate(pattern: string[], options?: PagOptions): Pixel[][][]; 3 | function generateImages 4 | (pattern: string[], options?: PagOptions): HTMLImageElement[]; 5 | function setSeed(seed?: number); 6 | function setDefaultOptions(_defaultOptions: PagOptions); 7 | function draw(context: CanvasRenderingContext2D, pixels: Pixel[][][], 8 | x: number, y: number, rotationIndex?: number); 9 | function drawImage(context: CanvasRenderingContext2D, images: HTMLImageElement[], 10 | x: number, y: number, rotationIndex?: number); 11 | 12 | const defaultOptions: PagOptions; 13 | 14 | interface Pixel { 15 | r: number; 16 | g: number; 17 | b: number; 18 | isEmpty: boolean; 19 | style: string; 20 | } 21 | 22 | interface PagOptions { 23 | isMirrorX?: boolean; 24 | isMirrorY?: boolean; 25 | seed?: number; 26 | hue?: number; 27 | saturation?: number, 28 | value?: number, 29 | rotationNum?: number, 30 | scale?: number, 31 | scaleX?: number, 32 | scaleY?: number, 33 | colorNoise?: number, 34 | colorLighting?: number, 35 | edgeDarkness?: number, 36 | isShowingEdge?: boolean, 37 | isShowingBody?: boolean, 38 | isLimitingColors?: boolean 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lark-matter 2 | ====================== 3 | Pixel art style [matter.js](http://brm.io/matter-js/) renderer plugin. 4 | 5 | ![screenshot](https://abagames.github.io/lark-matter/screenshot.gif) 6 | 7 | ### Demos 8 | 9 | [wrecking](https://abagames.github.io/lark-matter/wrecking/index.html) 10 | 11 | [newton](https://abagames.github.io/lark-matter/newton/index.html) 12 | 13 | ### Usage 14 | 15 | See [the sample code](https://github.com/abagames/lark-matter/blob/master/docs/index.html) of [the car demo](https://abagames.github.io/lark-matter/index.html). 16 | 17 | ```javascript 18 | Matter.use('lark-matter'); 19 | // Pixel size of a dot 20 | LarkMatter.options.dotSize = 5; 21 | // Number of pixel art rotation patterns 22 | LarkMatter.options.rotationNum = 16; 23 | // Enable sound effects 24 | LarkMatter.options.enableSes = false; 25 | // Enable a bgm 26 | LarkMatter.options.enableBgm = false; 27 | // Set a random seed for generating pixels, particles and audios 28 | // (null: Set a seed randomly each time) 29 | LarkMatter.options.seed = null; 30 | // OnRender callback function 31 | LarkMatter.options.onRender = null; 32 | ``` 33 | 34 | ### Libraries 35 | 36 | [pixel-art-gen](https://github.com/abagames/pixel-art-gen) / 37 | [particle-pattern-emitter](https://github.com/abagames/particle-pattern-emitter) / 38 | [sounds-some-sounds](https://github.com/abagames/sounds-some-sounds) / 39 | [gif-capture-canvas](https://github.com/abagames/gif-capture-canvas) 40 | -------------------------------------------------------------------------------- /src/wrecking/index.ts: -------------------------------------------------------------------------------- 1 | import * as Matter from 'matter-js'; 2 | import * as LarkMatter from '../lark-matter/index'; 3 | //import * as gcc from 'gcc'; 4 | 5 | window.onload = () => { 6 | LarkMatter.options.enableBgm = true; 7 | LarkMatter.options.enableSes = true; 8 | /*lm.LarkMatter.options.onRender = () => { 9 | gcc.capture(lm.canvas); 10 | }; 11 | gcc.setOptions({ 12 | scale: 1 13 | });*/ 14 | (Matter).use('lark-matter'); 15 | init(); 16 | } 17 | 18 | function init() { 19 | var engine = Matter.Engine.create(), 20 | world = engine.world; 21 | var render = Matter.Render.create({ 22 | element: document.body, 23 | engine: engine, 24 | options: { 25 | width: Math.min(document.documentElement.clientWidth, 800), 26 | height: Math.min(document.documentElement.clientHeight, 600), 27 | } 28 | }); 29 | Matter.Render.run(render); 30 | var runner = (Matter.Runner).create(); 31 | Matter.Runner.run(runner, engine); 32 | var rows = 10, 33 | yy = 600 - 21 - 40 * rows; 34 | var stack = Matter.Composites.stack(400, yy, 5, rows, 0, 0, function (x, y) { 35 | return Matter.Bodies.rectangle(x, y, 40, 40); 36 | }); 37 | (Matter.World.add)(world, [ 38 | stack, 39 | Matter.Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), 40 | Matter.Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), 41 | Matter.Bodies.rectangle(800, 300, 50, 600, { isStatic: true }), 42 | Matter.Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) 43 | ]); 44 | var ball = Matter.Bodies.circle(100, 400, 50, { density: 0.04, frictionAir: 0.005 }); 45 | Matter.World.add(world, ball); 46 | Matter.World.add(world, Matter.Constraint.create({ 47 | pointA: { x: 300, y: 100 }, 48 | bodyB: ball 49 | })); 50 | } 51 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | lark-matter 7 | 8 | 9 | 10 | 11 | 12 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/lark-matter/index.ts: -------------------------------------------------------------------------------- 1 | import * as Matter from 'matter-js'; 2 | import * as pag from 'pag'; 3 | import * as ppe from 'ppe'; 4 | import * as sss from 'sss'; 5 | 6 | let matter: any; 7 | 8 | export const LarkMatter = { 9 | name: 'lark-matter', 10 | version: '0.1.0', 11 | for: 'matter-js@^0.12.0', 12 | options: { 13 | dotSize: 5, 14 | rotationNum: 16, 15 | enableSes: false, 16 | enableBgm: false, 17 | seed: null, 18 | onRender: null 19 | }, 20 | install: function (base) { 21 | matter = base; 22 | init(); 23 | } 24 | }; 25 | export const options = LarkMatter.options; 26 | 27 | (Matter).Plugin.register(LarkMatter); 28 | 29 | export let canvas: HTMLCanvasElement; 30 | export let context: CanvasRenderingContext2D; 31 | 32 | function init() { 33 | matter.after('Render.create', initRender); 34 | matter.after('Render.run', runRender); 35 | matter.after('Engine.create', initEngine); 36 | matter.after('Body.create', createBody); 37 | } 38 | 39 | function initRender() { 40 | const render: Matter.Render = this; 41 | render.element.removeChild(render.canvas); 42 | canvas = document.createElement('canvas'); 43 | canvas.width = render.options.width / LarkMatter.options.dotSize; 44 | canvas.height = render.options.height / LarkMatter.options.dotSize; 45 | canvas.style.cssText = ` 46 | width: ${render.options.width}px; 47 | height: ${render.options.height}px; 48 | image-rendering: -moz-crisp-edges; 49 | image-rendering: -webkit-optimize-contrast; 50 | image-rendering: -o-crisp-edges; 51 | image-rendering: pixelated; 52 | background: white; 53 | `; 54 | context = canvas.getContext('2d'); 55 | document.body.appendChild(canvas); 56 | pag.setDefaultOptions({ 57 | isLimitingColors: true, 58 | rotationNum: LarkMatter.options.rotationNum, 59 | colorLighting: 0.5, 60 | colorNoise: 0.01 61 | }); 62 | ppe.setOptions({ 63 | canvas: canvas, 64 | isLimitingColors: true 65 | }); 66 | const seed = LarkMatter.options.seed != null ? 67 | LarkMatter.options.seed : Math.random() * 0x7fffffff; 68 | pag.setSeed(seed); 69 | ppe.setSeed(seed); 70 | if (LarkMatter.options.enableBgm || LarkMatter.options.enableSes) { 71 | sss.init(); 72 | sss.setVolume(0.2) 73 | sss.setQuantize(0.25); 74 | sss.setSeed(seed); 75 | if (LarkMatter.options.enableBgm) { 76 | sss.playBgm('0', 0.25, [sss.Preset.Laser, sss.Preset.Hit], 8, 0.3); 77 | } 78 | } 79 | } 80 | 81 | function createBody() { 82 | const body: Matter.Body = this; 83 | let bMinX = (body.bounds).min.x; 84 | let bMinY = (body.bounds).min.y; 85 | let bMaxX = (body.bounds).max.x; 86 | let bMaxY = (body.bounds).max.y; 87 | let w = Math.max(body.position.x - bMinX, bMaxX - body.position.x); 88 | let h = Math.max(body.position.y - bMinY, bMaxY - body.position.y); 89 | bMinX = body.position.x - w; 90 | bMaxX = body.position.x + w; 91 | bMinY = body.position.y - h; 92 | bMaxY = body.position.y + h; 93 | const tw = Math.ceil((bMaxX - bMinX) / LarkMatter.options.dotSize) + 1; 94 | const th = Math.ceil((bMaxY - bMinY) / LarkMatter.options.dotSize) + 1; 95 | let lines: { min: number, max: number }[] = nArray(th, null); 96 | const patterns: string[][] = timesArray(th, () => timesArray(tw, () => ' ')); 97 | for (let k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) { 98 | const verticies = body.parts[k].vertices; 99 | let fv: Matter.Vector; 100 | let pv: Matter.Vector; 101 | verticies.forEach((vert: any) => { 102 | const v = matter.Vector.create( 103 | (vert.x - bMinX) / LarkMatter.options.dotSize, 104 | (vert.y - bMinY) / LarkMatter.options.dotSize); 105 | if (pv != null) { 106 | drawLine(lines, tw, th, pv, v); 107 | } else { 108 | fv = v; 109 | } 110 | pv = v; 111 | }); 112 | if (pv != null && fv != null) { 113 | drawLine(lines, tw, th, pv, fv); 114 | } 115 | } 116 | fillLines(patterns, lines); 117 | let px = 0; 118 | let py = 0; 119 | let pvx = 0; 120 | let pvy = 0; 121 | let pw = tw; 122 | let ph = th; 123 | let pc: number; 124 | if (tw > th) { 125 | pc = Math.floor(tw / th / 2); 126 | if (pc < 1) { 127 | pc = 1; 128 | } 129 | pw = Math.ceil(tw / pc); 130 | pvx = tw / pc; 131 | } else { 132 | pc = Math.floor(th / tw / 2); 133 | if (pc < 1) { 134 | pc = 1; 135 | } 136 | ph = Math.ceil(th / pc); 137 | pvy = th / pc; 138 | } 139 | (body).pixels = timesArray(pc, () => { 140 | const pixel = { 141 | pattern: pag.generate 142 | (getPatternStrings(patterns, Math.floor(px), Math.floor(py), pw, ph)), 143 | x: px + pw / 2 - tw / 2, 144 | y: py + ph / 2 - th / 2 145 | }; 146 | px += pvx; 147 | py += pvy; 148 | return pixel; 149 | }); 150 | (body).ppeTypeId = `m_${tw}_${th}`; 151 | const seTypes = ['h', 'l', 's']; 152 | (body).sssTypeId = `${seTypes[(tw + th) % 3]}_${tw}_${th}`; 153 | } 154 | 155 | function getPatternStrings 156 | (patterns: string[][], px: number, py: number, w: number, h: number) { 157 | let strs = []; 158 | for (let y = py; y < py + h; y++) { 159 | let str = ''; 160 | for (let x = px; x < px + w; x++) { 161 | str += patterns[y][x]; 162 | } 163 | strs.push(str); 164 | } 165 | return strs; 166 | } 167 | 168 | function drawLine 169 | (lines: { min: number, max: number }[], pw: number, ph: number, 170 | p1: Matter.Vector, p2: Matter.Vector) { 171 | let vx: number; 172 | let vy: number; 173 | let c: number; 174 | if (Math.abs(p1.x - p2.x) < Math.abs(p1.y - p2.y)) { 175 | c = Math.abs(p2.y - p1.y) + 1; 176 | vx = (p2.x - p1.x) / c; 177 | vy = p1.y > p2.y ? -1 : 1; 178 | } else { 179 | c = Math.abs(p2.x - p1.x) + 1; 180 | vy = (p2.y - p1.y) / c; 181 | vx = p1.x > p2.x ? -1 : 1; 182 | } 183 | let px = p1.x; 184 | let py = p1.y; 185 | times(c, () => { 186 | const fpy = Math.round(py) 187 | if (px >= 0 && px < pw && fpy >= 0 && fpy < ph) { 188 | let l = lines[fpy]; 189 | if (l == null) { 190 | lines[fpy] = { min: px, max: px }; 191 | } else { 192 | if (px < l.min) { 193 | lines[fpy].min = px; 194 | } 195 | if (px > l.max) { 196 | lines[fpy].max = px; 197 | } 198 | } 199 | } 200 | px += vx; 201 | py += vy; 202 | }); 203 | } 204 | 205 | function fillLines(patterns: string[][], lines: { min: number, max: number }[]) { 206 | lines.forEach((l, y) => { 207 | if (l == null) { 208 | return; 209 | } 210 | for (let x = Math.floor(l.min); x <= Math.floor(l.max); x++) { 211 | patterns[y][x] = '*'; 212 | } 213 | }); 214 | } 215 | 216 | function runRender(render: Matter.Render) { 217 | matter.Render.stop(render); 218 | renderLm(render); 219 | } 220 | 221 | function renderLm(render: Matter.Render) { 222 | requestAnimationFrame(() => { renderLm(render); }); 223 | context.fillStyle = '#fff'; 224 | context.fillRect(0, 0, canvas.width, canvas.height); 225 | ppe.update(); 226 | if (LarkMatter.options.enableBgm || LarkMatter.options.enableSes) { 227 | sss.update(); 228 | } 229 | const bodies = matter.Composite.allBodies((render).engine.world); 230 | bodies.forEach(body => { 231 | if (!body.render.visible) { 232 | return; 233 | } 234 | const angle = body.angle; 235 | const ri = wrap( 236 | Math.round(angle * LarkMatter.options.rotationNum / (Math.PI * 2)), 237 | 0, LarkMatter.options.rotationNum); 238 | const x = body.position.x / LarkMatter.options.dotSize; 239 | const y = body.position.y / LarkMatter.options.dotSize; 240 | let o = matter.Vector.create(); 241 | (body).pixels.forEach(p => { 242 | o.x = p.x; 243 | o.y = p.y; 244 | o = matter.Vector.rotate(o, angle); 245 | pag.draw(context, p.pattern, x + o.x, y + o.y, ri); 246 | }); 247 | }); 248 | if (LarkMatter.options.onRender != null) { 249 | LarkMatter.options.onRender(); 250 | } 251 | } 252 | 253 | function initEngine() { 254 | const engine: Matter.Engine = this; 255 | matter.Events.on(engine, 'collisionStart', e => { 256 | e.pairs.forEach(p => { 257 | p.activeContacts.forEach(ac => { 258 | const b = ac.vertex.body; 259 | const v = b.velocity; 260 | let ratio = (p).collision.depth * matter.Vector.magnitude(v) * 0.1; 261 | if (ratio > 2) { 262 | ratio = 2; 263 | } 264 | if (ratio > 0.3) { 265 | ppe.emit(b.ppeTypeId, 266 | ac.vertex.x / LarkMatter.options.dotSize, 267 | ac.vertex.y / LarkMatter.options.dotSize, 268 | Math.atan2(-v.y, -v.x), 269 | { countScale: ratio, speed: 0.7 * ratio }); 270 | if (LarkMatter.options.enableSes) { 271 | sss.play(b.sssTypeId, 2, null, ratio > 1 ? 1 : ratio); 272 | } 273 | } 274 | }); 275 | }); 276 | }); 277 | } 278 | 279 | function wrap(v: number, low: number, high: number) { 280 | const w = high - low; 281 | const o = v - low; 282 | if (o >= 0) { 283 | return o % w + low; 284 | } else { 285 | let v = w + o % w + low; 286 | if (v >= high) { 287 | v -= w; 288 | } 289 | return v; 290 | } 291 | } 292 | 293 | function nArray(n: number, v: any) { 294 | let result = []; 295 | for (let i = 0; i < n; i++) { 296 | result.push(v); 297 | } 298 | return result; 299 | } 300 | 301 | function times(n: number, func: Function) { 302 | for (let i = 0; i < n; i++) { 303 | func(); 304 | } 305 | } 306 | 307 | function timesArray(n: number, func: Function) { 308 | let result = []; 309 | for (let i = 0; i < n; i++) { 310 | result.push(func()); 311 | } 312 | return result; 313 | } 314 | -------------------------------------------------------------------------------- /web_modules/pag/index.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["pag"] = factory(); 8 | else 9 | root["pag"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | module.exports = __webpack_require__(1); 58 | 59 | 60 | /***/ }, 61 | /* 1 */ 62 | /***/ function(module, exports) { 63 | 64 | "use strict"; 65 | exports.defaultOptions = { 66 | isMirrorX: false, 67 | isMirrorY: false, 68 | seed: 0, 69 | hue: null, 70 | saturation: 0.8, 71 | value: 1, 72 | rotationNum: 1, 73 | scale: 1, 74 | scaleX: null, 75 | scaleY: null, 76 | colorNoise: 0.1, 77 | colorLighting: 1, 78 | edgeDarkness: 0.4, 79 | isShowingEdge: true, 80 | isShowingBody: true, 81 | isLimitingColors: false, 82 | }; 83 | var generatedPixels = {}; 84 | var seed = 0; 85 | function generate(patterns, _options) { 86 | if (_options === void 0) { _options = {}; } 87 | _options.baseSeed = seed; 88 | var jso = JSON.stringify({ patterns: patterns, options: _options }); 89 | if (generatedPixels[jso]) { 90 | return generatedPixels[jso]; 91 | } 92 | var options = {}; 93 | forOwn(exports.defaultOptions, function (v, k) { 94 | options[k] = v; 95 | }); 96 | forOwn(_options, function (v, k) { 97 | options[k] = v; 98 | }); 99 | var random = new Random(); 100 | var rndSeed = seed + getHashFromString(patterns.join()); 101 | if (options.seed != null) { 102 | rndSeed += options.seed; 103 | } 104 | random.setSeed(rndSeed); 105 | if (options.hue == null) { 106 | options.hue = random.get01(); 107 | } 108 | if (options.scaleX == null) { 109 | options.scaleX = options.scale; 110 | } 111 | if (options.scaleY == null) { 112 | options.scaleY = options.scale; 113 | } 114 | var pixels = generatePixels(patterns, options, random); 115 | var result; 116 | if (options.rotationNum > 1) { 117 | result = map(createRotated(pixels, options.rotationNum), function (p) { 118 | return createColored(p, options); 119 | }); 120 | } 121 | else { 122 | result = [createColored(pixels, options)]; 123 | } 124 | generatedPixels[jso] = result; 125 | return result; 126 | } 127 | exports.generate = generate; 128 | function generateImages(patterns, _options) { 129 | if (_options === void 0) { _options = {}; } 130 | var pixels = generate(patterns, _options); 131 | var width = pixels[0].length; 132 | var height = pixels[0][0].length; 133 | var canvas = document.createElement('canvas'); 134 | canvas.width = width; 135 | canvas.height = height; 136 | var context = canvas.getContext('2d'); 137 | var images = []; 138 | for (var i = 0; i < pixels.length; i++) { 139 | context.clearRect(0, 0, width, height); 140 | draw(context, pixels, width / 2, height / 2, i); 141 | var image = new Image(); 142 | image.src = canvas.toDataURL(); 143 | images.push(image); 144 | } 145 | return images; 146 | } 147 | exports.generateImages = generateImages; 148 | function setSeed(_seed) { 149 | if (_seed === void 0) { _seed = 0; } 150 | seed = _seed; 151 | } 152 | exports.setSeed = setSeed; 153 | function setDefaultOptions(_defaultOptions) { 154 | forOwn(_defaultOptions, function (v, k) { 155 | exports.defaultOptions[k] = v; 156 | }); 157 | } 158 | exports.setDefaultOptions = setDefaultOptions; 159 | var Pixel = (function () { 160 | function Pixel() { 161 | this.r = 0; 162 | this.g = 0; 163 | this.b = 0; 164 | this.isEmpty = true; 165 | } 166 | Pixel.prototype.setFromHsv = function (hue, saturation, value, isLimitingColors) { 167 | if (isLimitingColors === void 0) { isLimitingColors = false; } 168 | this.isEmpty = false; 169 | this.r = value; 170 | this.g = value; 171 | this.b = value; 172 | var h = hue * 6; 173 | var i = Math.floor(h); 174 | var f = h - i; 175 | switch (i) { 176 | case 0: 177 | this.g *= 1 - saturation * (1 - f); 178 | this.b *= 1 - saturation; 179 | break; 180 | case 1: 181 | this.b *= 1 - saturation; 182 | this.r *= 1 - saturation * f; 183 | break; 184 | case 2: 185 | this.b *= 1 - saturation * (1 - f); 186 | this.r *= 1 - saturation; 187 | break; 188 | case 3: 189 | this.r *= 1 - saturation; 190 | this.g *= 1 - saturation * f; 191 | break; 192 | case 4: 193 | this.r *= 1 - saturation * (1 - f); 194 | this.g *= 1 - saturation; 195 | break; 196 | case 5: 197 | this.g *= 1 - saturation; 198 | this.b *= 1 - saturation * f; 199 | break; 200 | } 201 | if (isLimitingColors === true) { 202 | this.r = this.limitColor(this.r); 203 | this.g = this.limitColor(this.g); 204 | this.b = this.limitColor(this.b); 205 | } 206 | this.setStyle(); 207 | }; 208 | Pixel.prototype.setStyle = function () { 209 | var r = Math.floor(this.r * 255); 210 | var g = Math.floor(this.g * 255); 211 | var b = Math.floor(this.b * 255); 212 | this.style = "rgb(" + r + "," + g + "," + b + ")"; 213 | }; 214 | Pixel.prototype.limitColor = function (v) { 215 | return v < 0.25 ? 0 : v < 0.75 ? 0.5 : 1; 216 | }; 217 | return Pixel; 218 | }()); 219 | exports.Pixel = Pixel; 220 | function draw(context, pixels, x, y, rotationIndex) { 221 | if (rotationIndex === void 0) { rotationIndex = 0; } 222 | var pxs = pixels[rotationIndex]; 223 | var pw = pxs.length; 224 | var ph = pxs[0].length; 225 | var sbx = Math.floor(x - pw / 2); 226 | var sby = Math.floor(y - ph / 2); 227 | for (var y_1 = 0, sy = sby; y_1 < ph; y_1++, sy++) { 228 | for (var x_1 = 0, sx = sbx; x_1 < pw; x_1++, sx++) { 229 | var px = pxs[x_1][y_1]; 230 | if (!px.isEmpty) { 231 | context.fillStyle = px.style; 232 | context.fillRect(sx, sy, 1, 1); 233 | } 234 | } 235 | } 236 | } 237 | exports.draw = draw; 238 | function drawImage(context, images, x, y, rotationIndex) { 239 | if (rotationIndex === void 0) { rotationIndex = 0; } 240 | var img = images[rotationIndex]; 241 | context.drawImage(img, Math.floor(x - img.width / 2), Math.floor(y - img.height / 2)); 242 | } 243 | exports.drawImage = drawImage; 244 | function generatePixels(patterns, options, random) { 245 | var pw = reduce(patterns, function (w, p) { return Math.max(w, p.length); }, 0); 246 | var ph = patterns.length; 247 | var w = Math.round(pw * options.scaleX); 248 | var h = Math.round(ph * options.scaleY); 249 | w += options.isMirrorX ? 1 : 2; 250 | h += options.isMirrorY ? 1 : 2; 251 | var pixels = createPixels(patterns, pw, ph, w, h, options.scaleX, options.scaleY, random); 252 | if (options.isMirrorX) { 253 | pixels = mirrorX(pixels, w, h); 254 | w *= 2; 255 | } 256 | if (options.isMirrorY) { 257 | pixels = mirrorY(pixels, w, h); 258 | h *= 2; 259 | } 260 | pixels = createEdge(pixels, w, h); 261 | return pixels; 262 | } 263 | function createPixels(patterns, pw, ph, w, h, scaleX, scaleY, random) { 264 | return timesMap(w, function (x) { 265 | var px = Math.floor((x - 1) / scaleX); 266 | return timesMap(h, function (y) { 267 | var py = Math.floor((y - 1) / scaleY); 268 | if (px < 0 || px >= pw || py < 0 || py >= ph) { 269 | return 0; 270 | } 271 | var c = px < patterns[py].length ? patterns[py][px] : ' '; 272 | var m = 0; 273 | if (c === '-') { 274 | m = random.get01() < 0.5 ? 1 : 0; 275 | } 276 | else if (c === 'x' || c === 'X') { 277 | m = random.get01() < 0.5 ? 1 : -1; 278 | } 279 | else if (c === 'o' || c === 'O') { 280 | m = -1; 281 | } 282 | else if (c === '*') { 283 | m = 1; 284 | } 285 | return m; 286 | }); 287 | }); 288 | } 289 | function mirrorX(pixels, w, h) { 290 | return timesMap(w * 2, function (x) { return timesMap(h, function (y) { 291 | return x < w ? pixels[x][y] : pixels[w * 2 - x - 1][y]; 292 | }); }); 293 | } 294 | function mirrorY(pixels, w, h) { 295 | return timesMap(w, function (x) { return timesMap(h * 2, function (y) { 296 | return y < h ? pixels[x][y] : pixels[x][h * 2 - y - 1]; 297 | }); }); 298 | } 299 | function createEdge(pixels, w, h) { 300 | return timesMap(w, function (x) { return timesMap(h, function (y) { 301 | return ((pixels[x][y] === 0 && 302 | ((x - 1 >= 0 && pixels[x - 1][y] > 0) || 303 | (x + 1 < w && pixels[x + 1][y] > 0) || 304 | (y - 1 >= 0 && pixels[x][y - 1] > 0) || 305 | (y + 1 < h && pixels[x][y + 1] > 0))) ? 306 | -1 : pixels[x][y]); 307 | }); }); 308 | } 309 | function createRotated(pixels, rotationNum) { 310 | var pw = pixels.length; 311 | var ph = pixels[0].length; 312 | var pcx = pw / 2; 313 | var pcy = ph / 2; 314 | var s = Math.max(pw, ph); 315 | var w = Math.round(s * 1.5 / 2) * 2; 316 | var h = Math.round(s * 1.5 / 2) * 2; 317 | var cx = w / 2; 318 | var cy = h / 2; 319 | var offset = { x: 0, y: 0 }; 320 | return timesMap(rotationNum, function (ai) { 321 | var angle = -ai * Math.PI * 2 / rotationNum; 322 | return timesMap(w, function (x) { return timesMap(h, function (y) { 323 | offset.x = x - cx; 324 | offset.y = y - cy; 325 | rotateVector(offset, angle); 326 | var px = Math.round(offset.x + pcx); 327 | var py = Math.round(offset.y + pcy); 328 | return (px < 0 || px >= pw || py < 0 || py >= ph) ? 329 | 0 : pixels[px][py]; 330 | }); }); 331 | }); 332 | } 333 | function rotateVector(v, angle) { 334 | var vx = v.x; 335 | v.x = Math.cos(angle) * vx - Math.sin(angle) * v.y; 336 | v.y = Math.sin(angle) * vx + Math.cos(angle) * v.y; 337 | } 338 | function createColored(pixels, options) { 339 | var w = pixels.length; 340 | var h = pixels[0].length; 341 | var oh = 0; 342 | var hasPixel = false; 343 | for (var y = 0; y < h / 2; y++) { 344 | for (var x = 0; x < w; x++) { 345 | if (pixels[x][y] !== 0 || pixels[x][h - 1 - y] !== 0) { 346 | hasPixel = true; 347 | break; 348 | } 349 | } 350 | if (hasPixel) { 351 | break; 352 | } 353 | oh++; 354 | } 355 | var random = new Random(); 356 | random.setSeed(options.seed); 357 | return timesMap(w, function (x) { return timesMap(h, function (y) { 358 | var p = pixels[x][y]; 359 | if ((p === 1 && !options.isShowingBody) || 360 | (p === -1 && !options.isShowingEdge)) { 361 | return new Pixel(); 362 | } 363 | if (p !== 0) { 364 | var l = Math.sin((y - oh) / h * Math.PI) * options.colorLighting + 365 | (1 - options.colorLighting); 366 | var v = (l * (1 - options.colorNoise) + 367 | random.get01() * options.colorNoise) * options.value; 368 | v = v >= 0 ? (v <= 1 ? v : 1) : 0; 369 | if (p === -1) { 370 | v *= (1 - options.edgeDarkness); 371 | } 372 | var px = new Pixel(); 373 | px.setFromHsv(options.hue, options.saturation, v, options.isLimitingColors); 374 | return px; 375 | } 376 | else { 377 | return new Pixel(); 378 | } 379 | }); }); 380 | } 381 | function getHashFromString(str) { 382 | var hash = 0; 383 | var len = str.length; 384 | for (var i = 0; i < len; i++) { 385 | var chr = str.charCodeAt(i); 386 | hash = ((hash << 5) - hash) + chr; 387 | hash |= 0; 388 | } 389 | return hash; 390 | } 391 | function nArray(n, v) { 392 | var a = []; 393 | for (var i = 0; i < n; i++) { 394 | a.push(v); 395 | } 396 | return a; 397 | } 398 | function times(n, func) { 399 | for (var i = 0; i < n; i++) { 400 | func(i); 401 | } 402 | } 403 | function timesMap(n, func) { 404 | var result = []; 405 | for (var i = 0; i < n; i++) { 406 | result.push(func(i)); 407 | } 408 | return result; 409 | } 410 | function forEach(array, func) { 411 | for (var i = 0; i < array.length; i++) { 412 | func(array[i]); 413 | } 414 | } 415 | function forOwn(obj, func) { 416 | for (var p in obj) { 417 | func(obj[p], p); 418 | } 419 | } 420 | function map(array, func) { 421 | var result = []; 422 | for (var i = 0; i < array.length; i++) { 423 | result.push(func(array[i], i)); 424 | } 425 | return result; 426 | } 427 | function reduce(array, func, initValue) { 428 | var result = initValue; 429 | for (var i = 0; i < array.length; i++) { 430 | result = func(result, array[i], i); 431 | } 432 | return result; 433 | } 434 | var Random = (function () { 435 | function Random() { 436 | this.setSeed(); 437 | this.get01 = this.get01.bind(this); 438 | } 439 | Random.prototype.setSeed = function (v) { 440 | if (v === void 0) { v = -0x7fffffff; } 441 | if (v === -0x7fffffff) { 442 | v = Math.floor(Math.random() * 0x7fffffff); 443 | } 444 | this.x = v = 1812433253 * (v ^ (v >> 30)); 445 | this.y = v = 1812433253 * (v ^ (v >> 30)) + 1; 446 | this.z = v = 1812433253 * (v ^ (v >> 30)) + 2; 447 | this.w = v = 1812433253 * (v ^ (v >> 30)) + 3; 448 | return this; 449 | }; 450 | Random.prototype.getInt = function () { 451 | var t = this.x ^ (this.x << 11); 452 | this.x = this.y; 453 | this.y = this.z; 454 | this.z = this.w; 455 | this.w = (this.w ^ (this.w >> 19)) ^ (t ^ (t >> 8)); 456 | return this.w; 457 | }; 458 | Random.prototype.get01 = function () { 459 | return this.getInt() / 0x7fffffff; 460 | }; 461 | return Random; 462 | }()); 463 | 464 | 465 | /***/ } 466 | /******/ ]) 467 | }); 468 | ; -------------------------------------------------------------------------------- /web_modules/ppe/index.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["ppe"] = factory(); 8 | else 9 | root["ppe"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | module.exports = __webpack_require__(1); 58 | 59 | 60 | /***/ }, 61 | /* 1 */ 62 | /***/ function(module, exports) { 63 | 64 | "use strict"; 65 | exports.options = { 66 | scaleRatio: 1, 67 | canvas: null, 68 | isLimitingColors: false 69 | }; 70 | var emitters = {}; 71 | var seed = 0; 72 | var pools = []; 73 | var defaultPool; 74 | // emit the particle. 75 | // specify the type with the first character of the patternName 76 | // (e: explosion, m: muzzle, s: spark, t: trail, j: jet) 77 | function emit(patternName, x, y, angle, emitOptions, pool) { 78 | if (angle === void 0) { angle = 0; } 79 | if (emitOptions === void 0) { emitOptions = {}; } 80 | if (pool === void 0) { pool = defaultPool; } 81 | if (pool == null && defaultPool == null) { 82 | pool = defaultPool = new ParticlePool(); 83 | } 84 | if (emitters[patternName] == null) { 85 | var random_1 = new Random(); 86 | random_1.setSeed(seed + getHashFromString(patternName)); 87 | emitters[patternName] = new Emitter(patternName[0], emitOptions, random_1); 88 | } 89 | var velX = emitOptions.velX == null ? 0 : emitOptions.velX; 90 | var velY = emitOptions.velY == null ? 0 : emitOptions.velY; 91 | emitters[patternName].emit(x, y, angle, velX, velY, pool); 92 | } 93 | exports.emit = emit; 94 | function update() { 95 | for (var i = 0; i < pools.length; i++) { 96 | var pool = pools[i]; 97 | pool.update(); 98 | } 99 | } 100 | exports.update = update; 101 | function getParticles() { 102 | return defaultPool.particles; 103 | } 104 | exports.getParticles = getParticles; 105 | function setSeed(_seed) { 106 | if (_seed === void 0) { _seed = 0; } 107 | seed = _seed; 108 | } 109 | exports.setSeed = setSeed; 110 | function reset() { 111 | emitters = {}; 112 | clear(); 113 | } 114 | exports.reset = reset; 115 | function clear() { 116 | for (var i = 0; i < pools.length; i++) { 117 | var pool = pools[i]; 118 | pool.clear(); 119 | } 120 | } 121 | exports.clear = clear; 122 | function clearPools() { 123 | pools = []; 124 | defaultPool = new ParticlePool(); 125 | } 126 | exports.clearPools = clearPools; 127 | function setOptions(_options) { 128 | for (var attr in _options) { 129 | exports.options[attr] = _options[attr]; 130 | } 131 | } 132 | exports.setOptions = setOptions; 133 | var Emitter = (function () { 134 | function Emitter(patternType, emitOptions, random) { 135 | this.base = new Particle(); 136 | this.angleDeflection = 0; 137 | this.speedDeflection = 0.5; 138 | this.sizeDeflection = 0.5; 139 | this.ticksDeflection = 0.3; 140 | this.count = 1; 141 | var hue = emitOptions.hue == null ? random.get01() : emitOptions.hue; 142 | var sizeScale = emitOptions.sizeScale == null ? 1 : emitOptions.sizeScale; 143 | var countScale = emitOptions.countScale == null ? 1 : emitOptions.countScale; 144 | switch (patternType) { 145 | case 'e': 146 | this.base.speed = 0.7; 147 | this.base.slowdownRatio = 0.05; 148 | this.base.targetSize = 10; 149 | this.base.beginColor = new Color(hue, 1, 0.5, 0.3); 150 | this.base.middleColor = new Color(hue, 0.2, 0.9, 0.1); 151 | this.base.endColor = new Color(hue, 0, 0, 0); 152 | this.base.middleTicks = 20; 153 | this.base.endTicks = 30; 154 | this.angleDeflection = Math.PI * 2; 155 | this.count = 15; 156 | break; 157 | case 'm': 158 | case 's': 159 | this.base.speed = patternType === 'm' ? 1.5 : 0.5; 160 | this.base.slowdownRatio = 0.025; 161 | this.base.targetSize = 5; 162 | this.base.beginColor = new Color(hue, 0.5, 0.5, 0.3); 163 | this.base.middleColor = new Color(hue, 1, 0.9, 0.3); 164 | this.base.endColor = new Color(hue, 0.75, 0.75, 0.2); 165 | this.base.middleTicks = 10; 166 | this.base.endTicks = 20; 167 | this.angleDeflection = patternType === 'm' ? 168 | 0.3 * random.getForParam() : Math.PI * 2; 169 | this.count = 10; 170 | break; 171 | case 't': 172 | case 'j': 173 | this.base.speed = patternType === 't' ? 0.1 : 1; 174 | this.base.slowdownRatio = 0.03; 175 | this.base.targetSize = patternType === 't' ? 3 : 7; 176 | this.base.beginColor = new Color(hue, 0.7, 0.7, 0.4); 177 | this.base.middleColor = new Color(hue, 1, 0.9, 0.2); 178 | this.base.endColor = new Color(hue, 0.7, 0.7, 0.1); 179 | this.base.middleTicks = patternType === 't' ? 30 : 15; 180 | this.base.endTicks = patternType === 't' ? 40 : 20; 181 | this.angleDeflection = 0.5 * random.getForParam(); 182 | this.speedDeflection = 0.1; 183 | this.sizeDeflection = 0.1; 184 | this.ticksDeflection = 0.1; 185 | this.count = 0.5; 186 | break; 187 | } 188 | if (emitOptions.speed != null) { 189 | this.base.speed = emitOptions.speed; 190 | } 191 | if (emitOptions.slowdownRatio != null) { 192 | this.base.slowdownRatio = emitOptions.slowdownRatio; 193 | } 194 | this.base.speed *= sizeScale * exports.options.scaleRatio; 195 | this.base.targetSize *= sizeScale * exports.options.scaleRatio; 196 | this.count *= countScale; 197 | this.base.speed *= random.getForParam(); 198 | this.base.slowdownRatio *= random.getForParam(); 199 | this.base.targetSize *= random.getForParam(); 200 | var em = this.base.endTicks - this.base.middleTicks; 201 | this.base.middleTicks *= random.getForParam(); 202 | this.base.endTicks = this.base.middleTicks + em * random.getForParam(); 203 | this.speedDeflection *= random.getForParam(); 204 | this.sizeDeflection *= random.getForParam(); 205 | this.ticksDeflection *= random.getForParam(); 206 | this.count *= random.getForParam(); 207 | } 208 | Emitter.prototype.emit = function (x, y, angle, velX, velY, pool) { 209 | if (angle === void 0) { angle = 0; } 210 | if (velX === void 0) { velX = 0; } 211 | if (velY === void 0) { velY = 0; } 212 | if (this.count < 1 && this.count < Math.random()) { 213 | return; 214 | } 215 | for (var i = 0; i < this.count; i++) { 216 | var p = new Particle(); 217 | p.pos.x = x; 218 | p.pos.y = y; 219 | p.vel.x = velX; 220 | p.vel.y = velY; 221 | p.angle = angle + (Math.random() - 0.5) * this.angleDeflection; 222 | p.speed = this.base.speed * 223 | ((Math.random() * 2 - 1) * this.speedDeflection + 1); 224 | p.slowdownRatio = this.base.slowdownRatio; 225 | p.targetSize = this.base.targetSize * 226 | ((Math.random() * 2 - 1) * this.sizeDeflection + 1); 227 | p.middleTicks = this.base.middleTicks * 228 | ((Math.random() * 2 - 1) * this.ticksDeflection + 1); 229 | p.endTicks = this.base.endTicks * 230 | ((Math.random() * 2 - 1) * this.ticksDeflection + 1); 231 | p.beginColor = this.base.beginColor; 232 | p.middleColor = this.base.middleColor; 233 | p.endColor = this.base.endColor; 234 | pool.particles.push(p); 235 | } 236 | }; 237 | return Emitter; 238 | }()); 239 | exports.Emitter = Emitter; 240 | var Particle = (function () { 241 | function Particle() { 242 | this.pos = new Vector(); 243 | this.vel = new Vector(); 244 | this.size = 0; 245 | this.angle = 0; 246 | this.speed = 1; 247 | this.slowdownRatio = 0.01; 248 | this.targetSize = 10; 249 | this.middleTicks = 20; 250 | this.endTicks = 60; 251 | this.ticks = 0; 252 | } 253 | Particle.prototype.update = function (context) { 254 | this.pos.x += Math.cos(this.angle) * this.speed + this.vel.x; 255 | this.pos.y += Math.sin(this.angle) * this.speed + this.vel.y; 256 | this.speed *= (1 - this.slowdownRatio); 257 | this.vel.x *= 0.99; 258 | this.vel.y *= 0.99; 259 | if (this.ticks >= this.endTicks) { 260 | return false; 261 | } 262 | if (this.ticks < this.middleTicks) { 263 | this.color = this.beginColor.getLerped(this.middleColor, this.ticks / this.middleTicks); 264 | this.size += (this.targetSize - this.size) * 0.1; 265 | } 266 | else { 267 | this.color = this.middleColor.getLerped(this.endColor, (this.ticks - this.middleTicks) / (this.endTicks - this.middleTicks)); 268 | this.size *= 0.95; 269 | } 270 | this.color = this.color.getSparkled(); 271 | if (context != null) { 272 | context.fillStyle = this.color.getStyle(); 273 | context.fillRect(this.pos.x - this.size / 2, this.pos.y - this.size / 2, this.size, this.size); 274 | } 275 | this.ticks++; 276 | }; 277 | return Particle; 278 | }()); 279 | exports.Particle = Particle; 280 | var ParticlePool = (function () { 281 | function ParticlePool(canvas) { 282 | if (canvas === void 0) { canvas = exports.options.canvas; } 283 | this.canvas = canvas; 284 | this.particles = []; 285 | pools.push(this); 286 | } 287 | ParticlePool.prototype.update = function () { 288 | if (this.context == null && this.canvas != null) { 289 | this.context = this.canvas.getContext('2d'); 290 | } 291 | for (var i = 0; i < this.particles.length;) { 292 | if (this.particles[i].update(this.context) === false) { 293 | this.particles.splice(i, 1); 294 | } 295 | else { 296 | i++; 297 | } 298 | } 299 | }; 300 | ParticlePool.prototype.getParticles = function () { 301 | return this.particles; 302 | }; 303 | ParticlePool.prototype.clear = function () { 304 | this.particles = []; 305 | }; 306 | return ParticlePool; 307 | }()); 308 | exports.ParticlePool = ParticlePool; 309 | var Vector = (function () { 310 | function Vector(x, y) { 311 | if (x === void 0) { x = 0; } 312 | if (y === void 0) { y = 0; } 313 | this.x = x; 314 | this.y = y; 315 | } 316 | return Vector; 317 | }()); 318 | exports.Vector = Vector; 319 | var Color = (function () { 320 | function Color(hue, saturation, value, sparkleRatio) { 321 | if (hue === void 0) { hue = 0; } 322 | if (saturation === void 0) { saturation = 1; } 323 | if (value === void 0) { value = 1; } 324 | if (sparkleRatio === void 0) { sparkleRatio = 0; } 325 | this.hue = hue; 326 | this.saturation = saturation; 327 | this.value = value; 328 | this.sparkleRatio = sparkleRatio; 329 | this.r = 0; 330 | this.g = 0; 331 | this.b = 0; 332 | this.r = value; 333 | this.g = value; 334 | this.b = value; 335 | var h = hue * 6; 336 | var i = Math.floor(h); 337 | var f = h - i; 338 | switch (i) { 339 | case 0: 340 | this.g *= 1 - saturation * (1 - f); 341 | this.b *= 1 - saturation; 342 | break; 343 | case 1: 344 | this.b *= 1 - saturation; 345 | this.r *= 1 - saturation * f; 346 | break; 347 | case 2: 348 | this.b *= 1 - saturation * (1 - f); 349 | this.r *= 1 - saturation; 350 | break; 351 | case 3: 352 | this.r *= 1 - saturation; 353 | this.g *= 1 - saturation * f; 354 | break; 355 | case 4: 356 | this.r *= 1 - saturation * (1 - f); 357 | this.g *= 1 - saturation; 358 | break; 359 | case 5: 360 | this.g *= 1 - saturation; 361 | this.b *= 1 - saturation * f; 362 | break; 363 | } 364 | if (exports.options.isLimitingColors === true) { 365 | this.limitRgb(); 366 | } 367 | } 368 | Color.prototype.getStyle = function () { 369 | var r = Math.floor(this.r * 255); 370 | var g = Math.floor(this.g * 255); 371 | var b = Math.floor(this.b * 255); 372 | return "rgb(" + r + "," + g + "," + b + ")"; 373 | }; 374 | Color.prototype.getSparkled = function () { 375 | if (this.sparkled == null) { 376 | this.sparkled = new Color(); 377 | } 378 | this.sparkled.r = clamp(this.r + this.sparkleRatio * (Math.random() * 2 - 1)); 379 | this.sparkled.g = clamp(this.g + this.sparkleRatio * (Math.random() * 2 - 1)); 380 | this.sparkled.b = clamp(this.b + this.sparkleRatio * (Math.random() * 2 - 1)); 381 | if (exports.options.isLimitingColors === true) { 382 | this.sparkled.limitRgb(); 383 | } 384 | return this.sparkled; 385 | }; 386 | Color.prototype.getLerped = function (other, ratio) { 387 | if (this.lerped == null) { 388 | this.lerped = new Color(); 389 | } 390 | this.lerped.r = this.r * (1 - ratio) + other.r * ratio; 391 | this.lerped.g = this.g * (1 - ratio) + other.g * ratio; 392 | this.lerped.b = this.b * (1 - ratio) + other.b * ratio; 393 | this.lerped.sparkleRatio = 394 | this.sparkleRatio * (1 - ratio) + other.sparkleRatio * ratio; 395 | if (exports.options.isLimitingColors === true) { 396 | this.lerped.limitRgb(); 397 | } 398 | return this.lerped; 399 | }; 400 | Color.prototype.limitRgb = function () { 401 | this.r = this.limitColor(this.r); 402 | this.g = this.limitColor(this.g); 403 | this.b = this.limitColor(this.b); 404 | }; 405 | Color.prototype.limitColor = function (v) { 406 | return v < 0.25 ? 0 : v < 0.75 ? 0.5 : 1; 407 | }; 408 | return Color; 409 | }()); 410 | exports.Color = Color; 411 | function getHashFromString(str) { 412 | var hash = 0; 413 | var len = str.length; 414 | for (var i = 0; i < len; i++) { 415 | var chr = str.charCodeAt(i); 416 | hash = ((hash << 5) - hash) + chr; 417 | hash |= 0; 418 | } 419 | return hash; 420 | } 421 | function clamp(v) { 422 | if (v <= 0) { 423 | return 0; 424 | } 425 | else if (v >= 1) { 426 | return 1; 427 | } 428 | else { 429 | return v; 430 | } 431 | } 432 | var Random = (function () { 433 | function Random() { 434 | this.setSeed(); 435 | this.get01 = this.get01.bind(this); 436 | } 437 | Random.prototype.setSeed = function (v) { 438 | if (v === void 0) { v = -0x7fffffff; } 439 | if (v === -0x7fffffff) { 440 | v = Math.floor(Math.random() * 0x7fffffff); 441 | } 442 | this.x = v = 1812433253 * (v ^ (v >> 30)); 443 | this.y = v = 1812433253 * (v ^ (v >> 30)) + 1; 444 | this.z = v = 1812433253 * (v ^ (v >> 30)) + 2; 445 | this.w = v = 1812433253 * (v ^ (v >> 30)) + 3; 446 | return this; 447 | }; 448 | Random.prototype.getInt = function () { 449 | var t = this.x ^ (this.x << 11); 450 | this.x = this.y; 451 | this.y = this.z; 452 | this.z = this.w; 453 | this.w = (this.w ^ (this.w >> 19)) ^ (t ^ (t >> 8)); 454 | return this.w; 455 | }; 456 | Random.prototype.get01 = function () { 457 | return this.getInt() / 0x7fffffff; 458 | }; 459 | Random.prototype.getForParam = function () { 460 | return this.get01() + 0.5; 461 | }; 462 | return Random; 463 | }()); 464 | 465 | 466 | /***/ } 467 | /******/ ]) 468 | }); 469 | ; -------------------------------------------------------------------------------- /web_modules/gcc/index.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["gcc"] = factory(); 8 | else 9 | root["gcc"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | module.exports = __webpack_require__(1); 58 | 59 | 60 | /***/ }, 61 | /* 1 */ 62 | /***/ function(module, exports, __webpack_require__) { 63 | 64 | "use strict"; 65 | var GIFEncoder = __webpack_require__(2); 66 | exports.options = { 67 | scale: 0.5, 68 | durationSec: 3, 69 | keyCode: 67, 70 | capturingFps: 20, 71 | appFps: 60, 72 | isAppendingImgElement: true 73 | }; 74 | var contextsNum; 75 | var contexts; 76 | var isCaptured; 77 | var index = 0; 78 | var frameCount = 0; 79 | var image = new Image(); 80 | function capture(element) { 81 | frameCount++; 82 | var capturePerFrame = exports.options.appFps / exports.options.capturingFps; 83 | if (frameCount < capturePerFrame) { 84 | return; 85 | } 86 | frameCount -= capturePerFrame; 87 | if (!contexts) { 88 | begin(element); 89 | } 90 | contexts[index].drawImage(element, 0, 0); 91 | isCaptured[index] = true; 92 | index++; 93 | if (index >= contextsNum) { 94 | index = 0; 95 | } 96 | } 97 | exports.capture = capture; 98 | function captureSvg(svgElm) { 99 | var capturePerFrame = exports.options.appFps / exports.options.capturingFps; 100 | if (frameCount + 1 < capturePerFrame) { 101 | frameCount++; 102 | return; 103 | } 104 | var svgXml = new XMLSerializer().serializeToString(svgElm); 105 | image.src = "data:image/svg+xml;base64," + btoa(svgXml); 106 | capture(image); 107 | } 108 | exports.captureSvg = captureSvg; 109 | function begin(element) { 110 | contextsNum = exports.options.durationSec * exports.options.capturingFps; 111 | contexts = times(contextsNum, function () { 112 | var cvs = document.createElement('canvas'); 113 | cvs.width = element.width * exports.options.scale; 114 | cvs.height = element.height * exports.options.scale; 115 | var ctx = cvs.getContext('2d'); 116 | ctx.scale(exports.options.scale, exports.options.scale); 117 | return ctx; 118 | }); 119 | isCaptured = times(contextsNum, function () { return false; }); 120 | document.addEventListener('keydown', function (e) { 121 | if (e.keyCode == exports.options.keyCode) { 122 | end(); 123 | } 124 | }); 125 | } 126 | function end() { 127 | var encoder = new GIFEncoder(); 128 | encoder.setRepeat(0); 129 | encoder.setDelay(1000 / exports.options.capturingFps); 130 | encoder.start(); 131 | var idx = index; 132 | times(contextsNum, function () { 133 | if (isCaptured[idx]) { 134 | encoder.addFrame(contexts[idx]); 135 | } 136 | idx++; 137 | if (idx >= contextsNum) { 138 | idx = 0; 139 | } 140 | }); 141 | encoder.finish(); 142 | var binaryGif = encoder.stream().getData(); 143 | var imgElement = document.createElement('img'); 144 | imgElement.src = 'data:image/gif;base64,' + encode64(binaryGif); 145 | if (exports.options.isAppendingImgElement) { 146 | document.getElementsByTagName('body')[0].appendChild(imgElement); 147 | } 148 | return imgElement; 149 | } 150 | exports.end = end; 151 | function times(n, func) { 152 | var result = []; 153 | for (var i = 0; i < n; i++) { 154 | result.push(func()); 155 | } 156 | return result; 157 | } 158 | function setOptions(_options) { 159 | for (var attr in _options) { 160 | exports.options[attr] = _options[attr]; 161 | } 162 | } 163 | exports.setOptions = setOptions; 164 | // https://github.com/antimatter15/jsgif/blob/master/b64.js 165 | function encode64(input) { 166 | var output = "", i = 0, l = input.length, key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", chr1, chr2, chr3, enc1, enc2, enc3, enc4; 167 | while (i < l) { 168 | chr1 = input.charCodeAt(i++); 169 | chr2 = input.charCodeAt(i++); 170 | chr3 = input.charCodeAt(i++); 171 | enc1 = chr1 >> 2; 172 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 173 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 174 | enc4 = chr3 & 63; 175 | if (isNaN(chr2)) 176 | enc3 = enc4 = 64; 177 | else if (isNaN(chr3)) 178 | enc4 = 64; 179 | output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4); 180 | } 181 | return output; 182 | } 183 | 184 | 185 | /***/ }, 186 | /* 2 */ 187 | /***/ function(module, exports) { 188 | 189 | /** 190 | * This class lets you encode animated GIF files 191 | * Base class : http://www.java2s.com/Code/Java/2D-Graphics-GUI/AnimatedGifEncoder.htm 192 | * @author Kevin Weiner (original Java version - kweiner@fmsware.com) 193 | * @author Thibault Imbert (AS3 version - bytearray.org) 194 | * @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) 195 | * @version 0.1 AS3 implementation 196 | */ 197 | 198 | GIFEncoder = function () { 199 | 200 | for (var i = 0, chr = {}; i < 256; i++) 201 | chr[i] = String.fromCharCode(i); 202 | 203 | function ByteArray() { 204 | this.bin = []; 205 | } 206 | 207 | ByteArray.prototype.getData = function () { 208 | for (var v = '', l = this.bin.length, i = 0; i < l; i++) 209 | v += chr[this.bin[i]]; 210 | return v; 211 | }; 212 | 213 | ByteArray.prototype.writeByte = function (val) { 214 | this.bin.push(val); 215 | }; 216 | 217 | ByteArray.prototype.writeUTFBytes = function (string) { 218 | for (var l = string.length, i = 0; i < l; i++) 219 | this.writeByte(string.charCodeAt(i)); 220 | }; 221 | 222 | ByteArray.prototype.writeBytes = function (array, offset, length) { 223 | for (var l = length || array.length, i = offset || 0; i < l; i++) 224 | this.writeByte(array[i]); 225 | }; 226 | 227 | var exports = {}; 228 | var width; // image size 229 | var height; 230 | var transparent = null; // transparent color if given 231 | var transIndex; // transparent index in color table 232 | var repeat = -1; // no repeat 233 | var delay = 0; // frame delay (hundredths) 234 | var started = false; // ready to output frames 235 | var out; 236 | var image; // current frame 237 | var pixels; // BGR byte array from frame 238 | var indexedPixels; // converted frame indexed to palette 239 | var colorDepth; // number of bit planes 240 | var colorTab; // RGB palette 241 | var usedEntry = []; // active palette entries 242 | var palSize = 7; // color table size (bits-1) 243 | var dispose = -1; // disposal code (-1 = use default) 244 | var closeStream = false; // close stream when finished 245 | var firstFrame = true; 246 | var sizeSet = false; // if false, get size from first frame 247 | var sample = 10; // default sample interval for quantizer 248 | var comment = "Generated by jsgif (https://github.com/antimatter15/jsgif/)"; // default comment for generated gif 249 | 250 | /** 251 | * Sets the delay time between each frame, or changes it for subsequent frames 252 | * (applies to last frame added) 253 | * int delay time in milliseconds 254 | * @param ms 255 | */ 256 | 257 | var setDelay = exports.setDelay = function setDelay(ms) { 258 | delay = Math.round(ms / 10); 259 | }; 260 | 261 | /** 262 | * Sets the GIF frame disposal code for the last added frame and any 263 | * 264 | * subsequent frames. Default is 0 if no transparent color has been set, 265 | * otherwise 2. 266 | * @param code 267 | * int disposal code. 268 | */ 269 | 270 | var setDispose = exports.setDispose = function setDispose(code) { 271 | if (code >= 0) dispose = code; 272 | }; 273 | 274 | /** 275 | * Sets the number of times the set of GIF frames should be played. Default is 276 | * 1; 0 means play indefinitely. Must be invoked before the first image is 277 | * added. 278 | * 279 | * @param iter 280 | * int number of iterations. 281 | * @return 282 | */ 283 | 284 | var setRepeat = exports.setRepeat = function setRepeat(iter) { 285 | if (iter >= 0) repeat = iter; 286 | }; 287 | 288 | /** 289 | * Sets the transparent color for the last added frame and any subsequent 290 | * frames. Since all colors are subject to modification in the quantization 291 | * process, the color in the final palette for each frame closest to the given 292 | * color becomes the transparent color for that frame. May be set to null to 293 | * indicate no transparent color. 294 | * @param 295 | * Color to be treated as transparent on display. 296 | */ 297 | 298 | var setTransparent = exports.setTransparent = function setTransparent(c) { 299 | transparent = c; 300 | }; 301 | 302 | 303 | /** 304 | * Sets the comment for the block comment 305 | * @param 306 | * string to be insterted as comment 307 | */ 308 | 309 | var setComment = exports.setComment = function setComment(c) { 310 | comment = c; 311 | }; 312 | 313 | 314 | 315 | /** 316 | * The addFrame method takes an incoming BitmapData object to create each frames 317 | * @param 318 | * BitmapData object to be treated as a GIF's frame 319 | */ 320 | 321 | var addFrame = exports.addFrame = function addFrame(im, is_imageData) { 322 | 323 | if ((im === null) || !started || out === null) { 324 | throw new Error("Please call start method before calling addFrame"); 325 | } 326 | 327 | var ok = true; 328 | 329 | try { 330 | if (!is_imageData) { 331 | image = im.getImageData(0, 0, im.canvas.width, im.canvas.height).data; 332 | if (!sizeSet) setSize(im.canvas.width, im.canvas.height); 333 | } else { 334 | image = im; 335 | } 336 | getImagePixels(); // convert to correct format if necessary 337 | analyzePixels(); // build color table & map pixels 338 | 339 | if (firstFrame) { 340 | writeLSD(); // logical screen descriptior 341 | writePalette(); // global color table 342 | if (repeat >= 0) { 343 | // use NS app extension to indicate reps 344 | writeNetscapeExt(); 345 | } 346 | } 347 | 348 | writeGraphicCtrlExt(); // write graphic control extension 349 | if (comment !== '') { 350 | writeCommentExt(); // write comment extension 351 | } 352 | writeImageDesc(); // image descriptor 353 | if (!firstFrame) writePalette(); // local color table 354 | writePixels(); // encode and write pixel data 355 | firstFrame = false; 356 | } catch (e) { 357 | ok = false; 358 | } 359 | 360 | return ok; 361 | }; 362 | 363 | /** 364 | * Adds final trailer to the GIF stream, if you don't call the finish method 365 | * the GIF stream will not be valid. 366 | */ 367 | 368 | var finish = exports.finish = function finish() { 369 | 370 | if (!started) return false; 371 | 372 | var ok = true; 373 | started = false; 374 | 375 | try { 376 | out.writeByte(0x3b); // gif trailer 377 | } catch (e) { 378 | ok = false; 379 | } 380 | 381 | return ok; 382 | }; 383 | 384 | /** 385 | * Resets some members so that a new stream can be started. 386 | * This method is actually called by the start method 387 | */ 388 | 389 | var reset = function reset() { 390 | 391 | // reset for subsequent use 392 | transIndex = 0; 393 | image = null; 394 | pixels = null; 395 | indexedPixels = null; 396 | colorTab = null; 397 | closeStream = false; 398 | firstFrame = true; 399 | }; 400 | 401 | /** 402 | * * Sets frame rate in frames per second. Equivalent to 403 | * setDelay(1000/fps). 404 | * @param fps 405 | * float frame rate (frames per second) 406 | */ 407 | 408 | var setFrameRate = exports.setFrameRate = function setFrameRate(fps) { 409 | if (fps != 0xf) delay = Math.round(100 / fps); 410 | }; 411 | 412 | /** 413 | * Sets quality of color quantization (conversion of images to the maximum 256 414 | * colors allowed by the GIF specification). Lower values (minimum = 1) 415 | * produce better colors, but slow processing significantly. 10 is the 416 | * default, and produces good color mapping at reasonable speeds. Values 417 | * greater than 20 do not yield significant improvements in speed. 418 | * @param quality 419 | * int greater than 0. 420 | * @return 421 | */ 422 | 423 | var setQuality = exports.setQuality = function setQuality(quality) { 424 | if (quality < 1) quality = 1; 425 | sample = quality; 426 | }; 427 | 428 | /** 429 | * Sets the GIF frame size. The default size is the size of the first frame 430 | * added if this method is not invoked. 431 | * @param w 432 | * int frame width. 433 | * @param h 434 | * int frame width. 435 | */ 436 | 437 | var setSize = exports.setSize = function setSize(w, h) { 438 | 439 | if (started && !firstFrame) return; 440 | width = w; 441 | height = h; 442 | if (width < 1) width = 320; 443 | if (height < 1) height = 240; 444 | sizeSet = true; 445 | }; 446 | 447 | /** 448 | * Initiates GIF file creation on the given stream. 449 | * @param os 450 | * OutputStream on which GIF images are written. 451 | * @return false if initial write failed. 452 | */ 453 | 454 | var start = exports.start = function start() { 455 | 456 | reset(); 457 | var ok = true; 458 | closeStream = false; 459 | out = new ByteArray(); 460 | try { 461 | out.writeUTFBytes("GIF89a"); // header 462 | } catch (e) { 463 | ok = false; 464 | } 465 | 466 | return started = ok; 467 | }; 468 | 469 | var cont = exports.cont = function cont() { 470 | 471 | reset(); 472 | var ok = true; 473 | closeStream = false; 474 | out = new ByteArray(); 475 | 476 | return started = ok; 477 | }; 478 | 479 | /** 480 | * Analyzes image colors and creates color map. 481 | */ 482 | 483 | var analyzePixels = function analyzePixels() { 484 | 485 | var len = pixels.length; 486 | var nPix = len / 3; 487 | indexedPixels = []; 488 | var nq = new NeuQuant(pixels, len, sample); 489 | 490 | // initialize quantizer 491 | colorTab = nq.process(); // create reduced palette 492 | 493 | // map image pixels to new palette 494 | var k = 0; 495 | for (var j = 0; j < nPix; j++) { 496 | var index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff); 497 | usedEntry[index] = true; 498 | indexedPixels[j] = index; 499 | } 500 | 501 | pixels = null; 502 | colorDepth = 8; 503 | palSize = 7; 504 | 505 | // get closest match to transparent color if specified 506 | if (transparent !== null) { 507 | transIndex = findClosest(transparent); 508 | } 509 | }; 510 | 511 | /** 512 | * Returns index of palette color closest to c 513 | */ 514 | 515 | var findClosest = function findClosest(c) { 516 | 517 | if (colorTab === null) return -1; 518 | var r = (c & 0xFF0000) >> 16; 519 | var g = (c & 0x00FF00) >> 8; 520 | var b = (c & 0x0000FF); 521 | var minpos = 0; 522 | var dmin = 256 * 256 * 256; 523 | var len = colorTab.length; 524 | 525 | for (var i = 0; i < len;) { 526 | var dr = r - (colorTab[i++] & 0xff); 527 | var dg = g - (colorTab[i++] & 0xff); 528 | var db = b - (colorTab[i] & 0xff); 529 | var d = dr * dr + dg * dg + db * db; 530 | var index = i / 3; 531 | if (usedEntry[index] && (d < dmin)) { 532 | dmin = d; 533 | minpos = index; 534 | } 535 | i++; 536 | } 537 | return minpos; 538 | }; 539 | 540 | /** 541 | * Extracts image pixels into byte array "pixels 542 | */ 543 | 544 | var getImagePixels = function getImagePixels() { 545 | var w = width; 546 | var h = height; 547 | pixels = []; 548 | var data = image; 549 | var count = 0; 550 | 551 | for (var i = 0; i < h; i++) { 552 | 553 | for (var j = 0; j < w; j++) { 554 | 555 | var b = (i * w * 4) + j * 4; 556 | pixels[count++] = data[b]; 557 | pixels[count++] = data[b + 1]; 558 | pixels[count++] = data[b + 2]; 559 | 560 | } 561 | 562 | } 563 | }; 564 | 565 | /** 566 | * Writes Graphic Control Extension 567 | */ 568 | 569 | var writeGraphicCtrlExt = function writeGraphicCtrlExt() { 570 | out.writeByte(0x21); // extension introducer 571 | out.writeByte(0xf9); // GCE label 572 | out.writeByte(4); // data block size 573 | var transp; 574 | var disp; 575 | if (transparent === null) { 576 | transp = 0; 577 | disp = 0; // dispose = no action 578 | } else { 579 | transp = 1; 580 | disp = 2; // force clear if using transparent color 581 | } 582 | if (dispose >= 0) { 583 | disp = dispose & 7; // user override 584 | } 585 | disp <<= 2; 586 | // packed fields 587 | out.writeByte(0 | // 1:3 reserved 588 | disp | // 4:6 disposal 589 | 0 | // 7 user input - 0 = none 590 | transp); // 8 transparency flag 591 | 592 | WriteShort(delay); // delay x 1/100 sec 593 | out.writeByte(transIndex); // transparent color index 594 | out.writeByte(0); // block terminator 595 | }; 596 | 597 | /** 598 | * Writes Comment Extention 599 | */ 600 | 601 | var writeCommentExt = function writeCommentExt() { 602 | out.writeByte(0x21); // extension introducer 603 | out.writeByte(0xfe); // comment label 604 | out.writeByte(comment.length); // Block Size (s) 605 | out.writeUTFBytes(comment); 606 | out.writeByte(0); // block terminator 607 | }; 608 | 609 | 610 | /** 611 | * Writes Image Descriptor 612 | */ 613 | 614 | var writeImageDesc = function writeImageDesc() { 615 | 616 | out.writeByte(0x2c); // image separator 617 | WriteShort(0); // image position x,y = 0,0 618 | WriteShort(0); 619 | WriteShort(width); // image size 620 | WriteShort(height); 621 | 622 | // packed fields 623 | if (firstFrame) { 624 | // no LCT - GCT is used for first (or only) frame 625 | out.writeByte(0); 626 | } else { 627 | // specify normal LCT 628 | out.writeByte(0x80 | // 1 local color table 1=yes 629 | 0 | // 2 interlace - 0=no 630 | 0 | // 3 sorted - 0=no 631 | 0 | // 4-5 reserved 632 | palSize); // 6-8 size of color table 633 | } 634 | }; 635 | 636 | /** 637 | * Writes Logical Screen Descriptor 638 | */ 639 | 640 | var writeLSD = function writeLSD() { 641 | 642 | // logical screen size 643 | WriteShort(width); 644 | WriteShort(height); 645 | // packed fields 646 | out.writeByte((0x80 | // 1 : global color table flag = 1 (gct used) 647 | 0x70 | // 2-4 : color resolution = 7 648 | 0x00 | // 5 : gct sort flag = 0 649 | palSize)); // 6-8 : gct size 650 | 651 | out.writeByte(0); // background color index 652 | out.writeByte(0); // pixel aspect ratio - assume 1:1 653 | }; 654 | 655 | /** 656 | * Writes Netscape application extension to define repeat count. 657 | */ 658 | 659 | var writeNetscapeExt = function writeNetscapeExt() { 660 | out.writeByte(0x21); // extension introducer 661 | out.writeByte(0xff); // app extension label 662 | out.writeByte(11); // block size 663 | out.writeUTFBytes("NETSCAPE" + "2.0"); // app id + auth code 664 | out.writeByte(3); // sub-block size 665 | out.writeByte(1); // loop sub-block id 666 | WriteShort(repeat); // loop count (extra iterations, 0=repeat forever) 667 | out.writeByte(0); // block terminator 668 | }; 669 | 670 | /** 671 | * Writes color table 672 | */ 673 | 674 | var writePalette = function writePalette() { 675 | out.writeBytes(colorTab); 676 | var n = (3 * 256) - colorTab.length; 677 | for (var i = 0; i < n; i++) out.writeByte(0); 678 | }; 679 | 680 | var WriteShort = function WriteShort(pValue) { 681 | out.writeByte(pValue & 0xFF); 682 | out.writeByte((pValue >> 8) & 0xFF); 683 | }; 684 | 685 | /** 686 | * Encodes and writes pixel data 687 | */ 688 | 689 | var writePixels = function writePixels() { 690 | var myencoder = new LZWEncoder(width, height, indexedPixels, colorDepth); 691 | myencoder.encode(out); 692 | }; 693 | 694 | /** 695 | * Retrieves the GIF stream 696 | */ 697 | 698 | var stream = exports.stream = function stream() { 699 | return out; 700 | }; 701 | 702 | var setProperties = exports.setProperties = function setProperties(has_start, is_first) { 703 | started = has_start; 704 | firstFrame = is_first; 705 | }; 706 | 707 | return exports; 708 | 709 | }; 710 | 711 | module.exports = GIFEncoder; 712 | 713 | /** 714 | * This class handles LZW encoding 715 | * Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. 716 | * @author Kevin Weiner (original Java version - kweiner@fmsware.com) 717 | * @author Thibault Imbert (AS3 version - bytearray.org) 718 | * @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) 719 | * @version 0.1 AS3 implementation 720 | */ 721 | 722 | LZWEncoder = function () { 723 | 724 | var exports = {}; 725 | var EOF = -1; 726 | var imgW; 727 | var imgH; 728 | var pixAry; 729 | var initCodeSize; 730 | var remaining; 731 | var curPixel; 732 | 733 | // GIFCOMPR.C - GIF Image compression routines 734 | // Lempel-Ziv compression based on 'compress'. GIF modifications by 735 | // David Rowley (mgardi@watdcsu.waterloo.edu) 736 | // General DEFINEs 737 | 738 | var BITS = 12; 739 | var HSIZE = 5003; // 80% occupancy 740 | 741 | // GIF Image compression - modified 'compress' 742 | // Based on: compress.c - File compression ala IEEE Computer, June 1984. 743 | // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) 744 | // Jim McKie (decvax!mcvax!jim) 745 | // Steve Davies (decvax!vax135!petsd!peora!srd) 746 | // Ken Turkowski (decvax!decwrl!turtlevax!ken) 747 | // James A. Woods (decvax!ihnp4!ames!jaw) 748 | // Joe Orost (decvax!vax135!petsd!joe) 749 | 750 | var n_bits; // number of bits/code 751 | var maxbits = BITS; // user settable max # bits/code 752 | var maxcode; // maximum code, given n_bits 753 | var maxmaxcode = 1 << BITS; // should NEVER generate this code 754 | var htab = []; 755 | var codetab = []; 756 | var hsize = HSIZE; // for dynamic table sizing 757 | var free_ent = 0; // first unused entry 758 | 759 | // block compression parameters -- after all codes are used up, 760 | // and compression rate changes, start over. 761 | 762 | var clear_flg = false; 763 | 764 | // Algorithm: use open addressing double hashing (no chaining) on the 765 | // prefix code / next character combination. We do a variant of Knuth's 766 | // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime 767 | // secondary probe. Here, the modular division first probe is gives way 768 | // to a faster exclusive-or manipulation. Also do block compression with 769 | // an adaptive reset, whereby the code table is cleared when the compression 770 | // ratio decreases, but after the table fills. The variable-length output 771 | // codes are re-sized at this point, and a special CLEAR code is generated 772 | // for the decompressor. Late addition: construct the table according to 773 | // file size for noticeable speed improvement on small files. Please direct 774 | // questions about this implementation to ames!jaw. 775 | 776 | var g_init_bits; 777 | var ClearCode; 778 | var EOFCode; 779 | 780 | // output 781 | // Output the given code. 782 | // Inputs: 783 | // code: A n_bits-bit integer. If == -1, then EOF. This assumes 784 | // that n_bits =< wordsize - 1. 785 | // Outputs: 786 | // Outputs code to the file. 787 | // Assumptions: 788 | // Chars are 8 bits long. 789 | // Algorithm: 790 | // Maintain a BITS character long buffer (so that 8 codes will 791 | // fit in it exactly). Use the VAX insv instruction to insert each 792 | // code in turn. When the buffer fills up empty it and start over. 793 | 794 | var cur_accum = 0; 795 | var cur_bits = 0; 796 | var masks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF]; 797 | 798 | // Number of characters so far in this 'packet' 799 | var a_count; 800 | 801 | // Define the storage for the packet accumulator 802 | var accum = []; 803 | 804 | var LZWEncoder = exports.LZWEncoder = function LZWEncoder(width, height, pixels, color_depth) { 805 | imgW = width; 806 | imgH = height; 807 | pixAry = pixels; 808 | initCodeSize = Math.max(2, color_depth); 809 | }; 810 | 811 | // Add a character to the end of the current packet, and if it is 254 812 | // characters, flush the packet to disk. 813 | var char_out = function char_out(c, outs) { 814 | accum[a_count++] = c; 815 | if (a_count >= 254) flush_char(outs); 816 | }; 817 | 818 | // Clear out the hash table 819 | // table clear for block compress 820 | 821 | var cl_block = function cl_block(outs) { 822 | cl_hash(hsize); 823 | free_ent = ClearCode + 2; 824 | clear_flg = true; 825 | output(ClearCode, outs); 826 | }; 827 | 828 | // reset code table 829 | var cl_hash = function cl_hash(hsize) { 830 | for (var i = 0; i < hsize; ++i) htab[i] = -1; 831 | }; 832 | 833 | var compress = exports.compress = function compress(init_bits, outs) { 834 | 835 | var fcode; 836 | var i; /* = 0 */ 837 | var c; 838 | var ent; 839 | var disp; 840 | var hsize_reg; 841 | var hshift; 842 | 843 | // Set up the globals: g_init_bits - initial number of bits 844 | g_init_bits = init_bits; 845 | 846 | // Set up the necessary values 847 | clear_flg = false; 848 | n_bits = g_init_bits; 849 | maxcode = MAXCODE(n_bits); 850 | 851 | ClearCode = 1 << (init_bits - 1); 852 | EOFCode = ClearCode + 1; 853 | free_ent = ClearCode + 2; 854 | 855 | a_count = 0; // clear packet 856 | 857 | ent = nextPixel(); 858 | 859 | hshift = 0; 860 | for (fcode = hsize; fcode < 65536; fcode *= 2) 861 | ++hshift; 862 | hshift = 8 - hshift; // set hash code range bound 863 | 864 | hsize_reg = hsize; 865 | cl_hash(hsize_reg); // clear hash table 866 | 867 | output(ClearCode, outs); 868 | 869 | outer_loop: while ((c = nextPixel()) != EOF) { 870 | fcode = (c << maxbits) + ent; 871 | i = (c << hshift) ^ ent; // xor hashing 872 | 873 | if (htab[i] == fcode) { 874 | ent = codetab[i]; 875 | continue; 876 | } 877 | 878 | else if (htab[i] >= 0) { // non-empty slot 879 | 880 | disp = hsize_reg - i; // secondary hash (after G. Knott) 881 | if (i === 0) disp = 1; 882 | 883 | do { 884 | if ((i -= disp) < 0) 885 | i += hsize_reg; 886 | 887 | if (htab[i] == fcode) { 888 | ent = codetab[i]; 889 | continue outer_loop; 890 | } 891 | } while (htab[i] >= 0); 892 | } 893 | 894 | output(ent, outs); 895 | ent = c; 896 | if (free_ent < maxmaxcode) { 897 | codetab[i] = free_ent++; // code -> hashtable 898 | htab[i] = fcode; 899 | } 900 | else cl_block(outs); 901 | } 902 | 903 | // Put out the final code. 904 | output(ent, outs); 905 | output(EOFCode, outs); 906 | }; 907 | 908 | // ---------------------------------------------------------------------------- 909 | var encode = exports.encode = function encode(os) { 910 | os.writeByte(initCodeSize); // write "initial code size" byte 911 | remaining = imgW * imgH; // reset navigation variables 912 | curPixel = 0; 913 | compress(initCodeSize + 1, os); // compress and write the pixel data 914 | os.writeByte(0); // write block terminator 915 | }; 916 | 917 | // Flush the packet to disk, and reset the accumulator 918 | var flush_char = function flush_char(outs) { 919 | if (a_count > 0) { 920 | outs.writeByte(a_count); 921 | outs.writeBytes(accum, 0, a_count); 922 | a_count = 0; 923 | } 924 | }; 925 | 926 | var MAXCODE = function MAXCODE(n_bits) { 927 | return (1 << n_bits) - 1; 928 | }; 929 | 930 | // ---------------------------------------------------------------------------- 931 | // Return the next pixel from the image 932 | // ---------------------------------------------------------------------------- 933 | 934 | var nextPixel = function nextPixel() { 935 | if (remaining === 0) return EOF; 936 | --remaining; 937 | var pix = pixAry[curPixel++]; 938 | return pix & 0xff; 939 | }; 940 | 941 | var output = function output(code, outs) { 942 | 943 | cur_accum &= masks[cur_bits]; 944 | 945 | if (cur_bits > 0) cur_accum |= (code << cur_bits); 946 | else cur_accum = code; 947 | 948 | cur_bits += n_bits; 949 | 950 | while (cur_bits >= 8) { 951 | char_out((cur_accum & 0xff), outs); 952 | cur_accum >>= 8; 953 | cur_bits -= 8; 954 | } 955 | 956 | // If the next entry is going to be too big for the code size, 957 | // then increase it, if possible. 958 | 959 | if (free_ent > maxcode || clear_flg) { 960 | 961 | if (clear_flg) { 962 | 963 | maxcode = MAXCODE(n_bits = g_init_bits); 964 | clear_flg = false; 965 | 966 | } else { 967 | 968 | ++n_bits; 969 | if (n_bits == maxbits) maxcode = maxmaxcode; 970 | else maxcode = MAXCODE(n_bits); 971 | } 972 | } 973 | 974 | if (code == EOFCode) { 975 | 976 | // At EOF, write the rest of the buffer. 977 | while (cur_bits > 0) { 978 | char_out((cur_accum & 0xff), outs); 979 | cur_accum >>= 8; 980 | cur_bits -= 8; 981 | } 982 | 983 | flush_char(outs); 984 | } 985 | }; 986 | 987 | LZWEncoder.apply(this, arguments); 988 | return exports; 989 | }; 990 | 991 | /* 992 | * NeuQuant Neural-Net Quantization Algorithm 993 | * ------------------------------------------ 994 | * 995 | * Copyright (c) 1994 Anthony Dekker 996 | * 997 | * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See 998 | * "Kohonen neural networks for optimal colour quantization" in "Network: 999 | * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of 1000 | * the algorithm. 1001 | * 1002 | * Any party obtaining a copy of these files from the author, directly or 1003 | * indirectly, is granted, free of charge, a full and unrestricted irrevocable, 1004 | * world-wide, paid up, royalty-free, nonexclusive right and license to deal in 1005 | * this software and documentation files (the "Software"), including without 1006 | * limitation the rights to use, copy, modify, merge, publish, distribute, 1007 | * sublicense, and/or sell copies of the Software, and to permit persons who 1008 | * receive copies from any such party to do so, with the only requirement being 1009 | * that this copyright notice remain intact. 1010 | */ 1011 | 1012 | /* 1013 | * This class handles Neural-Net quantization algorithm 1014 | * @author Kevin Weiner (original Java version - kweiner@fmsware.com) 1015 | * @author Thibault Imbert (AS3 version - bytearray.org) 1016 | * @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif) 1017 | * @version 0.1 AS3 implementation 1018 | */ 1019 | 1020 | NeuQuant = function () { 1021 | 1022 | var exports = {}; 1023 | var netsize = 256; /* number of colours used */ 1024 | 1025 | /* four primes near 500 - assume no image has a length so large */ 1026 | /* that it is divisible by all four primes */ 1027 | 1028 | var prime1 = 499; 1029 | var prime2 = 491; 1030 | var prime3 = 487; 1031 | var prime4 = 503; 1032 | var minpicturebytes = (3 * prime4); /* minimum size for input image */ 1033 | 1034 | /* 1035 | * Program Skeleton ---------------- [select samplefac in range 1..30] [read 1036 | * image from input file] pic = (unsigned char*) malloc(3*width*height); 1037 | * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output 1038 | * image header, using writecolourmap(f)] inxbuild(); write output image using 1039 | * inxsearch(b,g,r) 1040 | */ 1041 | 1042 | /* 1043 | * Network Definitions ------------------- 1044 | */ 1045 | 1046 | var maxnetpos = (netsize - 1); 1047 | var netbiasshift = 4; /* bias for colour values */ 1048 | var ncycles = 100; /* no. of learning cycles */ 1049 | 1050 | /* defs for freq and bias */ 1051 | var intbiasshift = 16; /* bias for fractions */ 1052 | var intbias = (1 << intbiasshift); 1053 | var gammashift = 10; /* gamma = 1024 */ 1054 | var gamma = (1 << gammashift); 1055 | var betashift = 10; 1056 | var beta = (intbias >> betashift); /* beta = 1/1024 */ 1057 | var betagamma = (intbias << (gammashift - betashift)); 1058 | 1059 | /* defs for decreasing radius factor */ 1060 | var initrad = (netsize >> 3); /* for 256 cols, radius starts */ 1061 | var radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ 1062 | var radiusbias = (1 << radiusbiasshift); 1063 | var initradius = (initrad * radiusbias); /* and decreases by a */ 1064 | var radiusdec = 30; /* factor of 1/30 each cycle */ 1065 | 1066 | /* defs for decreasing alpha factor */ 1067 | var alphabiasshift = 10; /* alpha starts at 1.0 */ 1068 | var initalpha = (1 << alphabiasshift); 1069 | var alphadec; /* biased by 10 bits */ 1070 | 1071 | /* radbias and alpharadbias used for radpower calculation */ 1072 | var radbiasshift = 8; 1073 | var radbias = (1 << radbiasshift); 1074 | var alpharadbshift = (alphabiasshift + radbiasshift); 1075 | var alpharadbias = (1 << alpharadbshift); 1076 | 1077 | /* 1078 | * Types and Global Variables -------------------------- 1079 | */ 1080 | 1081 | var thepicture; /* the input image itself */ 1082 | var lengthcount; /* lengthcount = H*W*3 */ 1083 | var samplefac; /* sampling factor 1..30 */ 1084 | 1085 | // typedef int pixel[4]; /* BGRc */ 1086 | var network; /* the network itself - [netsize][4] */ 1087 | var netindex = []; 1088 | 1089 | /* for network lookup - really 256 */ 1090 | var bias = []; 1091 | 1092 | /* bias and freq arrays for learning */ 1093 | var freq = []; 1094 | var radpower = []; 1095 | 1096 | var NeuQuant = exports.NeuQuant = function NeuQuant(thepic, len, sample) { 1097 | 1098 | var i; 1099 | var p; 1100 | 1101 | thepicture = thepic; 1102 | lengthcount = len; 1103 | samplefac = sample; 1104 | 1105 | network = new Array(netsize); 1106 | 1107 | for (i = 0; i < netsize; i++) { 1108 | 1109 | network[i] = new Array(4); 1110 | p = network[i]; 1111 | p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize; 1112 | freq[i] = intbias / netsize; /* 1/netsize */ 1113 | bias[i] = 0; 1114 | } 1115 | }; 1116 | 1117 | var colorMap = function colorMap() { 1118 | 1119 | var map = []; 1120 | var index = new Array(netsize); 1121 | 1122 | for (var i = 0; i < netsize; i++) 1123 | index[network[i][3]] = i; 1124 | 1125 | var k = 0; 1126 | for (var l = 0; l < netsize; l++) { 1127 | var j = index[l]; 1128 | map[k++] = (network[j][0]); 1129 | map[k++] = (network[j][1]); 1130 | map[k++] = (network[j][2]); 1131 | } 1132 | 1133 | return map; 1134 | }; 1135 | 1136 | /* 1137 | * Insertion sort of network and building of netindex[0..255] (to do after 1138 | * unbias) 1139 | * ------------------------------------------------------------------------------- 1140 | */ 1141 | 1142 | var inxbuild = function inxbuild() { 1143 | 1144 | var i; 1145 | var j; 1146 | var smallpos; 1147 | var smallval; 1148 | var p; 1149 | var q; 1150 | var previouscol; 1151 | var startpos; 1152 | 1153 | previouscol = 0; 1154 | startpos = 0; 1155 | for (i = 0; i < netsize; i++) { 1156 | 1157 | p = network[i]; 1158 | smallpos = i; 1159 | smallval = p[1]; /* index on g */ 1160 | 1161 | /* find smallest in i..netsize-1 */ 1162 | for (j = i + 1; j < netsize; j++) { 1163 | 1164 | q = network[j]; 1165 | if (q[1] < smallval) { /* index on g */ 1166 | smallpos = j; 1167 | smallval = q[1]; /* index on g */ 1168 | } 1169 | } 1170 | q = network[smallpos]; 1171 | 1172 | /* swap p (i) and q (smallpos) entries */ 1173 | if (i != smallpos) { 1174 | j = q[0]; 1175 | q[0] = p[0]; 1176 | p[0] = j; 1177 | j = q[1]; 1178 | q[1] = p[1]; 1179 | p[1] = j; 1180 | j = q[2]; 1181 | q[2] = p[2]; 1182 | p[2] = j; 1183 | j = q[3]; 1184 | q[3] = p[3]; 1185 | p[3] = j; 1186 | } 1187 | 1188 | /* smallval entry is now in position i */ 1189 | 1190 | if (smallval != previouscol) { 1191 | 1192 | netindex[previouscol] = (startpos + i) >> 1; 1193 | 1194 | for (j = previouscol + 1; j < smallval; j++) netindex[j] = i; 1195 | 1196 | previouscol = smallval; 1197 | startpos = i; 1198 | } 1199 | } 1200 | 1201 | netindex[previouscol] = (startpos + maxnetpos) >> 1; 1202 | for (j = previouscol + 1; j < 256; j++) netindex[j] = maxnetpos; /* really 256 */ 1203 | }; 1204 | 1205 | /* 1206 | * Main Learning Loop ------------------ 1207 | */ 1208 | 1209 | var learn = function learn() { 1210 | 1211 | var i; 1212 | var j; 1213 | var b; 1214 | var g; 1215 | var r; 1216 | var radius; 1217 | var rad; 1218 | var alpha; 1219 | var step; 1220 | var delta; 1221 | var samplepixels; 1222 | var p; 1223 | var pix; 1224 | var lim; 1225 | 1226 | if (lengthcount < minpicturebytes) samplefac = 1; 1227 | 1228 | alphadec = 30 + ((samplefac - 1) / 3); 1229 | p = thepicture; 1230 | pix = 0; 1231 | lim = lengthcount; 1232 | samplepixels = lengthcount / (3 * samplefac); 1233 | delta = (samplepixels / ncycles) | 0; 1234 | alpha = initalpha; 1235 | radius = initradius; 1236 | 1237 | rad = radius >> radiusbiasshift; 1238 | if (rad <= 1) rad = 0; 1239 | 1240 | for (i = 0; i < rad; i++) radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); 1241 | 1242 | if (lengthcount < minpicturebytes) step = 3; 1243 | 1244 | else if ((lengthcount % prime1) !== 0) step = 3 * prime1; 1245 | 1246 | else { 1247 | 1248 | if ((lengthcount % prime2) !== 0) step = 3 * prime2; 1249 | else { 1250 | if ((lengthcount % prime3) !== 0) step = 3 * prime3; 1251 | else step = 3 * prime4; 1252 | } 1253 | } 1254 | 1255 | i = 0; 1256 | while (i < samplepixels) { 1257 | 1258 | b = (p[pix + 0] & 0xff) << netbiasshift; 1259 | g = (p[pix + 1] & 0xff) << netbiasshift; 1260 | r = (p[pix + 2] & 0xff) << netbiasshift; 1261 | j = contest(b, g, r); 1262 | 1263 | altersingle(alpha, j, b, g, r); 1264 | if (rad !== 0) alterneigh(rad, j, b, g, r); /* alter neighbours */ 1265 | 1266 | pix += step; 1267 | if (pix >= lim) pix -= lengthcount; 1268 | 1269 | i++; 1270 | 1271 | if (delta === 0) delta = 1; 1272 | 1273 | if (i % delta === 0) { 1274 | alpha -= alpha / alphadec; 1275 | radius -= radius / radiusdec; 1276 | rad = radius >> radiusbiasshift; 1277 | 1278 | if (rad <= 1) rad = 0; 1279 | 1280 | for (j = 0; j < rad; j++) radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); 1281 | } 1282 | } 1283 | }; 1284 | 1285 | /* 1286 | ** Search for BGR values 0..255 (after net is unbiased) and return colour 1287 | * index 1288 | * ---------------------------------------------------------------------------- 1289 | */ 1290 | 1291 | var map = exports.map = function map(b, g, r) { 1292 | 1293 | var i; 1294 | var j; 1295 | var dist; 1296 | var a; 1297 | var bestd; 1298 | var p; 1299 | var best; 1300 | 1301 | bestd = 1000; /* biggest possible dist is 256*3 */ 1302 | best = -1; 1303 | i = netindex[g]; /* index on g */ 1304 | j = i - 1; /* start at netindex[g] and work outwards */ 1305 | 1306 | while ((i < netsize) || (j >= 0)) { 1307 | 1308 | if (i < netsize) { 1309 | p = network[i]; 1310 | dist = p[1] - g; /* inx key */ 1311 | 1312 | if (dist >= bestd) i = netsize; /* stop iter */ 1313 | 1314 | else { 1315 | 1316 | i++; 1317 | if (dist < 0) dist = -dist; 1318 | a = p[0] - b; 1319 | if (a < 0) a = -a; 1320 | dist += a; 1321 | 1322 | if (dist < bestd) { 1323 | a = p[2] - r; 1324 | if (a < 0) a = -a; 1325 | dist += a; 1326 | 1327 | if (dist < bestd) { 1328 | bestd = dist; 1329 | best = p[3]; 1330 | } 1331 | } 1332 | } 1333 | } 1334 | 1335 | if (j >= 0) { 1336 | 1337 | p = network[j]; 1338 | dist = g - p[1]; /* inx key - reverse dif */ 1339 | 1340 | if (dist >= bestd) j = -1; /* stop iter */ 1341 | 1342 | else { 1343 | 1344 | j--; 1345 | if (dist < 0) dist = -dist; 1346 | a = p[0] - b; 1347 | if (a < 0) a = -a; 1348 | dist += a; 1349 | 1350 | if (dist < bestd) { 1351 | a = p[2] - r; 1352 | if (a < 0) a = -a; 1353 | dist += a; 1354 | if (dist < bestd) { 1355 | bestd = dist; 1356 | best = p[3]; 1357 | } 1358 | } 1359 | } 1360 | } 1361 | } 1362 | 1363 | return (best); 1364 | }; 1365 | 1366 | var process = exports.process = function process() { 1367 | learn(); 1368 | unbiasnet(); 1369 | inxbuild(); 1370 | return colorMap(); 1371 | }; 1372 | 1373 | /* 1374 | * Unbias network to give byte values 0..255 and record position i to prepare 1375 | * for sort 1376 | * ----------------------------------------------------------------------------------- 1377 | */ 1378 | 1379 | var unbiasnet = function unbiasnet() { 1380 | 1381 | var i; 1382 | var j; 1383 | 1384 | for (i = 0; i < netsize; i++) { 1385 | network[i][0] >>= netbiasshift; 1386 | network[i][1] >>= netbiasshift; 1387 | network[i][2] >>= netbiasshift; 1388 | network[i][3] = i; /* record colour no */ 1389 | } 1390 | }; 1391 | 1392 | /* 1393 | * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in 1394 | * radpower[|i-j|] 1395 | * --------------------------------------------------------------------------------- 1396 | */ 1397 | 1398 | var alterneigh = function alterneigh(rad, i, b, g, r) { 1399 | 1400 | var j; 1401 | var k; 1402 | var lo; 1403 | var hi; 1404 | var a; 1405 | var m; 1406 | var p; 1407 | 1408 | lo = i - rad; 1409 | if (lo < -1) lo = -1; 1410 | 1411 | hi = i + rad; 1412 | if (hi > netsize) hi = netsize; 1413 | 1414 | j = i + 1; 1415 | k = i - 1; 1416 | m = 1; 1417 | 1418 | while ((j < hi) || (k > lo)) { 1419 | a = radpower[m++]; 1420 | 1421 | if (j < hi) { 1422 | p = network[j++]; 1423 | 1424 | try { 1425 | p[0] -= (a * (p[0] - b)) / alpharadbias; 1426 | p[1] -= (a * (p[1] - g)) / alpharadbias; 1427 | p[2] -= (a * (p[2] - r)) / alpharadbias; 1428 | } catch (e) { } // prevents 1.3 miscompilation 1429 | } 1430 | 1431 | if (k > lo) { 1432 | p = network[k--]; 1433 | 1434 | try { 1435 | p[0] -= (a * (p[0] - b)) / alpharadbias; 1436 | p[1] -= (a * (p[1] - g)) / alpharadbias; 1437 | p[2] -= (a * (p[2] - r)) / alpharadbias; 1438 | } catch (e) { } 1439 | } 1440 | } 1441 | }; 1442 | 1443 | /* 1444 | * Move neuron i towards biased (b,g,r) by factor alpha 1445 | * ---------------------------------------------------- 1446 | */ 1447 | 1448 | var altersingle = function altersingle(alpha, i, b, g, r) { 1449 | 1450 | /* alter hit neuron */ 1451 | var n = network[i]; 1452 | n[0] -= (alpha * (n[0] - b)) / initalpha; 1453 | n[1] -= (alpha * (n[1] - g)) / initalpha; 1454 | n[2] -= (alpha * (n[2] - r)) / initalpha; 1455 | }; 1456 | 1457 | /* 1458 | * Search for biased BGR values ---------------------------- 1459 | */ 1460 | 1461 | var contest = function contest(b, g, r) { 1462 | 1463 | /* finds closest neuron (min dist) and updates freq */ 1464 | /* finds best neuron (min dist-bias) and returns position */ 1465 | /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ 1466 | /* bias[i] = gamma*((1/netsize)-freq[i]) */ 1467 | 1468 | var i; 1469 | var dist; 1470 | var a; 1471 | var biasdist; 1472 | var betafreq; 1473 | var bestpos; 1474 | var bestbiaspos; 1475 | var bestd; 1476 | var bestbiasd; 1477 | var n; 1478 | 1479 | bestd = ~(1 << 31); 1480 | bestbiasd = bestd; 1481 | bestpos = -1; 1482 | bestbiaspos = bestpos; 1483 | 1484 | for (i = 0; i < netsize; i++) { 1485 | n = network[i]; 1486 | dist = n[0] - b; 1487 | if (dist < 0) dist = -dist; 1488 | a = n[1] - g; 1489 | if (a < 0) a = -a; 1490 | dist += a; 1491 | a = n[2] - r; 1492 | if (a < 0) a = -a; 1493 | dist += a; 1494 | 1495 | if (dist < bestd) { 1496 | bestd = dist; 1497 | bestpos = i; 1498 | } 1499 | 1500 | biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); 1501 | 1502 | if (biasdist < bestbiasd) { 1503 | bestbiasd = biasdist; 1504 | bestbiaspos = i; 1505 | } 1506 | 1507 | betafreq = (freq[i] >> betashift); 1508 | freq[i] -= betafreq; 1509 | bias[i] += (betafreq << gammashift); 1510 | } 1511 | 1512 | freq[bestpos] += beta; 1513 | bias[bestpos] -= betagamma; 1514 | return (bestbiaspos); 1515 | }; 1516 | 1517 | NeuQuant.apply(this, arguments); 1518 | return exports; 1519 | }; 1520 | 1521 | 1522 | /***/ } 1523 | /******/ ]) 1524 | }); 1525 | ; -------------------------------------------------------------------------------- /web_modules/sss/index.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["sss"] = factory(); 8 | else 9 | root["sss"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = "/libs/"; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | module.exports = __webpack_require__(2); 58 | 59 | 60 | /***/ }, 61 | /* 1 */, 62 | /* 2 */ 63 | /***/ function(module, exports, __webpack_require__) { 64 | 65 | "use strict"; 66 | var __extends = (this && this.__extends) || function (d, b) { 67 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 68 | function __() { this.constructor = d; } 69 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 70 | }; 71 | var jsfx = __webpack_require__(3); 72 | exports.Preset = jsfx.Preset; 73 | var live; 74 | var random; 75 | var buffers = {}; 76 | var tracks = []; 77 | var schedulingInterval; 78 | var seed; 79 | var playPrefixes = { 80 | c: exports.Preset.Coin, 81 | l: exports.Preset.Laser, 82 | e: exports.Preset.Explosion, 83 | p: exports.Preset.Powerup, 84 | h: exports.Preset.Hit, 85 | j: exports.Preset.Jump, 86 | s: exports.Preset.Select, 87 | u: exports.Preset.Lucky, 88 | }; 89 | var playprefixeArray = values(playPrefixes); 90 | var quantize = 0.5; 91 | var isEmptyPlayed = false; 92 | var prevPlayingFileName; 93 | function init(_seed, tempo, fps) { 94 | if (_seed === void 0) { _seed = 0; } 95 | if (tempo === void 0) { tempo = 120; } 96 | if (fps === void 0) { fps = 60; } 97 | live = jsfx.Live({}); 98 | setVolume(0.1); 99 | seed = _seed; 100 | random = new Random(); 101 | jsfx.setRandomFunc(random.get01); 102 | exports.playInterval = 60 / tempo; 103 | schedulingInterval = 1 / fps * 2; 104 | } 105 | exports.init = init; 106 | function setSeed(_seed) { 107 | if (_seed === void 0) { _seed = 0; } 108 | seed = _seed; 109 | } 110 | exports.setSeed = setSeed; 111 | function play(name, mult, params, volume) { 112 | if (name === void 0) { name = '0'; } 113 | if (mult === void 0) { mult = 2; } 114 | if (params === void 0) { params = null; } 115 | if (volume === void 0) { volume = null; } 116 | if (live == null) { 117 | return; 118 | } 119 | if (buffers[name] != null) { 120 | buffers[name].play(volume); 121 | return; 122 | } 123 | random.setSeed(seed + getHashFromString(name)); 124 | if (params == null) { 125 | var p = playPrefixes[name[0]]; 126 | if (typeof p === 'undefined') { 127 | p = random.sample(playprefixeArray); 128 | } 129 | params = nArray(mult, p); 130 | } 131 | buffers[name] = new Sound(params); 132 | buffers[name].play(volume); 133 | } 134 | exports.play = play; 135 | function setVolume(volume) { 136 | if (live == null) { 137 | return; 138 | } 139 | live._volume.gain.value = volume; 140 | } 141 | exports.setVolume = setVolume; 142 | function setQuantize(_quantize) { 143 | quantize = _quantize; 144 | } 145 | exports.setQuantize = setQuantize; 146 | function playBgm(name, interval, params, tracksNum, volume) { 147 | if (name === void 0) { name = '0'; } 148 | if (interval === void 0) { interval = 0.25; } 149 | if (params === void 0) { params = [exports.Preset.Laser, exports.Preset.Hit]; } 150 | if (tracksNum === void 0) { tracksNum = 8; } 151 | if (volume === void 0) { volume = null; } 152 | if (live == null) { 153 | return; 154 | } 155 | stopBgm(); 156 | random.setSeed(seed + getHashFromString(name)); 157 | tracks = []; 158 | times(tracksNum, function () { return addRandomTrack(interval, params); }); 159 | forEach(tracks, function (t) { return t.play(volume); }); 160 | } 161 | exports.playBgm = playBgm; 162 | function stopBgm() { 163 | if (live == null) { 164 | return; 165 | } 166 | forEach(tracks, function (t) { return t.stop(); }); 167 | } 168 | exports.stopBgm = stopBgm; 169 | function update() { 170 | if (live == null) { 171 | return; 172 | } 173 | var currentTime = live._context.currentTime; 174 | var schedulingTime = currentTime + schedulingInterval; 175 | forOwn(buffers, function (b) { return b.update(currentTime, schedulingTime); }); 176 | forEach(tracks, function (t) { return t.update(currentTime, schedulingTime); }); 177 | return currentTime; 178 | } 179 | exports.update = update; 180 | function reset() { 181 | stopBgm(); 182 | buffers = {}; 183 | tracks = []; 184 | } 185 | exports.reset = reset; 186 | function playEmpty() { 187 | if (live == null) { 188 | return; 189 | } 190 | if (isEmptyPlayed) { 191 | return; 192 | } 193 | var eb = live._createEmptyBuffer(); 194 | live._playBuffer(eb, 0); 195 | isEmptyPlayed = true; 196 | } 197 | exports.playEmpty = playEmpty; 198 | function playParam(param) { 199 | if (live == null) { 200 | return; 201 | } 202 | live._play(param); 203 | } 204 | exports.playParam = playParam; 205 | function addRandomTrack(interval, params) { 206 | addTrack(random.sample(params), createRandomPattern(), interval); 207 | } 208 | function createRandomPattern() { 209 | var len = 64; 210 | var pattern = nArray(len, false); 211 | var pi = 4; 212 | while (pi <= len) { 213 | pattern = reversePattern(pattern, pi); 214 | pi *= 2; 215 | } 216 | return pattern; 217 | } 218 | function reversePattern(pattern, interval) { 219 | var pt = nArray(interval, false); 220 | var pr = 0.5; 221 | for (var i = 0; i < interval / 2; i++) { 222 | if (random.f() < pr) { 223 | pt[random.i(interval - 1)] = true; 224 | } 225 | pr *= 0.5; 226 | } 227 | return map(pattern, function (p, i) { return pt[i % interval] ? !p : p; }); 228 | } 229 | function addTrack(param, pattern, interval) { 230 | if (interval === void 0) { interval = 0.25; } 231 | var track = new Track(param); 232 | track.patternInterval = interval; 233 | if (typeof pattern === 'string') { 234 | track.pattern = mapString(pattern, function (p) { return p === '1'; }); 235 | } 236 | else { 237 | track.pattern = pattern; 238 | } 239 | tracks.push(track); 240 | } 241 | exports.addTrack = addTrack; 242 | var Sound = (function () { 243 | function Sound(params) { 244 | this.isPlaying = false; 245 | this.playedTime = null; 246 | if (!Array.isArray(params)) { 247 | params = [params]; 248 | } 249 | this.buffers = map(params, function (p) { return live._createBuffer(p); }); 250 | this.gainNode = live._createGain(); 251 | } 252 | Sound.prototype.play = function (volume) { 253 | if (volume === void 0) { volume = null; } 254 | this.isPlaying = true; 255 | this.volume = volume; 256 | }; 257 | Sound.prototype.stop = function () { 258 | this.isPlaying = false; 259 | }; 260 | Sound.prototype.update = function (currentTime, schedulingTime) { 261 | if (!this.isPlaying) { 262 | return; 263 | } 264 | this.isPlaying = false; 265 | var interval = exports.playInterval * quantize; 266 | var time = interval > 0 ? 267 | Math.ceil(currentTime / interval) * interval : currentTime; 268 | if (this.playedTime == null || time > this.playedTime) { 269 | this.playLater(time); 270 | this.playedTime = time; 271 | } 272 | }; 273 | Sound.prototype.playLater = function (when) { 274 | var _this = this; 275 | if (this.volume == null) { 276 | forEach(this.buffers, function (b) { return live._playBuffer(b, when); }); 277 | } 278 | else { 279 | this.gainNode.gain.value = this.volume; 280 | forEach(this.buffers, function (b) { return live._playBufferAndConnect(b, when, _this.gainNode); }); 281 | } 282 | }; 283 | return Sound; 284 | }()); 285 | var Track = (function (_super) { 286 | __extends(Track, _super); 287 | function Track() { 288 | _super.apply(this, arguments); 289 | this.patternIndex = 0; 290 | this.patternInterval = 0.25; 291 | this.scheduledTime = null; 292 | } 293 | Track.prototype.update = function (currentTime, schedulingTime) { 294 | if (!this.isPlaying) { 295 | return; 296 | } 297 | if (this.scheduledTime == null) { 298 | this.calcFirstScheduledTime(currentTime); 299 | } 300 | for (var i = 0; i < 99; i++) { 301 | if (this.scheduledTime >= currentTime) { 302 | break; 303 | } 304 | this.calcNextScheduledTime(); 305 | } 306 | if (this.scheduledTime < currentTime) { 307 | this.scheduledTime = null; 308 | } 309 | else { 310 | while (this.scheduledTime <= schedulingTime) { 311 | this.playLater(this.scheduledTime); 312 | this.calcNextScheduledTime(); 313 | } 314 | } 315 | }; 316 | Track.prototype.calcFirstScheduledTime = function (currentTime) { 317 | this.scheduledTime = Math.ceil(currentTime / exports.playInterval) * exports.playInterval - 318 | exports.playInterval * this.patternInterval; 319 | this.patternIndex = 0; 320 | this.calcNextScheduledTime(); 321 | }; 322 | Track.prototype.calcNextScheduledTime = function () { 323 | var pl = this.pattern.length; 324 | var pi = exports.playInterval * this.patternInterval; 325 | for (var i = 0; i < pl; i++) { 326 | this.scheduledTime += pi; 327 | var p = this.pattern[this.patternIndex]; 328 | this.patternIndex++; 329 | if (this.patternIndex >= pl) { 330 | this.patternIndex = 0; 331 | } 332 | if (p) { 333 | break; 334 | } 335 | } 336 | }; 337 | return Track; 338 | }(Sound)); 339 | var Random = (function () { 340 | function Random() { 341 | this.setSeed(); 342 | this.get01 = this.get01.bind(this); 343 | this.f = this.f.bind(this); 344 | this.i = this.i.bind(this); 345 | } 346 | Random.prototype.setSeed = function (v) { 347 | if (v === void 0) { v = -0x7fffffff; } 348 | if (v === -0x7fffffff) { 349 | v = Math.floor(Math.random() * 0x7fffffff); 350 | } 351 | this.x = v = 1812433253 * (v ^ (v >> 30)); 352 | this.y = v = 1812433253 * (v ^ (v >> 30)) + 1; 353 | this.z = v = 1812433253 * (v ^ (v >> 30)) + 2; 354 | this.w = v = 1812433253 * (v ^ (v >> 30)) + 3; 355 | return this; 356 | }; 357 | Random.prototype.f = function (minOrMax, max) { 358 | if (minOrMax === void 0) { minOrMax = null; } 359 | if (max === void 0) { max = null; } 360 | if (minOrMax == null) { 361 | return this.get01(); 362 | } 363 | if (max == null) { 364 | return this.get01() * minOrMax; 365 | } 366 | return this.get01() * (max - minOrMax) + minOrMax; 367 | }; 368 | Random.prototype.i = function (minOrMax, max) { 369 | if (minOrMax === void 0) { minOrMax = null; } 370 | if (max === void 0) { max = null; } 371 | return Math.floor(this.f(minOrMax, max + 1)); 372 | }; 373 | Random.prototype.sample = function (array) { 374 | return array[this.i(array.length - 1)]; 375 | }; 376 | Random.prototype.getInt = function () { 377 | var t = this.x ^ (this.x << 11); 378 | this.x = this.y; 379 | this.y = this.z; 380 | this.z = this.w; 381 | this.w = (this.w ^ (this.w >> 19)) ^ (t ^ (t >> 8)); 382 | return this.w; 383 | }; 384 | Random.prototype.get01 = function () { 385 | return this.getInt() / 0x7fffffff; 386 | }; 387 | return Random; 388 | }()); 389 | function getHashFromString(str) { 390 | var hash = 0; 391 | var len = str.length; 392 | for (var i = 0; i < len; i++) { 393 | var chr = str.charCodeAt(i); 394 | hash = ((hash << 5) - hash) + chr; 395 | hash |= 0; 396 | } 397 | return hash; 398 | } 399 | function values(obj) { 400 | var vs = []; 401 | for (var p in obj) { 402 | if (obj.hasOwnProperty(p)) { 403 | vs.push(obj[p]); 404 | } 405 | } 406 | return vs; 407 | } 408 | function nArray(n, v) { 409 | var a = []; 410 | for (var i = 0; i < n; i++) { 411 | a.push(v); 412 | } 413 | return a; 414 | } 415 | function times(n, func) { 416 | for (var i = 0; i < n; i++) { 417 | func(); 418 | } 419 | } 420 | function forEach(array, func) { 421 | for (var i = 0; i < array.length; i++) { 422 | func(array[i]); 423 | } 424 | } 425 | function forOwn(obj, func) { 426 | for (var p in obj) { 427 | func(obj[p]); 428 | } 429 | } 430 | function map(array, func) { 431 | var result = []; 432 | for (var i = 0; i < array.length; i++) { 433 | result.push(func(array[i], i)); 434 | } 435 | return result; 436 | } 437 | function mapString(str, func) { 438 | var result = []; 439 | for (var i = 0; i < str.length; i++) { 440 | result.push(func(str.charAt(i), i)); 441 | } 442 | return result; 443 | } 444 | 445 | 446 | /***/ }, 447 | /* 3 */ 448 | /***/ function(module, exports) { 449 | 450 | // original ver.: https://github.com/loov/jsfx 451 | // these functions/variables are added by @abagames 452 | // - module.exports 453 | // - Live._createBuffer 454 | // - Live._createEmptyBuffer 455 | // - Live._playBuffer 456 | // - Live._playBufferAndConnect 457 | // - Live._createGain 458 | // - setRandomFunc 459 | // - webkitAudioContext 460 | var jsfx = {}; 461 | (function (jsfx) { 462 | 'use strict'; 463 | 464 | var chr = String.fromCharCode; 465 | var TAU = +Math.PI * 2; 466 | var bitsPerSample = 16 | 0; 467 | var numChannels = 1 | 0; 468 | var sin = Math.sin; 469 | var pow = Math.pow; 470 | var abs = Math.abs; 471 | var EPSILON = 0.000001; 472 | 473 | jsfx.SampleRate = 0 | 0; 474 | jsfx.Sec = 0 | 0; 475 | 476 | jsfx.SetSampleRate = function (sampleRate) { 477 | jsfx.SampleRate = sampleRate | 0; 478 | jsfx.Sec = sampleRate | 0; 479 | }; 480 | jsfx.SetSampleRate(getDefaultSampleRate()); 481 | 482 | // MAIN API 483 | 484 | // Creates a new Audio object based on the params 485 | // params can be a params generating function or the actual parameters 486 | jsfx.Sound = function (params) { 487 | var processor = new Processor(params, jsfx.DefaultModules); 488 | var block = createFloatArray(processor.getSamplesLeft()); 489 | processor.generate(block); 490 | return CreateAudio(block); 491 | }; 492 | 493 | // Same as Sounds, but avoids locking the browser for too long 494 | // in case you have a large amount of sounds to generate 495 | jsfx.Sounds = function (library, ondone, onprogress) { 496 | var audio = {}; 497 | var player = {}; 498 | player._audio = audio; 499 | 500 | var toLoad = []; 501 | 502 | // create playing functions 503 | map_object(library, function (_, name) { 504 | player[name] = function () { 505 | if (typeof audio[name] !== "undefined") { 506 | audio[name].currentTime = 0.0; 507 | audio[name].play(); 508 | } 509 | }; 510 | toLoad.push(name); 511 | }); 512 | 513 | var loaded = 0, total = toLoad.length; 514 | function next() { 515 | if (toLoad.length == 0) { 516 | ondone && ondone(sounds); 517 | return; 518 | } 519 | var name = toLoad.shift(); 520 | audio[name] = jsfx.Sound(library[name]); 521 | loaded++; 522 | onprogress && onprogress(name, loaded, total); 523 | 524 | window.setTimeout(next, 30); 525 | } 526 | next(); 527 | 528 | return player; 529 | } 530 | 531 | // SoundsImmediate takes a named set of params, and generates multiple 532 | // sound objects at once. 533 | jsfx.SoundsImmediate = function (library) { 534 | var audio = {}; 535 | var player = {}; 536 | player._audio = audio; 537 | map_object(library, function (params, name) { 538 | audio[name] = jsfx.Sound(params); 539 | player[name] = function () { 540 | if (typeof audio[name] !== "undefined") { 541 | audio[name].currentTime = 0.0; 542 | audio[name].play(); 543 | } 544 | }; 545 | }) 546 | return player; 547 | }; 548 | 549 | var AudioContext = window.AudioContext || window.webkitAudioContext; 550 | if (typeof AudioContext !== "undefined") { 551 | // Node creates a new AudioContext ScriptProcessor that outputs the 552 | // sound. It will automatically disconnect, unless otherwise specified. 553 | jsfx.Node = function (audioContext, params, modules, bufferSize, stayConnected) { 554 | var node = audioContext.createScriptProcessor(bufferSize, 0, 1); 555 | var gen = new Processor(params, modules || jsfx.DefaultModules); 556 | node.onaudioprocess = function (ev) { 557 | var block = ev.outputBuffer.getChannelData(0); 558 | gen.generate(block); 559 | if (!stayConnected && gen.finished) { 560 | // we need to do an async disconnect, otherwise Chrome may 561 | // glitch 562 | setTimeout(function () { node.disconnect(); }, 30); 563 | } 564 | } 565 | return node; 566 | } 567 | 568 | // Live creates an managed AudioContext for playing. 569 | // This is useful, when you want to use procedurally generated sounds. 570 | jsfx.Live = function (library, modules, BufferSize) { 571 | //TODO: add limit for number of notes played at the same time 572 | BufferSize = BufferSize || 2048; 573 | var player = {}; 574 | 575 | var context = new AudioContext(); 576 | var volume = context.createGain(); 577 | volume.connect(context.destination); 578 | 579 | player._context = context; 580 | player._volume = volume; 581 | 582 | map_object(library, function (params, name) { 583 | player[name] = function () { 584 | var node = jsfx.Node(context, params, modules, BufferSize); 585 | node.connect(volume); 586 | }; 587 | }); 588 | 589 | player._close = function () { 590 | context.close(); 591 | }; 592 | 593 | player._play = function (params) { 594 | var node = jsfx.Node(context, params, modules, BufferSize); 595 | node.connect(volume); 596 | }; 597 | 598 | player._createBuffer = function (params) { 599 | var processor = new Processor(params, jsfx.DefaultModules); 600 | var block = createFloatArray(processor.getSamplesLeft()); 601 | processor.generate(block); 602 | return player._createBufferFromBlock(block); 603 | } 604 | 605 | player._createEmptyBuffer = function () { 606 | return player._createBufferFromBlock([0]); 607 | } 608 | 609 | player._createBufferFromBlock = function (block) { 610 | var buffer = context.createBuffer(1, block.length, jsfx.SampleRate); 611 | var channelData = buffer.getChannelData(0); 612 | channelData.set(block); 613 | return buffer; 614 | } 615 | 616 | function createBufferSource(buffer, when) { 617 | var bufSrc = context.createBufferSource(); 618 | bufSrc.buffer = buffer; 619 | bufSrc.start = bufSrc.start || bufSrc.noteOn; 620 | bufSrc.start(when); 621 | bufSrc.onended = function () { 622 | bufSrc.disconnect(); 623 | }; 624 | return bufSrc; 625 | } 626 | 627 | player._playBuffer = function (buffer, when) { 628 | var bufSrc = createBufferSource(buffer, when); 629 | bufSrc.connect(volume); 630 | } 631 | 632 | player._playBufferAndConnect = function (buffer, when, node) { 633 | var bufSrc = createBufferSource(buffer, when); 634 | bufSrc.connect(node); 635 | node.connect(volume); 636 | }; 637 | 638 | player._createGain = function () { 639 | return context.createGain(); 640 | } 641 | 642 | return player; 643 | } 644 | } else { 645 | //jsfx.Live = jsfx.Sounds; 646 | jsfx.Live = function (library, modules, BufferSize) { 647 | return null; 648 | }; 649 | } 650 | 651 | // SOUND GENERATION 652 | jsfx.Module = {}; 653 | 654 | // generators 655 | jsfx.G = {}; 656 | 657 | var stage = jsfx.stage = { 658 | PhaseSpeed: 0, 659 | PhaseSpeedMod: 10, 660 | Generator: 20, 661 | SampleMod: 30, 662 | Volume: 40 663 | }; 664 | function byStage(a, b) { return a.stage - b.stage; } 665 | 666 | jsfx.InitDefaultParams = InitDefaultParams; 667 | function InitDefaultParams(params, modules) { 668 | // setup modules 669 | for (var i = 0; i < modules.length; i += 1) { 670 | var M = modules[i]; 671 | var P = params[M.name] || {}; 672 | 673 | // add missing parameters 674 | map_object(M.params, function (def, name) { 675 | if (typeof P[name] === 'undefined') { 676 | P[name] = def.D; 677 | } 678 | }); 679 | 680 | params[M.name] = P; 681 | } 682 | } 683 | 684 | // Generates a stateful sound effect processor 685 | // params can be a function that creates a parameter set 686 | jsfx.Processor = Processor; 687 | function Processor(params, modules) { 688 | params = params || {}; 689 | modules = modules || jsfx.DefaultModules; 690 | 691 | if (typeof params === 'function') { 692 | params = params(); 693 | } else { 694 | params = JSON.parse(JSON.stringify(params)) 695 | } 696 | this.finished = false; 697 | 698 | this.state = { 699 | SampleRate: params.SampleRate || jsfx.SampleRate 700 | }; 701 | 702 | // sort modules 703 | modules = modules.slice(); 704 | modules.sort(byStage) 705 | this.modules = modules; 706 | 707 | // init missing params 708 | InitDefaultParams(params, modules); 709 | 710 | // setup modules 711 | for (var i = 0; i < this.modules.length; i += 1) { 712 | var M = this.modules[i]; 713 | this.modules[i].setup(this.state, params[M.name]); 714 | } 715 | } 716 | Processor.prototype = { 717 | //TODO: see whether this can be converted to a module 718 | generate: function (block) { 719 | for (var i = 0 | 0; i < block.length; i += 1) { 720 | block[i] = 0; 721 | } 722 | if (this.finished) { return; } 723 | 724 | var $ = this.state, 725 | N = block.length | 0; 726 | for (var i = 0; i < this.modules.length; i += 1) { 727 | var M = this.modules[i]; 728 | var n = M.process($, block.subarray(0, N)) | 0; 729 | N = Math.min(N, n); 730 | } 731 | if (N < block.length) { 732 | this.finished = true; 733 | } 734 | for (var i = N; i < block.length; i++) { 735 | block[i] = 0; 736 | } 737 | }, 738 | getSamplesLeft: function () { 739 | var samples = 0; 740 | for (var i = 0; i < this.state.envelopes.length; i += 1) { 741 | samples += this.state.envelopes[i].N; 742 | } 743 | if (samples === 0) { 744 | samples = 3 * this.state.SampleRate; 745 | } 746 | return samples; 747 | } 748 | }; 749 | 750 | // Frequency 751 | jsfx.Module.Frequency = { 752 | name: 'Frequency', 753 | params: { 754 | Start: { L: 30, H: 1800, D: 440 }, 755 | 756 | Min: { L: 30, H: 1800, D: 30 }, 757 | Max: { L: 30, H: 1800, D: 1800 }, 758 | 759 | Slide: { L: -1, H: 1, D: 0 }, 760 | DeltaSlide: { L: -1, H: 1, D: 0 }, 761 | 762 | RepeatSpeed: { L: 0, H: 3.0, D: 0 }, 763 | 764 | ChangeAmount: { L: -12, H: 12, D: 0 }, 765 | ChangeSpeed: { L: 0, H: 1, D: 0 } 766 | }, 767 | stage: stage.PhaseSpeed, 768 | setup: function ($, P) { 769 | var SR = $.SampleRate; 770 | 771 | $.phaseParams = P; 772 | 773 | $.phaseSpeed = P.Start * TAU / SR; 774 | $.phaseSpeedMax = P.Max * TAU / SR; 775 | $.phaseSpeedMin = P.Min * TAU / SR; 776 | 777 | $.phaseSpeedMin = Math.min($.phaseSpeedMin, $.phaseSpeed); 778 | $.phaseSpeedMax = Math.max($.phaseSpeedMax, $.phaseSpeed); 779 | 780 | $.phaseSlide = 1.0 + pow(P.Slide, 3.0) * 64.0 / SR; 781 | $.phaseDeltaSlide = pow(P.DeltaSlide, 3.0) / (SR * 1000); 782 | 783 | $.repeatTime = 0; 784 | $.repeatLimit = Infinity; 785 | if (P.RepeatSpeed > 0) { 786 | $.repeatLimit = P.RepeatSpeed * SR; 787 | } 788 | 789 | $.arpeggiatorTime = 0; 790 | $.arpeggiatorLimit = P.ChangeSpeed * SR; 791 | if (P.ChangeAmount == 0) { 792 | $.arpeggiatorLimit = Infinity; 793 | } 794 | $.arpeggiatorMod = 1 + P.ChangeAmount / 12.0; 795 | }, 796 | process: function ($, block) { 797 | var speed = +$.phaseSpeed, 798 | min = +$.phaseSpeedMin, 799 | max = +$.phaseSpeedMax, 800 | slide = +$.phaseSlide, 801 | deltaSlide = +$.phaseDeltaSlide; 802 | 803 | var repeatTime = $.repeatTime, 804 | repeatLimit = $.repeatLimit; 805 | 806 | var arpTime = $.arpeggiatorTime, 807 | arpLimit = $.arpeggiatorLimit, 808 | arpMod = $.arpeggiatorMod; 809 | 810 | for (var i = 0; i < block.length; i++) { 811 | slide += deltaSlide; 812 | speed *= slide; 813 | speed = speed < min ? min : speed > max ? max : speed; 814 | 815 | if (repeatTime > repeatLimit) { 816 | this.setup($, $.phaseParams); 817 | return i + this.process($, block.subarray(i)) - 1; 818 | } 819 | repeatTime++; 820 | 821 | if (arpTime > arpLimit) { 822 | speed *= arpMod; 823 | arpTime = 0; 824 | arpLimit = Infinity; 825 | } 826 | arpTime++; 827 | 828 | block[i] += speed; 829 | } 830 | 831 | $.repeatTime = repeatTime; 832 | $.arpeggiatorTime = arpTime; 833 | $.arpeggiatorLimit = arpLimit; 834 | 835 | $.phaseSpeed = speed; 836 | $.phaseSlide = slide; 837 | 838 | return block.length; 839 | } 840 | }; 841 | 842 | // Vibrato 843 | jsfx.Module.Vibrato = { 844 | name: 'Vibrato', 845 | params: { 846 | Depth: { L: 0, H: 1, D: 0 }, 847 | DepthSlide: { L: -1, H: 1, D: 0 }, 848 | 849 | Frequency: { L: 0.01, H: 48, D: 0 }, 850 | FrequencySlide: { L: -1.00, H: 1, D: 0 } 851 | }, 852 | stage: stage.PhaseSpeedMod, 853 | setup: function ($, P) { 854 | var SR = $.SampleRate; 855 | $.vibratoPhase = 0; 856 | $.vibratoDepth = P.Depth; 857 | $.vibratoPhaseSpeed = P.Frequency * TAU / SR; 858 | 859 | $.vibratoPhaseSpeedSlide = 1.0 + pow(P.FrequencySlide, 3.0) * 3.0 / SR; 860 | $.vibratoDepthSlide = P.DepthSlide / SR; 861 | }, 862 | process: function ($, block) { 863 | var phase = +$.vibratoPhase, 864 | depth = +$.vibratoDepth, 865 | speed = +$.vibratoPhaseSpeed, 866 | slide = +$.vibratoPhaseSpeedSlide, 867 | depthSlide = +$.vibratoDepthSlide; 868 | 869 | if ((depth == 0) && (depthSlide <= 0)) { 870 | return block.length; 871 | } 872 | 873 | for (var i = 0; i < block.length; i++) { 874 | phase += speed; 875 | if (phase > TAU) { phase -= TAU }; 876 | block[i] += block[i] * sin(phase) * depth; 877 | 878 | speed *= slide; 879 | depth += depthSlide; 880 | depth = clamp1(depth); 881 | } 882 | 883 | $.vibratoPhase = phase; 884 | $.vibratoDepth = depth; 885 | $.vibratoPhaseSpeed = speed; 886 | return block.length; 887 | } 888 | }; 889 | 890 | // Generator 891 | jsfx.Module.Generator = { 892 | name: 'Generator', 893 | params: { 894 | // C = choose 895 | Func: { C: jsfx.G, D: 'square' }, 896 | 897 | A: { L: 0, H: 1, D: 0 }, 898 | B: { L: 0, H: 1, D: 0 }, 899 | 900 | ASlide: { L: -1, H: 1, D: 0 }, 901 | BSlide: { L: -1, H: 1, D: 0 } 902 | }, 903 | stage: stage.Generator, 904 | setup: function ($, P) { 905 | $.generatorPhase = 0; 906 | 907 | if (typeof P.Func === 'string') { 908 | $.generator = jsfx.G[P.Func]; 909 | } else { 910 | $.generator = P.Func; 911 | } 912 | if (typeof $.generator === 'object') { 913 | $.generator = $.generator.create(); 914 | } 915 | assert(typeof $.generator === 'function', 'generator must be a function') 916 | 917 | $.generatorA = P.A; 918 | $.generatorASlide = P.ASlide; 919 | $.generatorB = P.B; 920 | $.generatorBSlide = P.BSlide; 921 | }, 922 | process: function ($, block) { 923 | return $.generator($, block); 924 | } 925 | }; 926 | 927 | // Karplus Strong algorithm for string sound 928 | var GuitarBufferSize = 1 << 16; 929 | jsfx.Module.Guitar = { 930 | name: 'Guitar', 931 | params: { 932 | A: { L: 0.0, H: 1.0, D: 1 }, 933 | B: { L: 0.0, H: 1.0, D: 1 }, 934 | C: { L: 0.0, H: 1.0, D: 1 }, 935 | }, 936 | stage: stage.Generator, 937 | setup: function ($, P) { 938 | $.guitarA = P.A; 939 | $.guitarB = P.B; 940 | $.guitarC = P.C; 941 | 942 | $.guitarBuffer = createFloatArray(GuitarBufferSize); 943 | $.guitarHead = 0; 944 | var B = $.guitarBuffer; 945 | for (var i = 0; i < B.length; i++) { 946 | B[i] = random() * 2 - 1; 947 | } 948 | }, 949 | process: function ($, block) { 950 | var BS = GuitarBufferSize, 951 | BM = BS - 1; 952 | 953 | var A = +$.guitarA, B = +$.guitarB, C = +$.guitarC; 954 | var T = A + B + C; 955 | var h = $.guitarHead; 956 | 957 | var buffer = $.guitarBuffer; 958 | for (var i = 0; i < block.length; i++) { 959 | // buffer size 960 | var n = (TAU / block[i]) | 0; 961 | n = n > BS ? BS : n; 962 | 963 | // tail 964 | var t = ((h - n) + BS) & BM; 965 | buffer[h] = 966 | (buffer[(t - 0 + BS) & BM] * A + 967 | buffer[(t - 1 + BS) & BM] * B + 968 | buffer[(t - 2 + BS) & BM] * C) / T; 969 | 970 | block[i] = buffer[h]; 971 | h = (h + 1) & BM; 972 | } 973 | 974 | $.guitarHead = h; 975 | return block.length; 976 | } 977 | } 978 | 979 | // Low/High-Pass Filter 980 | jsfx.Module.Filter = { 981 | name: 'Filter', 982 | params: { 983 | LP: { L: 0, H: 1, D: 1 }, 984 | LPSlide: { L: -1, H: 1, D: 0 }, 985 | LPResonance: { L: 0, H: 1, D: 0 }, 986 | HP: { L: 0, H: 1, D: 0 }, 987 | HPSlide: { L: -1, H: 1, D: 0 } 988 | }, 989 | stage: stage.SampleMod + 0, 990 | setup: function ($, P) { 991 | $.FilterEnabled = (P.HP > EPSILON) || (P.LP < 1 - EPSILON); 992 | 993 | $.LPEnabled = P.LP < 1 - EPSILON; 994 | $.LP = pow(P.LP, 3.0) / 10; 995 | $.LPSlide = 1.0 + P.LPSlide * 100 / $.SampleRate; 996 | $.LPPos = 0; 997 | $.LPPosSlide = 0; 998 | 999 | $.LPDamping = 5.0 / (1.0 + pow(P.LPResonance, 2) * 20) * (0.01 + P.LP); 1000 | $.LPDamping = 1.0 - Math.min($.LPDamping, 0.8); 1001 | 1002 | $.HP = pow(P.HP, 2.0) / 10; 1003 | $.HPPos = 0; 1004 | $.HPSlide = 1.0 + P.HPSlide * 100 / $.SampleRate; 1005 | }, 1006 | enabled: function ($) { 1007 | return $.FilterEnabled; 1008 | }, 1009 | process: function ($, block) { 1010 | if (!this.enabled($)) { return block.length; } 1011 | 1012 | var lp = +$.LP; 1013 | var lpPos = +$.LPPos; 1014 | var lpPosSlide = +$.LPPosSlide; 1015 | var lpSlide = +$.LPSlide; 1016 | var lpDamping = +$.LPDamping; 1017 | var lpEnabled = +$.LPEnabled; 1018 | 1019 | var hp = +$.HP; 1020 | var hpPos = +$.HPPos; 1021 | var hpSlide = +$.HPSlide; 1022 | 1023 | for (var i = 0; i < block.length; i++) { 1024 | if ((hp > EPSILON) || (hp < -EPSILON)) { 1025 | hp *= hpSlide; 1026 | hp = hp < EPSILON ? EPSILON : hp > 0.1 ? 0.1 : hp; 1027 | } 1028 | 1029 | var lpPos_ = lpPos; 1030 | 1031 | lp *= lpSlide; 1032 | lp = lp < 0 ? lp = 0 : lp > 0.1 ? 0.1 : lp; 1033 | 1034 | var sample = block[i]; 1035 | if (lpEnabled) { 1036 | lpPosSlide += (sample - lpPos) * lp; 1037 | lpPosSlide *= lpDamping; 1038 | } else { 1039 | lpPos = sample; 1040 | lpPosSlide = 0; 1041 | } 1042 | lpPos += lpPosSlide; 1043 | 1044 | hpPos += lpPos - lpPos_; 1045 | hpPos *= 1.0 - hp; 1046 | 1047 | block[i] = hpPos; 1048 | } 1049 | 1050 | $.LPPos = lpPos; 1051 | $.LPPosSlide = lpPosSlide; 1052 | $.LP = lp; 1053 | $.HP = hp; 1054 | $.HPPos = hpPos; 1055 | 1056 | return block.length; 1057 | } 1058 | }; 1059 | 1060 | // Phaser Effect 1061 | var PhaserBufferSize = 1 << 10; 1062 | jsfx.Module.Phaser = { 1063 | name: 'Phaser', 1064 | params: { 1065 | Offset: { L: -1, H: 1, D: 0 }, 1066 | Sweep: { L: -1, H: 1, D: 0 } 1067 | }, 1068 | stage: stage.SampleMod + 1, 1069 | setup: function ($, P) { 1070 | $.phaserBuffer = createFloatArray(PhaserBufferSize); 1071 | $.phaserPos = 0; 1072 | $.phaserOffset = pow(P.Offset, 2.0) * (PhaserBufferSize - 4); 1073 | $.phaserOffsetSlide = pow(P.Sweep, 3.0) * 4000 / $.SampleRate; 1074 | }, 1075 | enabled: function ($) { 1076 | return (abs($.phaserOffsetSlide) > EPSILON) || 1077 | (abs($.phaserOffset) > EPSILON); 1078 | }, 1079 | process: function ($, block) { 1080 | if (!this.enabled($)) { return block.length; } 1081 | 1082 | var BS = PhaserBufferSize, 1083 | BM = BS - 1; 1084 | 1085 | var buffer = $.phaserBuffer, 1086 | pos = $.phaserPos | 0, 1087 | offset = +$.phaserOffset, 1088 | offsetSlide = +$.phaserOffsetSlide; 1089 | 1090 | for (var i = 0; i < block.length; i++) { 1091 | offset += offsetSlide; 1092 | //TODO: check whether this is correct 1093 | if (offset < 0) { 1094 | offset = -offset; 1095 | offsetSlide = -offsetSlide; 1096 | } 1097 | if (offset > BM) { 1098 | offset = BM; 1099 | offsetSlide = 0; 1100 | } 1101 | 1102 | buffer[pos] = block[i]; 1103 | var p = (pos - (offset | 0) + BS) & BM; 1104 | block[i] += buffer[p]; 1105 | 1106 | pos = ((pos + 1) & BM) | 0; 1107 | } 1108 | 1109 | $.phaserPos = pos; 1110 | $.phaserOffset = offset; 1111 | return block.length; 1112 | } 1113 | }; 1114 | 1115 | // Volume dynamic control with Attack-Sustain-Decay 1116 | // ATTACK | 0 - Volume + Punch 1117 | // SUSTAIN | Volume + Punch - Volume 1118 | // DECAY | Volume - 0 1119 | jsfx.Module.Volume = { 1120 | name: 'Volume', 1121 | params: { 1122 | Master: { L: 0, H: 1, D: 0.5 }, 1123 | Attack: { L: 0.001, H: 1, D: 0.01 }, 1124 | Sustain: { L: 0, H: 2, D: 0.3 }, 1125 | Punch: { L: 0, H: 3, D: 1.0 }, 1126 | Decay: { L: 0.001, H: 2, D: 1.0 } 1127 | }, 1128 | stage: stage.Volume, 1129 | setup: function ($, P) { 1130 | var SR = $.SampleRate; 1131 | var V = P.Master; 1132 | var VP = V * (1 + P.Punch); 1133 | $.envelopes = [ 1134 | // S = start volume, E = end volume, N = duration in samples 1135 | { S: 0, E: V, N: (P.Attack * SR) | 0 }, // Attack 1136 | { S: VP, E: V, N: (P.Sustain * SR) | 0 }, // Sustain 1137 | { S: V, E: 0, N: (P.Decay * SR) | 0 } // Decay 1138 | ]; 1139 | // G = volume gradient 1140 | for (var i = 0; i < $.envelopes.length; i += 1) { 1141 | var e = $.envelopes[i]; 1142 | e.G = (e.E - e.S) / e.N; 1143 | } 1144 | }, 1145 | process: function ($, block) { 1146 | var i = 0; 1147 | while (($.envelopes.length > 0) && (i < block.length)) { 1148 | var E = $.envelopes[0]; 1149 | var vol = E.S, 1150 | grad = E.G; 1151 | 1152 | var N = Math.min(block.length - i, E.N) | 0; 1153 | var end = (i + N) | 0; 1154 | for (; i < end; i += 1) { 1155 | block[i] *= vol; 1156 | vol += grad; 1157 | vol = clamp(vol, 0, 10); 1158 | } 1159 | E.S = vol; 1160 | E.N -= N; 1161 | if (E.N <= 0) { 1162 | $.envelopes.shift(); 1163 | } 1164 | } 1165 | return i; 1166 | } 1167 | }; 1168 | 1169 | // PRESETS 1170 | 1171 | jsfx.DefaultModules = [ 1172 | jsfx.Module.Frequency, 1173 | jsfx.Module.Vibrato, 1174 | jsfx.Module.Generator, 1175 | jsfx.Module.Filter, 1176 | jsfx.Module.Phaser, 1177 | jsfx.Module.Volume 1178 | ]; 1179 | jsfx.DefaultModules.sort(byStage); 1180 | 1181 | jsfx.EmptyParams = EmptyParams; 1182 | function EmptyParams() { 1183 | return map_object(jsfx.Module, function () { return {} }); 1184 | } 1185 | 1186 | jsfx._RemoveEmptyParams = RemoveEmptyParams; 1187 | function RemoveEmptyParams(params) { 1188 | for (var name in params) { 1189 | if (Object_keys(params[name]).length == 0) { 1190 | delete params[name]; 1191 | } 1192 | } 1193 | }; 1194 | 1195 | jsfx.Preset = { 1196 | Reset: function () { 1197 | return EmptyParams(); 1198 | }, 1199 | Coin: function () { 1200 | var p = EmptyParams(); 1201 | p.Frequency.Start = runif(880, 660); 1202 | p.Volume.Sustain = runif(0.1); 1203 | p.Volume.Decay = runif(0.4, 0.1); 1204 | p.Volume.Punch = runif(0.3, 0.3); 1205 | if (runif() < 0.5) { 1206 | p.Frequency.ChangeSpeed = runif(0.15, 0.1); 1207 | p.Frequency.ChangeAmount = runif(8, 4); 1208 | } 1209 | RemoveEmptyParams(p); 1210 | return p; 1211 | }, 1212 | Laser: function () { 1213 | var p = EmptyParams(); 1214 | p.Generator.Func = rchoose(['square', 'saw', 'sine']); 1215 | 1216 | if (runif() < 0.33) { 1217 | p.Frequency.Start = runif(880, 440); 1218 | p.Frequency.Min = runif(0.1); 1219 | p.Frequency.Slide = runif(0.3, -0.8); 1220 | } else { 1221 | p.Frequency.Start = runif(1200, 440); 1222 | p.Frequency.Min = p.Frequency.Start - runif(880, 440); 1223 | if (p.Frequency.Min < 110) { p.Frequency.Min = 110; } 1224 | p.Frequency.Slide = runif(0.3, -1); 1225 | } 1226 | 1227 | if (runif() < 0.5) { 1228 | p.Generator.A = runif(0.5); 1229 | p.Generator.ASlide = runif(0.2); 1230 | } else { 1231 | p.Generator.A = runif(0.5, 0.4); 1232 | p.Generator.ASlide = runif(0.7); 1233 | } 1234 | 1235 | p.Volume.Sustain = runif(0.2, 0.1); 1236 | p.Volume.Decay = runif(0.4); 1237 | if (runif() < 0.5) { 1238 | p.Volume.Punch = runif(0.3); 1239 | } 1240 | if (runif() < 0.33) { 1241 | p.Phaser.Offset = runif(0.2); 1242 | p.Phaser.Sweep = runif(0.2); 1243 | } 1244 | if (runif() < 0.5) { 1245 | p.Filter.HP = runif(0.3); 1246 | } 1247 | RemoveEmptyParams(p); 1248 | return p; 1249 | }, 1250 | Explosion: function () { 1251 | var p = EmptyParams(); 1252 | p.Generator.Func = 'noise'; 1253 | if (runif() < 0.5) { 1254 | p.Frequency.Start = runif(440, 40); 1255 | p.Frequency.Slide = runif(0.4, -0.1); 1256 | } else { 1257 | p.Frequency.Start = runif(1600, 220); 1258 | p.Frequency.Slide = runif(-0.2, -0.2); 1259 | } 1260 | 1261 | if (runif() < 0.2) { p.Frequency.Slide = 0; } 1262 | if (runif() < 0.3) { p.Frequency.RepeatSpeed = runif(0.5, 0.3); } 1263 | 1264 | p.Volume.Sustain = runif(0.3, 0.1); 1265 | p.Volume.Decay = runif(0.5); 1266 | p.Volume.Punch = runif(0.6, 0.2); 1267 | 1268 | if (runif() < 0.5) { 1269 | p.Phaser.Offset = runif(0.9, -0.3); 1270 | p.Phaser.Sweep = runif(-0.3); 1271 | } 1272 | 1273 | if (runif() < 0.33) { 1274 | p.Frequency.ChangeSpeed = runif(0.3, 0.6); 1275 | p.Frequency.ChangeAmount = runif(24, -12); 1276 | } 1277 | RemoveEmptyParams(p); 1278 | return p; 1279 | }, 1280 | Powerup: function () { 1281 | var p = EmptyParams(); 1282 | if (runif() < 0.5) { 1283 | p.Generator.Func = 'saw'; 1284 | } else { 1285 | p.Generator.A = runif(0.6); 1286 | } 1287 | 1288 | p.Frequency.Start = runif(220, 440); 1289 | if (runif() < 0.5) { 1290 | p.Frequency.Slide = runif(0.5, 0.2); 1291 | p.Frequency.RepeatSpeed = runif(0.4, 0.4); 1292 | } else { 1293 | p.Frequency.Slide = runif(0.2, 0.05); 1294 | if (runif() < 0.5) { 1295 | p.Vibrato.Depth = runif(0.6, 0.1); 1296 | p.Vibrato.Frequency = runif(30, 10); 1297 | } 1298 | } 1299 | 1300 | p.Volume.Sustain = runif(0.4); 1301 | p.Volume.Decay = runif(0.4, 0.1); 1302 | 1303 | RemoveEmptyParams(p); 1304 | return p; 1305 | }, 1306 | Hit: function () { 1307 | var p = EmptyParams(); 1308 | p.Generator.Func = rchoose(['square', 'saw', 'noise']); 1309 | p.Generator.A = runif(0.6); 1310 | p.Generator.ASlide = runif(1, -0.5); 1311 | 1312 | p.Frequency.Start = runif(880, 220); 1313 | p.Frequency.Slide = -runif(0.4, 0.3); 1314 | 1315 | p.Volume.Sustain = runif(0.1); 1316 | p.Volume.Decay = runif(0.2, 0.1); 1317 | 1318 | if (runif() < 0.5) { 1319 | p.Filter.HP = runif(0.3); 1320 | } 1321 | 1322 | RemoveEmptyParams(p); 1323 | return p; 1324 | }, 1325 | Jump: function () { 1326 | var p = EmptyParams(); 1327 | p.Generator.Func = 'square'; 1328 | p.Generator.A = runif(0.6); 1329 | 1330 | p.Frequency.Start = runif(330, 330); 1331 | p.Frequency.Slide = runif(0.4, 0.2); 1332 | 1333 | p.Volume.Sustain = runif(0.3, 0.1); 1334 | p.Volume.Decay = runif(0.2, 0.1); 1335 | 1336 | if (runif() < 0.5) { 1337 | p.Filter.HP = runif(0.3); 1338 | } 1339 | if (runif() < 0.3) { 1340 | p.Filter.LP = runif(-0.6, 1); 1341 | } 1342 | 1343 | RemoveEmptyParams(p); 1344 | return p; 1345 | }, 1346 | Select: function () { 1347 | var p = EmptyParams(); 1348 | p.Generator.Func = rchoose(['square', 'saw']); 1349 | p.Generator.A = runif(0.6); 1350 | 1351 | p.Frequency.Start = runif(660, 220); 1352 | 1353 | p.Volume.Sustain = runif(0.1, 0.1); 1354 | p.Volume.Decay = runif(0.2); 1355 | 1356 | p.Filter.HP = 0.2; 1357 | RemoveEmptyParams(p); 1358 | return p; 1359 | }, 1360 | Lucky: function () { 1361 | var p = EmptyParams(); 1362 | map_object(p, function (out, moduleName) { 1363 | var defs = jsfx.Module[moduleName].params; 1364 | map_object(defs, function (def, name) { 1365 | if (def.C) { 1366 | var values = Object_keys(def.C); 1367 | out[name] = values[(values.length * random()) | 0]; 1368 | } else { 1369 | out[name] = random() * (def.H - def.L) + def.L; 1370 | } 1371 | }); 1372 | }); 1373 | p.Volume.Master = 0.4; 1374 | p.Filter = {}; // disable filter, as it usually will clip everything 1375 | RemoveEmptyParams(p); 1376 | return p; 1377 | } 1378 | }; 1379 | 1380 | // GENERATORS 1381 | 1382 | // uniform noise 1383 | jsfx.G.unoise = newGenerator("sample = Math.random();"); 1384 | // sine wave 1385 | jsfx.G.sine = newGenerator("sample = Math.sin(phase);"); 1386 | // saw wave 1387 | jsfx.G.saw = newGenerator("sample = 2*(phase/TAU - ((phase/TAU + 0.5)|0));"); 1388 | // triangle wave 1389 | jsfx.G.triangle = newGenerator("sample = Math.abs(4 * ((phase/TAU - 0.25)%1) - 2) - 1;"); 1390 | // square wave 1391 | jsfx.G.square = newGenerator("var s = Math.sin(phase); sample = s > A ? 1.0 : s < A ? -1.0 : A;"); 1392 | // simple synth 1393 | jsfx.G.synth = newGenerator("sample = Math.sin(phase) + .5*Math.sin(phase/2) + .3*Math.sin(phase/4);"); 1394 | 1395 | // STATEFUL 1396 | var __noiseLast = 0; 1397 | jsfx.G.noise = newGenerator("if(phase % TAU < 4){__noiseLast = Math.random() * 2 - 1;} sample = __noiseLast;"); 1398 | 1399 | // Karplus-Strong string 1400 | jsfx.G.string = { 1401 | create: function () { 1402 | var BS = 1 << 16; 1403 | var BM = BS - 1; 1404 | 1405 | var buffer = createFloatArray(BS); 1406 | for (var i = 0; i < buffer.length; i++) { 1407 | buffer[i] = random() * 2 - 1; 1408 | } 1409 | 1410 | var head = 0; 1411 | return function ($, block) { 1412 | var TAU = Math.PI * 2; 1413 | var A = +$.generatorA, ASlide = +$.generatorASlide, 1414 | B = +$.generatorB, BSlide = +$.generatorBSlide; 1415 | var buf = buffer; 1416 | 1417 | for (var i = 0; i < block.length; i++) { 1418 | var phaseSpeed = block[i]; 1419 | var n = (TAU / phaseSpeed) | 0; 1420 | A += ASlide; B += BSlide; 1421 | A = A < 0 ? 0 : A > 1 ? 1 : A; 1422 | B = B < 0 ? 0 : B > 1 ? 1 : B; 1423 | 1424 | var t = ((head - n) + BS) & BM; 1425 | var sample = ( 1426 | buf[(t - 0 + BS) & BM] * 1 + 1427 | buf[(t - 1 + BS) & BM] * A + 1428 | buf[(t - 2 + BS) & BM] * B) / (1 + A + B); 1429 | 1430 | buf[head] = sample; 1431 | block[i] = buf[head]; 1432 | head = (head + 1) & BM; 1433 | } 1434 | 1435 | $.generatorA = A; 1436 | $.generatorB = B; 1437 | return block.length; 1438 | } 1439 | } 1440 | }; 1441 | 1442 | // Generates samples using given frequency and generator 1443 | function newGenerator(line) { 1444 | return new Function("$", "block", "" + 1445 | "var TAU = Math.PI * 2;\n" + 1446 | "var sample;\n" + 1447 | "var phase = +$.generatorPhase,\n" + 1448 | " A = +$.generatorA, ASlide = +$.generatorASlide,\n" + 1449 | " B = +$.generatorB, BSlide = +$.generatorBSlide;\n" + 1450 | "\n" + 1451 | "for(var i = 0; i < block.length; i++){\n" + 1452 | " var phaseSpeed = block[i];\n" + 1453 | " phase += phaseSpeed;\n" + 1454 | " if(phase > TAU){ phase -= TAU };\n" + 1455 | " A += ASlide; B += BSlide;\n" + 1456 | " A = A < 0 ? 0 : A > 1 ? 1 : A;\n" + 1457 | " B = B < 0 ? 0 : B > 1 ? 1 : B;\n" + 1458 | line + 1459 | " block[i] = sample;\n" + 1460 | "}\n" + 1461 | "\n" + 1462 | "$.generatorPhase = phase;\n" + 1463 | "$.generatorA = A;\n" + 1464 | "$.generatorB = B;\n" + 1465 | "return block.length;\n" + 1466 | ""); 1467 | } 1468 | 1469 | // WAVE SUPPORT 1470 | 1471 | // Creates an Audio element from audio data [-1.0 .. 1.0] 1472 | jsfx.CreateAudio = CreateAudio; 1473 | function CreateAudio(data) { 1474 | if (typeof Float32Array !== "undefined") { 1475 | assert(data instanceof Float32Array, 'data must be an Float32Array'); 1476 | } 1477 | 1478 | var blockAlign = numChannels * bitsPerSample >> 3; 1479 | var byteRate = jsfx.SampleRate * blockAlign; 1480 | 1481 | var output = createByteArray(8 + 36 + data.length * 2); 1482 | var p = 0; 1483 | 1484 | // emits string to output 1485 | function S(value) { 1486 | for (var i = 0; i < value.length; i += 1) { 1487 | output[p] = value.charCodeAt(i); p++; 1488 | } 1489 | } 1490 | 1491 | // emits integer value to output 1492 | function V(value, nBytes) { 1493 | if (nBytes <= 0) { return; } 1494 | output[p] = value & 0xFF; p++; 1495 | V(value >> 8, nBytes - 1); 1496 | } 1497 | 1498 | S('RIFF'); V(36 + data.length * 2, 4); 1499 | 1500 | S('WAVEfmt '); V(16, 4); V(1, 2); 1501 | V(numChannels, 2); V(jsfx.SampleRate, 4); 1502 | V(byteRate, 4); V(blockAlign, 2); V(bitsPerSample, 2); 1503 | 1504 | S('data'); V(data.length * 2, 4); 1505 | CopyFToU8(output.subarray(p), data); 1506 | 1507 | return new Audio('data:audio/wav;base64,' + U8ToB64(output)); 1508 | }; 1509 | 1510 | jsfx.DownloadAsFile = function (audio) { 1511 | assert(audio instanceof Audio, 'input must be an Audio object'); 1512 | document.location.href = audio.src; 1513 | }; 1514 | 1515 | // HELPERS 1516 | jsfx.Util = {}; 1517 | 1518 | // Copies array of Floats to a Uint8Array with 16bits per sample 1519 | jsfx.Util.CopyFToU8 = CopyFToU8; 1520 | function CopyFToU8(into, floats) { 1521 | assert(into.length / 2 == floats.length, 1522 | 'the target buffer must be twice as large as the iinput'); 1523 | 1524 | var k = 0; 1525 | for (var i = 0; i < floats.length; i++) { 1526 | var v = +floats[i]; 1527 | var a = (v * 0x7FFF) | 0; 1528 | a = a < -0x8000 ? -0x8000 : 0x7FFF < a ? 0x7FFF : a; 1529 | a += a < 0 ? 0x10000 : 0; 1530 | into[k] = a & 0xFF; k++; 1531 | into[k] = a >> 8; k++; 1532 | } 1533 | } 1534 | 1535 | function U8ToB64(data) { 1536 | var CHUNK = 0x8000; 1537 | var result = ''; 1538 | for (var start = 0; start < data.length; start += CHUNK) { 1539 | var end = Math.min(start + CHUNK, data.length); 1540 | result += String.fromCharCode.apply(null, data.subarray(start, end)); 1541 | } 1542 | return btoa(result); 1543 | } 1544 | 1545 | // uses AudioContext sampleRate or 44100; 1546 | function getDefaultSampleRate() { 1547 | if (typeof AudioContext !== 'undefined') { 1548 | return (new AudioContext()).sampleRate; 1549 | } 1550 | return 44100; 1551 | } 1552 | 1553 | // for checking pre/post conditions 1554 | function assert(condition, message) { 1555 | if (!condition) { throw new Error(message); } 1556 | } 1557 | 1558 | function clamp(v, min, max) { 1559 | v = +v; min = +min; max = +max; 1560 | if (v < min) { return +min; } 1561 | if (v > max) { return +max; } 1562 | return +v; 1563 | } 1564 | 1565 | function clamp1(v) { 1566 | v = +v; 1567 | if (v < +0.0) { return +0.0; } 1568 | if (v > +1.0) { return +1.0; } 1569 | return +v; 1570 | } 1571 | 1572 | function map_object(obj, fn) { 1573 | var r = {}; 1574 | for (var name in obj) { 1575 | if (obj.hasOwnProperty(name)) { 1576 | r[name] = fn(obj[name], name); 1577 | } 1578 | } 1579 | return r; 1580 | } 1581 | 1582 | // uniform random 1583 | function runif(scale, offset) { 1584 | var a = random(); 1585 | if (scale !== undefined) 1586 | a *= scale; 1587 | if (offset !== undefined) 1588 | a += offset; 1589 | return a; 1590 | } 1591 | 1592 | function rchoose(gens) { 1593 | return gens[(gens.length * random()) | 0]; 1594 | } 1595 | 1596 | function Object_keys(obj) { 1597 | var r = []; 1598 | for (var name in obj) { r.push(name); } 1599 | return r; 1600 | } 1601 | 1602 | jsfx._createFloatArray = createFloatArray; 1603 | function createFloatArray(N) { 1604 | if (typeof Float32Array === "undefined") { 1605 | var r = new Array(N); 1606 | for (var i = 0; i < r.length; i++) { 1607 | r[i] = 0.0; 1608 | } 1609 | } 1610 | return new Float32Array(N); 1611 | } 1612 | 1613 | function createByteArray(N) { 1614 | if (typeof Uint8Array === "undefined") { 1615 | var r = new Array(N); 1616 | for (var i = 0; i < r.length; i++) { 1617 | r[i] = 0 | 0; 1618 | } 1619 | } 1620 | return new Uint8Array(N); 1621 | } 1622 | 1623 | var randomFunc = Math.random; 1624 | jsfx.setRandomFunc = function (func) { 1625 | randomFunc = func; 1626 | } 1627 | 1628 | function random() { 1629 | return randomFunc(); 1630 | } 1631 | })(jsfx = {}); 1632 | module.exports = jsfx; 1633 | 1634 | 1635 | /***/ } 1636 | /******/ ]) 1637 | }); 1638 | ; --------------------------------------------------------------------------------