From 24afe2c3cee55cdbfba6bac6aa262a5aa7d4a52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 17:47:04 +0200 Subject: [PATCH 01/22] Microphone bars --- src/index.js | 66 ++++++++++++++++++++++++++++++++++++++++---------- src/index.scss | 1 + 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index e3da635..3544e8a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,16 @@ import p5 from 'p5'; - +import 'p5/lib/addons/p5.sound'; import './index.scss'; -function getRandomColorValue() { - return Math.floor(Math.random() * 256); +const VOLUME_NORMALIZATION = 1000; +const THRESHOLD = 10 / VOLUME_NORMALIZATION; + +function getColorValue(volume) { + return Math.floor(volume * VOLUME_NORMALIZATION * 256); +} + +function getWidth(volume) { + return volume * VOLUME_NORMALIZATION; } function getRandomX() { @@ -14,14 +21,47 @@ function getRandomY() { return Math.floor(Math.random() * window.innerHeight); } -const instance = new p5(( sketch ) => { - sketch.setup = () => { - sketch.createCanvas(window.innerWidth, window.innerHeight); - }; - sketch.draw = () => { - sketch.background(0); - sketch.fill(getRandomColorValue()); - sketch.rect(getRandomX(),getRandomY(),50,50); - }; -}); +async function main() { + const mic = new p5.AudioIn(); + mic.start(); + + const instance = new p5(( sketch ) => { + sketch.setup = () => { + sketch.createCanvas(window.innerWidth, window.innerHeight); + }; + + function drawBar(bar) { + sketch.fill(getColorValue(bar.volume)); + sketch.rect(bar.x, 0, getWidth(bar.volume), window.innerHeight); + } + + let bars = []; + + sketch.draw = () => { + sketch.background(0); + + const volume = mic.getLevel(); + const x = getRandomX(); + bars.push({ + volume, + // pitch, + x, + }); + + const newBars = []; + for (const bar of bars) { + bar.volume = bar.volume / 1.5; + if (bar.volume <= THRESHOLD) { + continue; + } + drawBar(bar); + newBars.push(bar); + }; + + bars = newBars; + }; + }); +} + +main(); diff --git a/src/index.scss b/src/index.scss index 4221350..5548e4b 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,4 +1,5 @@ body { margin: 0; overflow: hidden; + transform: rotateY(180deg); } From f30115f4113bcd15a1833dfc88d424ddd5772ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 17:51:11 +0200 Subject: [PATCH 02/22] Changed threshold --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 3544e8a..37bf08e 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ import 'p5/lib/addons/p5.sound'; import './index.scss'; const VOLUME_NORMALIZATION = 1000; -const THRESHOLD = 10 / VOLUME_NORMALIZATION; +const THRESHOLD = 5 / VOLUME_NORMALIZATION; function getColorValue(volume) { return Math.floor(volume * VOLUME_NORMALIZATION * 256); From ede4449de87bcc220301d288e64d507f51ad4c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 18:31:53 +0200 Subject: [PATCH 03/22] Tryna get pitch --- src/index.js | 81 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/src/index.js b/src/index.js index 37bf08e..3dbd84d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,24 @@ import p5 from 'p5'; -import 'p5/lib/addons/p5.sound'; import './index.scss'; -const VOLUME_NORMALIZATION = 1000; -const THRESHOLD = 5 / VOLUME_NORMALIZATION; +const VOLUME_THRESHOLD = 0; +const FFT_SIZE = 1024; function getColorValue(volume) { - return Math.floor(volume * VOLUME_NORMALIZATION * 256); + const normalizedVolume = volume * VOLUME_NORMALIZATION; + const color = Math.floor(normalizedVolume); + const opacity = (normalizedVolume > VOLUME_MAX ? normalizedVolume : VOLUME_MAX) / 100; + return `rgba(${color}, ${color}, ${color}, ${opacity})`; +} + +function getRMS(spectrum) { + let rms = 0; + for (let i = 0; i < spectrum.length; i++) { + rms += spectrum[i] * spectrum[i]; + } + rms /= spectrum.length; + rms = Math.sqrt(rms); + return rms; } function getWidth(volume) { @@ -23,8 +35,38 @@ function getRandomY() { async function main() { - const mic = new p5.AudioIn(); - mic.start(); + const audioCtx = new AudioContext(); + const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); + const analyser = new AnalyserNode(audioCtx); + + let fresh = false; + let spectrum = []; + let volume = 0; + analyser.smoothingTimeConstant = 0.2; + analyser.fftSize = FFT_SIZE; + + const audioProcessor = audioCtx.createScriptProcessor(FFT_SIZE*2, 1, 1); + audioProcessor.onaudioprocess = () => { + fresh = true; + // bitcount returns array which is half the FFT_SIZE + spectrum = new Uint8Array(analyser.frequencyBinCount); + + // getByteFrequencyData returns amplitude for each bin + // analyser.getByteFrequencyData(spectrum); + // getByteTimeDomainData gets volumes over the sample time + // analyser.getByteTimeDomainData(self.spectrum); + + volume = getRMS(spectrum); + // get peak - a hack when our volumes are low + if (volume > VOLUME_THRESHOLD) VOLUME_THRESHOLD = volume; + volume = self.vol; + } + + const input = audioCtx.createMediaStreamSource(microphone); + input.connect(analyser); + analyser.connect(audioProcessor); + // audioProcessor.connect(audioCtx.destination); + const instance = new p5(( sketch ) => { sketch.setup = () => { @@ -39,27 +81,16 @@ async function main() { let bars = []; sketch.draw = () => { + if (!fresh) { + return; + } + + fresh = false; + sketch.background(0); - const volume = mic.getLevel(); - const x = getRandomX(); - bars.push({ - volume, - // pitch, - x, - }); - - const newBars = []; - for (const bar of bars) { - bar.volume = bar.volume / 1.5; - if (bar.volume <= THRESHOLD) { - continue; - } - drawBar(bar); - newBars.push(bar); - }; - - bars = newBars; + for (const pitch of spectrum) { + } }; }); } From 76c97155ceff296978be04c48b3f9b3ec2a232ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 20:07:24 +0200 Subject: [PATCH 04/22] Pitched random location bars --- src/index.js | 73 +++++++++++++++++++++++++++-------------------- webpack.config.js | 7 +---- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/index.js b/src/index.js index 3dbd84d..bd48cca 100644 --- a/src/index.js +++ b/src/index.js @@ -1,15 +1,7 @@ import p5 from 'p5'; import './index.scss'; -const VOLUME_THRESHOLD = 0; -const FFT_SIZE = 1024; - -function getColorValue(volume) { - const normalizedVolume = volume * VOLUME_NORMALIZATION; - const color = Math.floor(normalizedVolume); - const opacity = (normalizedVolume > VOLUME_MAX ? normalizedVolume : VOLUME_MAX) / 100; - return `rgba(${color}, ${color}, ${color}, ${opacity})`; -} +const FFT_SIZE = 256; function getRMS(spectrum) { let rms = 0; @@ -21,8 +13,14 @@ function getRMS(spectrum) { return rms; } -function getWidth(volume) { - return volume * VOLUME_NORMALIZATION; +function getPitch(spectrum) { + let cg = 0; + let weight = 0; + for (let i = 0; i < spectrum.length; i++) { + cg += spectrum[i] * i; + weight += spectrum[i]; + } + return Math.floor(cg / weight); } function getRandomX() { @@ -38,28 +36,34 @@ async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); const analyser = new AnalyserNode(audioCtx); - - let fresh = false; - let spectrum = []; - let volume = 0; analyser.smoothingTimeConstant = 0.2; analyser.fftSize = FFT_SIZE; - const audioProcessor = audioCtx.createScriptProcessor(FFT_SIZE*2, 1, 1); + let fresh = false; + let spectrum = []; + let pitch = 0; + let volume = 0; + let volumeThreshold = 0; + let bars = []; + + const audioProcessor = audioCtx.createScriptProcessor(FFT_SIZE * 2, 1, 1); audioProcessor.onaudioprocess = () => { fresh = true; // bitcount returns array which is half the FFT_SIZE spectrum = new Uint8Array(analyser.frequencyBinCount); // getByteFrequencyData returns amplitude for each bin - // analyser.getByteFrequencyData(spectrum); + analyser.getByteFrequencyData(spectrum); // getByteTimeDomainData gets volumes over the sample time // analyser.getByteTimeDomainData(self.spectrum); + + spectrum.reverse(); - volume = getRMS(spectrum); + const currentVolume = getRMS(spectrum); // get peak - a hack when our volumes are low - if (volume > VOLUME_THRESHOLD) VOLUME_THRESHOLD = volume; - volume = self.vol; + if (currentVolume > volumeThreshold) volumeThreshold = currentVolume; + volume = currentVolume; + pitch = getPitch(spectrum, volume, volumeThreshold); } const input = audioCtx.createMediaStreamSource(microphone); @@ -67,30 +71,37 @@ async function main() { analyser.connect(audioProcessor); // audioProcessor.connect(audioCtx.destination); - const instance = new p5(( sketch ) => { sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); + sketch.colorMode(sketch.HSL, 255); }; - function drawBar(bar) { - sketch.fill(getColorValue(bar.volume)); - sketch.rect(bar.x, 0, getWidth(bar.volume), window.innerHeight); - } - - let bars = []; - sketch.draw = () => { if (!fresh) { return; } fresh = false; + sketch.background(sketch.color(0, 0, 0)); - sketch.background(0); - - for (const pitch of spectrum) { + bars.push({ + x: getRandomX(), + volume, + pitch, + age: 1, + }); + const newBars = []; + for (const bar of bars) { + sketch.fill(sketch.color(0, (bar.volume / volumeThreshold) * 255, 255)); + sketch.rect(bar.x, 0, bar.age * bar.volume * 2, window.innerHeight); + bar.age /= 2; + if (bar.age > 0.1) { + newBars.push(bar); + } } + + bars = newBars; }; }); } diff --git a/webpack.config.js b/webpack.config.js index a5e91ba..5fc1667 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,12 +14,7 @@ module.exports = { use: [ { loader: "style-loader" }, { loader: "css-loader" }, - { - loader: "sass-loader", - options: { - includePaths: ["absolute/path/a", "absolute/path/b"], - }, - }, + { loader: "sass-loader" }, ], }, ], From 41b24dabb29ec39a23314cc2138e0cd079faf9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 20:09:24 +0200 Subject: [PATCH 05/22] Added thresholds --- src/index.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index bd48cca..faf77c0 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import p5 from 'p5'; import './index.scss'; const FFT_SIZE = 256; +const VOLUME_THRESHOLD = 20; function getRMS(spectrum) { let rms = 0; @@ -85,12 +86,15 @@ async function main() { fresh = false; sketch.background(sketch.color(0, 0, 0)); - bars.push({ - x: getRandomX(), - volume, - pitch, - age: 1, - }); + if (volume > VOLUME_THRESHOLD) { + bars.push({ + x: getRandomX(), + volume, + pitch, + age: 1, + }); + } + const newBars = []; for (const bar of bars) { sketch.fill(sketch.color(0, (bar.volume / volumeThreshold) * 255, 255)); From bc3f1e46ca8719120fad9737cef443fe83bf40c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 7 Jun 2019 20:16:27 +0200 Subject: [PATCH 06/22] Fixed lightness --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index faf77c0..9223541 100644 --- a/src/index.js +++ b/src/index.js @@ -97,7 +97,7 @@ async function main() { const newBars = []; for (const bar of bars) { - sketch.fill(sketch.color(0, (bar.volume / volumeThreshold) * 255, 255)); + sketch.fill(sketch.color(0, 0, (bar.volume / volumeThreshold) * 255)); sketch.rect(bar.x, 0, bar.age * bar.volume * 2, window.innerHeight); bar.age /= 2; if (bar.age > 0.1) { From 550712d2e03104c6fb12f8a402d8305270dd7da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 8 Jun 2019 16:29:22 +0200 Subject: [PATCH 07/22] Pitch bars colors --- src/index.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 9223541..635212f 100644 --- a/src/index.js +++ b/src/index.js @@ -43,6 +43,8 @@ async function main() { let fresh = false; let spectrum = []; let pitch = 0; + let minPitch = 90; + let maxPitch = 90; let volume = 0; let volumeThreshold = 0; let bars = []; @@ -62,9 +64,16 @@ async function main() { const currentVolume = getRMS(spectrum); // get peak - a hack when our volumes are low - if (currentVolume > volumeThreshold) volumeThreshold = currentVolume; + if (currentVolume > volumeThreshold) { + volumeThreshold = currentVolume; + } volume = currentVolume; pitch = getPitch(spectrum, volume, volumeThreshold); + if (pitch > maxPitch) { + maxPitch = pitch + } else if (pitch < minPitch) { + minPitch = pitch; + } } const input = audioCtx.createMediaStreamSource(microphone); @@ -97,7 +106,8 @@ async function main() { const newBars = []; for (const bar of bars) { - sketch.fill(sketch.color(0, 0, (bar.volume / volumeThreshold) * 255)); + const normalizedPitchColor = ((bar.pitch - minPitch) / (maxPitch - minPitch)) * 220; + sketch.fill(sketch.color(normalizedPitchColor, bar.volume, (bar.volume / volumeThreshold) * 255)); sketch.rect(bar.x, 0, bar.age * bar.volume * 2, window.innerHeight); bar.age /= 2; if (bar.age > 0.1) { @@ -108,6 +118,10 @@ async function main() { bars = newBars; }; }); + + setTimeout(() => { + // analyser.disconnect(); + }, 5000); } main(); From ee60abc62b8edac07a3e3faf9c8d3421213f97f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 8 Jun 2019 21:14:20 +0200 Subject: [PATCH 08/22] Volume lines --- src/index.js | 88 ++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/src/index.js b/src/index.js index 635212f..ab9a899 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,8 @@ import './index.scss'; const FFT_SIZE = 256; const VOLUME_THRESHOLD = 20; +const LINE_MARGIN = 20; +const MAX_LINES = 60; function getRMS(spectrum) { let rms = 0; @@ -40,20 +42,14 @@ async function main() { analyser.smoothingTimeConstant = 0.2; analyser.fftSize = FFT_SIZE; - let fresh = false; - let spectrum = []; - let pitch = 0; - let minPitch = 90; - let maxPitch = 90; - let volume = 0; - let volumeThreshold = 0; - let bars = []; + let lines = []; + let maxPitch = 0; + let minPitch = 0; const audioProcessor = audioCtx.createScriptProcessor(FFT_SIZE * 2, 1, 1); audioProcessor.onaudioprocess = () => { - fresh = true; // bitcount returns array which is half the FFT_SIZE - spectrum = new Uint8Array(analyser.frequencyBinCount); + const spectrum = new Uint8Array(analyser.frequencyBinCount); // getByteFrequencyData returns amplitude for each bin analyser.getByteFrequencyData(spectrum); @@ -62,24 +58,34 @@ async function main() { spectrum.reverse(); - const currentVolume = getRMS(spectrum); - // get peak - a hack when our volumes are low - if (currentVolume > volumeThreshold) { - volumeThreshold = currentVolume; - } - volume = currentVolume; - pitch = getPitch(spectrum, volume, volumeThreshold); + + const volume = getRMS(spectrum); + const pitch = getPitch(spectrum, volume); + lines.unshift({ + spectrum, + volume, + pitch, + }); + if (pitch > maxPitch) { - maxPitch = pitch - } else if (pitch < minPitch) { + maxPitch = pitch; + } + + if (pitch < minPitch) { minPitch = pitch; } + + if (lines.length > MAX_LINES) { + lines.pop(); + } } const input = audioCtx.createMediaStreamSource(microphone); input.connect(analyser); analyser.connect(audioProcessor); // audioProcessor.connect(audioCtx.destination); + const baseHeight = window.innerHeight * 0.8; + const spectrumPointWidth = window.innerWidth / analyser.frequencyBinCount; const instance = new p5(( sketch ) => { sketch.setup = () => { @@ -87,35 +93,35 @@ async function main() { sketch.colorMode(sketch.HSL, 255); }; + let shouldDraw = 0; + sketch.draw = () => { - if (!fresh) { + shouldDraw += 1; + if (shouldDraw > 0) { + if (shouldDraw > 3) { + shouldDraw = -1; + } return; } - fresh = false; sketch.background(sketch.color(0, 0, 0)); - - if (volume > VOLUME_THRESHOLD) { - bars.push({ - x: getRandomX(), - volume, - pitch, - age: 1, - }); - } - - const newBars = []; - for (const bar of bars) { - const normalizedPitchColor = ((bar.pitch - minPitch) / (maxPitch - minPitch)) * 220; - sketch.fill(sketch.color(normalizedPitchColor, bar.volume, (bar.volume / volumeThreshold) * 255)); - sketch.rect(bar.x, 0, bar.age * bar.volume * 2, window.innerHeight); - bar.age /= 2; - if (bar.age > 0.1) { - newBars.push(bar); + for (let l = 0; l < lines.length; l++) { + const lineBaseHeight = baseHeight - (l * LINE_MARGIN); + const line = lines[l]; + sketch.fill(`rgba(255, 255, 255, 0.2)`); + sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); + sketch.strokeWeight(1); + sketch.beginShape(); + sketch.curveVertex(0, lineBaseHeight); + sketch.curveVertex(0, lineBaseHeight); + for (let i = 1; i < line.spectrum.length - 1; i++) { + const point = line.spectrum[i]; + sketch.curveVertex(i * spectrumPointWidth, lineBaseHeight - point); } + sketch.curveVertex(window.innerWidth, lineBaseHeight); + sketch.curveVertex(window.innerWidth, lineBaseHeight); + sketch.endShape(); } - - bars = newBars; }; }); From 7194376bc8a73e4ee105b85779e0714f422c52a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 8 Jun 2019 21:20:13 +0200 Subject: [PATCH 09/22] Fixed volume lines --- src/index.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index ab9a899..5af581d 100644 --- a/src/index.js +++ b/src/index.js @@ -57,7 +57,6 @@ async function main() { // analyser.getByteTimeDomainData(self.spectrum); spectrum.reverse(); - const volume = getRMS(spectrum); const pitch = getPitch(spectrum, volume); @@ -93,17 +92,7 @@ async function main() { sketch.colorMode(sketch.HSL, 255); }; - let shouldDraw = 0; - sketch.draw = () => { - shouldDraw += 1; - if (shouldDraw > 0) { - if (shouldDraw > 3) { - shouldDraw = -1; - } - return; - } - sketch.background(sketch.color(0, 0, 0)); for (let l = 0; l < lines.length; l++) { const lineBaseHeight = baseHeight - (l * LINE_MARGIN); @@ -112,14 +101,16 @@ async function main() { sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); sketch.strokeWeight(1); sketch.beginShape(); - sketch.curveVertex(0, lineBaseHeight); + sketch.curveVertex(-10, lineBaseHeight); + sketch.curveVertex(-10, lineBaseHeight); sketch.curveVertex(0, lineBaseHeight); for (let i = 1; i < line.spectrum.length - 1; i++) { const point = line.spectrum[i]; sketch.curveVertex(i * spectrumPointWidth, lineBaseHeight - point); } sketch.curveVertex(window.innerWidth, lineBaseHeight); - sketch.curveVertex(window.innerWidth, lineBaseHeight); + sketch.curveVertex(window.innerWidth + 10, lineBaseHeight); + sketch.curveVertex(window.innerWidth + 10, lineBaseHeight); sketch.endShape(); } }; From b8415124114b1276ede83224617b41c5a14ab7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Thu, 13 Jun 2019 21:31:43 +0200 Subject: [PATCH 10/22] Lines horizontal --- src/index.js | 64 ++++++++++++++++++++++++++++---------------------- src/index.scss | 1 - 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/index.js b/src/index.js index 5af581d..a4bbffc 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,7 @@ import './index.scss'; const FFT_SIZE = 256; const VOLUME_THRESHOLD = 20; const LINE_MARGIN = 20; -const MAX_LINES = 60; +const MAX_LINES = window.innerWidth / LINE_MARGIN; function getRMS(spectrum) { let rms = 0; @@ -42,7 +42,8 @@ async function main() { analyser.smoothingTimeConstant = 0.2; analyser.fftSize = FFT_SIZE; - let lines = []; + let line; + let fresh = false; let maxPitch = 0; let minPitch = 0; @@ -60,11 +61,11 @@ async function main() { const volume = getRMS(spectrum); const pitch = getPitch(spectrum, volume); - lines.unshift({ + line = { spectrum, volume, pitch, - }); + }; if (pitch > maxPitch) { maxPitch = pitch; @@ -74,45 +75,52 @@ async function main() { minPitch = pitch; } - if (lines.length > MAX_LINES) { - lines.pop(); - } + fresh = true; } const input = audioCtx.createMediaStreamSource(microphone); input.connect(analyser); analyser.connect(audioProcessor); // audioProcessor.connect(audioCtx.destination); - const baseHeight = window.innerHeight * 0.8; - const spectrumPointWidth = window.innerWidth / analyser.frequencyBinCount; + const baseWidth = 2; + const spectrumPointHeight = window.innerHeight / analyser.frequencyBinCount; const instance = new p5(( sketch ) => { sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); sketch.colorMode(sketch.HSL, 255); + sketch.background(0); }; sketch.draw = () => { - sketch.background(sketch.color(0, 0, 0)); - for (let l = 0; l < lines.length; l++) { - const lineBaseHeight = baseHeight - (l * LINE_MARGIN); - const line = lines[l]; - sketch.fill(`rgba(255, 255, 255, 0.2)`); - sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); - sketch.strokeWeight(1); - sketch.beginShape(); - sketch.curveVertex(-10, lineBaseHeight); - sketch.curveVertex(-10, lineBaseHeight); - sketch.curveVertex(0, lineBaseHeight); - for (let i = 1; i < line.spectrum.length - 1; i++) { - const point = line.spectrum[i]; - sketch.curveVertex(i * spectrumPointWidth, lineBaseHeight - point); - } - sketch.curveVertex(window.innerWidth, lineBaseHeight); - sketch.curveVertex(window.innerWidth + 10, lineBaseHeight); - sketch.curveVertex(window.innerWidth + 10, lineBaseHeight); - sketch.endShape(); + if (!fresh) { + return; } + + fresh = false; + + const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - LINE_MARGIN) * 2, window.innerHeight * 2); + sketch.drawingContext.putImageData(image, LINE_MARGIN * 2, 0); + sketch.noStroke(); + sketch.fill(0); + sketch.rect(0, 0, LINE_MARGIN, window.innerHeight); + + const lineBaseWidth = baseWidth; + sketch.fill(`rgba(255, 255, 255, 0.2)`); + sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); + sketch.strokeWeight(1); + sketch.beginShape(); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, 0); + for (let i = 1; i < line.spectrum.length - 1; i++) { + const point = line.spectrum[i]; + sketch.curveVertex(lineBaseWidth + point, i * spectrumPointHeight); + } + sketch.curveVertex(lineBaseWidth, window.innerHeight); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.endShape(); }; }); diff --git a/src/index.scss b/src/index.scss index 5548e4b..4221350 100644 --- a/src/index.scss +++ b/src/index.scss @@ -1,5 +1,4 @@ body { margin: 0; overflow: hidden; - transform: rotateY(180deg); } From b9483fbcf0a7362a67569c5dd391872646eb4abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Thu, 13 Jun 2019 22:31:04 +0200 Subject: [PATCH 11/22] Started combining --- src/circles.js | 30 ++++++++++++++++++++ src/horizontal-lines.js | 35 +++++++++++++++++++++++ src/index.js | 63 ----------------------------------------- src/squares.js | 37 ++++++++++++++++++++++++ src/util.js | 24 ++++++++++++++++ 5 files changed, 126 insertions(+), 63 deletions(-) create mode 100644 src/circles.js create mode 100644 src/horizontal-lines.js create mode 100644 src/squares.js create mode 100644 src/util.js diff --git a/src/circles.js b/src/circles.js new file mode 100644 index 0000000..19b85db --- /dev/null +++ b/src/circles.js @@ -0,0 +1,30 @@ +const VOLUME_THRESHOLD = 20; +const CIRCLE_RADIUS = 100; +const CIRCLE_POINTS = 256; +const FFT_SIZE = CIRCLE_POINTS; +const MAX_CIRCLES = 20; + +export function setup(sketch, analyser) { + sketch.createCanvas(window.innerWidth, window.innerHeight); + sketch.colorMode(sketch.HSL, 255); +} + +export function draw(sketch) { + sketch.background(30); + sketch.noFill(); + sketch.strokeWeight(2); + sketch.stroke(230); + + for (let c = 0; c < circles.length; c++) { + const circle = circles[c]; + sketch.beginShape(); + for (let i = 0; i < circle.spectrum.length; i++) { + const p = circle.spectrum[i]; + sketch.curveVertex( + Math.cos((i / CIRCLE_POINTS) * 2 * Math.PI) * (CIRCLE_RADIUS + c * 15) + baseWidth + (maxVolume / 2 - circle.volume) + p, + Math.sin((i / CIRCLE_POINTS) * 2 * Math.PI) * (CIRCLE_RADIUS + c * 15) + baseHeight + (maxPitch / 2 - circle.pitch) - p, + ); + } + sketch.endShape(sketch.CLOSE); + } +} diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js new file mode 100644 index 0000000..1fc0b3b --- /dev/null +++ b/src/horizontal-lines.js @@ -0,0 +1,35 @@ +const VOLUME_THRESHOLD = 20; +const LINE_MARGIN = 20; +const MAX_LINES = window.innerWidth / LINE_MARGIN; +const FFT_SIZE = 256; + +export function setup(sketch, analyser) { + sketch.colorMode(sketch.HSL, 255); + sketch.background(0); + analyser.fftSize = FFT_SIZE; +} + +export function draw(sketch) { + const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - LINE_MARGIN) * 2, window.innerHeight * 2); + sketch.drawingContext.putImageData(image, LINE_MARGIN * 2, 0); + sketch.noStroke(); + sketch.fill(0); + sketch.rect(0, 0, LINE_MARGIN, window.innerHeight); + + const lineBaseWidth = baseWidth; + sketch.fill(`rgba(255, 255, 255, 0.2)`); + sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); + sketch.strokeWeight(1); + sketch.beginShape(); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, 0); + for (let i = 1; i < line.spectrum.length - 1; i++) { + const point = line.spectrum[i]; + sketch.curveVertex(lineBaseWidth + point, i * spectrumPointHeight); + } + sketch.curveVertex(lineBaseWidth, window.innerHeight); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.endShape(); +} diff --git a/src/index.js b/src/index.js index a4bbffc..45a33a8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,46 +1,11 @@ import p5 from 'p5'; import './index.scss'; -const FFT_SIZE = 256; -const VOLUME_THRESHOLD = 20; -const LINE_MARGIN = 20; -const MAX_LINES = window.innerWidth / LINE_MARGIN; - -function getRMS(spectrum) { - let rms = 0; - for (let i = 0; i < spectrum.length; i++) { - rms += spectrum[i] * spectrum[i]; - } - rms /= spectrum.length; - rms = Math.sqrt(rms); - return rms; -} - -function getPitch(spectrum) { - let cg = 0; - let weight = 0; - for (let i = 0; i < spectrum.length; i++) { - cg += spectrum[i] * i; - weight += spectrum[i]; - } - return Math.floor(cg / weight); -} - -function getRandomX() { - return Math.floor(Math.random() * window.innerWidth); -} - -function getRandomY() { - return Math.floor(Math.random() * window.innerHeight); -} - - async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); const analyser = new AnalyserNode(audioCtx); analyser.smoothingTimeConstant = 0.2; - analyser.fftSize = FFT_SIZE; let line; let fresh = false; @@ -88,8 +53,6 @@ async function main() { const instance = new p5(( sketch ) => { sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); - sketch.colorMode(sketch.HSL, 255); - sketch.background(0); }; sketch.draw = () => { @@ -99,34 +62,8 @@ async function main() { fresh = false; - const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - LINE_MARGIN) * 2, window.innerHeight * 2); - sketch.drawingContext.putImageData(image, LINE_MARGIN * 2, 0); - sketch.noStroke(); - sketch.fill(0); - sketch.rect(0, 0, LINE_MARGIN, window.innerHeight); - - const lineBaseWidth = baseWidth; - sketch.fill(`rgba(255, 255, 255, 0.2)`); - sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); - sketch.strokeWeight(1); - sketch.beginShape(); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, 0); - for (let i = 1; i < line.spectrum.length - 1; i++) { - const point = line.spectrum[i]; - sketch.curveVertex(lineBaseWidth + point, i * spectrumPointHeight); - } - sketch.curveVertex(lineBaseWidth, window.innerHeight); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); - sketch.endShape(); }; }); - - setTimeout(() => { - // analyser.disconnect(); - }, 5000); } main(); diff --git a/src/squares.js b/src/squares.js new file mode 100644 index 0000000..97a8293 --- /dev/null +++ b/src/squares.js @@ -0,0 +1,37 @@ +const VOLUME_THRESHOLD = 20; +const GRID_SIZE = 20; +const MAX_WIDTH = 5; +const MAX_HEIGHT = 8; +const FFT_SIZE = 1024; + +export function setup(sketch, analyser) { + sketch.background('rgb(20, 30, 30)'); + sketch.colorMode(sketch.HSB); + analyser.fftSize = FFT_SIZE; +} + +export function draw(sketch) { + sketch.background('rgba(20, 30, 30, 0.01)'); + if (!circle) { + return; + } + + const color = ((circle.pitch - minPitch) / (maxPitch - minPitch)) * 360; + const brightness = ((circle.volume - minVolume) / (maxVolume - minVolume)); + sketch.stroke(color, 100, 100, brightness); + for (let i = 0; i < circle.spectrum.length + 2; i++) { + const p = circle.spectrum[i - 2] * i * i * i * i / 2; + const x = getRandomBetween(0, Math.floor(window.innerWidth / GRID_SIZE)) * GRID_SIZE; + const y = getRandomBetween(0, Math.floor(window.innerHeight / GRID_SIZE)) * GRID_SIZE; + // const width = getRandomBetween(1, MAX_WIDTH) * GRID_SIZE; + // const height = getRandomBetween(1, MAX_HEIGHT) * GRID_SIZE; + sketch.fill(color, 100, 100, brightness * 0.5); + sketch.beginShape(); + sketch.vertex(x, y); + sketch.vertex(x, y + GRID_SIZE); + sketch.vertex(x + GRID_SIZE, y + GRID_SIZE); + sketch.vertex(x + GRID_SIZE, y); + sketch.vertex(x, y); + sketch.endShape(); + } +} diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..e3189e4 --- /dev/null +++ b/src/util.js @@ -0,0 +1,24 @@ +export function getRMS(spectrum) { + let rms = 0; + for (let i = 0; i < spectrum.length; i++) { + rms += spectrum[i] * spectrum[i]; + } + rms /= spectrum.length; + rms = Math.sqrt(rms); + return rms; +} + +export function getPitch(spectrum) { + let cg = 0; + let weight = 0; + for (let i = 0; i < spectrum.length; i++) { + cg += spectrum[i] * i; + weight += spectrum[i]; + } + return Math.floor(cg / weight); +} + +function getRandomBetween(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + From 5389f22ff21789c3615ffa47cd9a227d131850b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 12:45:35 +0200 Subject: [PATCH 12/22] More work on combined state --- package-lock.json | 22 +++++++ package.json | 3 +- src/horizontal-lines.js | 123 ++++++++++++++++++++++++++++++---------- src/index.js | 63 +++++--------------- src/sound.worker.js | 61 ++++++++++++++++++++ webpack.config.js | 4 ++ 6 files changed, 194 insertions(+), 82 deletions(-) create mode 100644 src/sound.worker.js diff --git a/package-lock.json b/package-lock.json index bbbfcf4..3ba06de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6996,6 +6996,28 @@ "errno": "~0.1.7" } }, + "worker-loader": { + "version": "2.0.0", + "resolved": "http://localhost:4873/worker-loader/-/worker-loader-2.0.0.tgz", + "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==", + "dev": true, + "requires": { + "loader-utils": "^1.0.0", + "schema-utils": "^0.4.0" + }, + "dependencies": { + "schema-utils": { + "version": "0.4.7", + "resolved": "http://localhost:4873/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "http://localhost:4873/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index bc23f6d..77c657a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "style-loader": "^0.23.1", "webpack": "^4.33.0", "webpack-cli": "^3.3.3", - "webpack-dev-server": "^3.7.1" + "webpack-dev-server": "^3.7.1", + "worker-loader": "^2.0.0" } } diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index 1fc0b3b..b27f260 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -1,35 +1,96 @@ -const VOLUME_THRESHOLD = 20; -const LINE_MARGIN = 20; -const MAX_LINES = window.innerWidth / LINE_MARGIN; -const FFT_SIZE = 256; +import Worker from './sound.worker.js'; +import { getRMS, getPitch } from './util.js'; -export function setup(sketch, analyser) { - sketch.colorMode(sketch.HSL, 255); - sketch.background(0); - analyser.fftSize = FFT_SIZE; -} +export default class HorizontalLines { + VOLUME_THRESHOLD = 20; + LINE_MARGIN = 20; + MAX_LINES = window.innerWidth / LINE_MARGIN; + FFT_SIZE = 256; + BASE_WIDTH = 2; -export function draw(sketch) { - const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - LINE_MARGIN) * 2, window.innerHeight * 2); - sketch.drawingContext.putImageData(image, LINE_MARGIN * 2, 0); - sketch.noStroke(); - sketch.fill(0); - sketch.rect(0, 0, LINE_MARGIN, window.innerHeight); - - const lineBaseWidth = baseWidth; - sketch.fill(`rgba(255, 255, 255, 0.2)`); - sketch.stroke(((line.pitch - minPitch) / (maxPitch - minPitch)) * 255); - sketch.strokeWeight(1); - sketch.beginShape(); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, 0); - for (let i = 1; i < line.spectrum.length - 1; i++) { - const point = line.spectrum[i]; - sketch.curveVertex(lineBaseWidth + point, i * spectrumPointHeight); + spectrumPointHeight = 20; + + line; + fresh; + maxPitch; + minPitch; + + worker; + analyser; + audioProcessor; + + constructor(audioCtx) { + this.worker = new Worker(); + this.analyser = new AnalyserNode(audioCtx); + this.analyser.fftSize = FFT_SIZE; + this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); + audioProcessor.onaudioprocess = this.audioProcess; + this.analyser.connect(this.audioProcessor); + } + + setup(sketch) { + sketch.colorMode(sketch.HSL, 255); + sketch.background(0); + this.resize(); + } + + resize() { + this.spectrumPointHeight = window.innerHeight / this.analyser.frequencyBinCount; + } + + audioProcess() { + const spectrum = new Uint8Array(this.analyser.frequencyBinCount); + this.analyser.getByteFrequencyData(spectrum); + + spectrum.reverse(); + + const volume = getRMS(spectrum); + const pitch = getPitch(spectrum, volume); + this.line = { + spectrum, + volume, + pitch, + }; + + if (pitch > this.maxPitch) { + this.maxPitch = pitch; + } + + if (pitch < this.minPitch) { + this.minPitch = pitch; + } + + this.fresh = true; + } + + draw(sketch) { + if (!this.fresh) { + return; + } + + this.fresh = false; + + const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - this.LINE_MARGIN) * 2, window.innerHeight * 2); + sketch.drawingContext.putImageData(image, this.LINE_MARGIN * 2, 0); + sketch.noStroke(); + sketch.fill(0); + sketch.rect(0, 0, this.LINE_MARGIN, window.innerHeight); + + const lineBaseWidth = this.BASE_WIDTH; + sketch.fill(`rgba(255, 255, 255, 0.2)`); + sketch.stroke(((this.line.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 255); + sketch.strokeWeight(1); + sketch.beginShape(); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, -10); + sketch.curveVertex(lineBaseWidth, 0); + for (let i = 1; i < this.line.spectrum.length - 1; i++) { + const point = this.line.spectrum[i]; + sketch.curveVertex(lineBaseWidth + point, i * this.spectrumPointHeight); + } + sketch.curveVertex(lineBaseWidth, window.innerHeight); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.endShape(); } - sketch.curveVertex(lineBaseWidth, window.innerHeight); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); - sketch.endShape(); } diff --git a/src/index.js b/src/index.js index 45a33a8..4f4a751 100644 --- a/src/index.js +++ b/src/index.js @@ -1,68 +1,31 @@ import p5 from 'p5'; import './index.scss'; +import HorizontalLines from './horizontal-lines.js'; + async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); - const analyser = new AnalyserNode(audioCtx); - analyser.smoothingTimeConstant = 0.2; - - let line; - let fresh = false; - let maxPitch = 0; - let minPitch = 0; - - const audioProcessor = audioCtx.createScriptProcessor(FFT_SIZE * 2, 1, 1); - audioProcessor.onaudioprocess = () => { - // bitcount returns array which is half the FFT_SIZE - const spectrum = new Uint8Array(analyser.frequencyBinCount); - - // getByteFrequencyData returns amplitude for each bin - analyser.getByteFrequencyData(spectrum); - // getByteTimeDomainData gets volumes over the sample time - // analyser.getByteTimeDomainData(self.spectrum); - - spectrum.reverse(); - - const volume = getRMS(spectrum); - const pitch = getPitch(spectrum, volume); - line = { - spectrum, - volume, - pitch, - }; - - if (pitch > maxPitch) { - maxPitch = pitch; - } - - if (pitch < minPitch) { - minPitch = pitch; - } - - fresh = true; - } - const input = audioCtx.createMediaStreamSource(microphone); - input.connect(analyser); - analyser.connect(audioProcessor); - // audioProcessor.connect(audioCtx.destination); - const baseWidth = 2; - const spectrumPointHeight = window.innerHeight / analyser.frequencyBinCount; + + let activeAnimation; const instance = new p5(( sketch ) => { sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); + + const horizontalLines = new HorizontalLines(analyser, sketch); + activeAnimation = horizontalLines; + input.connect(activeAnimation.analyser); }; sketch.draw = () => { - if (!fresh) { - return; - } - - fresh = false; - + activeAnimation.draw(sketch); }; + + setTimeout(() => { + activactiveAnimation = + }, 1 * 60 * 1000); }); } diff --git a/src/sound.worker.js b/src/sound.worker.js new file mode 100644 index 0000000..d323cd6 --- /dev/null +++ b/src/sound.worker.js @@ -0,0 +1,61 @@ +const PERIOD = 60 * 1000; +const data = []; +let firstRun = true; + +function onDataPoint(event) { + console.log(event); +} + +self.addEventListener('message', onDataPoint); + +function calculateAndSend() { + firstRun = false; + + let averagePitch = 0; + let averageVolume = 0; + let averageSpectrum = []; + + let maxPitch = 0; + let minPitch = spectrum.length; + let maxVolume = 0; + let minVolume = 500; + + for (const { volume, pitch, spectrum } in data) { + if (pitch > maxPitch) { + maxPitch = pitch; + } else if (pitch < minPitch) { + minPitch = pitch; + } + + if (volume > maxVolume) { + maxVolume = volume; + } else if (volume < minVolume) { + minVolume = volume; + } + + averageVolume += volume; + averagePitch += pitch; + + for (let i = 0; i < spectrum.length; i++) { + averageSpectrum[i] += spectrum[i]; + } + } + + averageVolume /= data.length; + averagePitch /= data.length; + averageSpectrum = averageSpectrum.map(p => p / spectrum.length); + + self.postMessage({ + minVolume, + averageVolume, + maxVolume, + + minPitch, + averagePitch, + maxPitch, + + averageSpectrum, + }); +} + +setInterval(calculateAndSend, PERIOD); diff --git a/webpack.config.js b/webpack.config.js index 5fc1667..3fe4f49 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,6 +17,10 @@ module.exports = { { loader: "sass-loader" }, ], }, + { + test: /\.worker\.js$/, + use: { loader: 'worker-loader' }, + }, ], }, plugins: [ From add2c28363154dd47297b47afbe9eec073423e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 16:33:22 +0200 Subject: [PATCH 13/22] Average --- package-lock.json | 1083 +++++++++++++++++++++++++++++++++++++++ package.json | 6 + src/horizontal-lines.js | 71 +-- src/index.js | 26 +- src/sound.worker.js | 23 +- src/squares.js | 124 +++-- src/util.js | 29 +- webpack.config.js | 20 +- 8 files changed, 1289 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ba06de..50e86ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,845 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fcode-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fcore/-/core-7.4.5.tgz", + "integrity": "sha512-OvjIh6aqXtlsA8ujtGKfC7LYWksYSX8yQcM8Ay3LuvVeQ63lcOKgoZWVqcpFwkd29aYU9rVx7jxhfhiEDV9MZA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helpers": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.5", + "@babel/types": "^7.4.4", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "http://localhost:4873/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "http://localhost:4873/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "http://localhost:4873/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fgenerator/-/generator-7.4.4.tgz", + "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "http://localhost:4873/@babel%2fhelper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz", + "integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4" + } + }, + "@babel/helper-define-map": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-define-map/-/helper-define-map-7.4.4.tgz", + "integrity": "sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "http://localhost:4873/@babel%2fhelper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "http://localhost:4873/@babel%2fhelper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", + "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-module-transforms/-/helper-module-transforms-7.4.4.tgz", + "integrity": "sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.4.4", + "lodash": "^4.17.11" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-regex/-/helper-regex-7.4.4.tgz", + "integrity": "sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "http://localhost:4873/@babel%2fhelper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-replace-supers/-/helper-replace-supers-7.4.4.tgz", + "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "http://localhost:4873/@babel%2fhelper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fhelper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fhelpers/-/helpers-7.4.4.tgz", + "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "http://localhost:4873/@babel%2fhighlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fparser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz", + "integrity": "sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-json-strings": "^7.2.0" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", + "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz", + "integrity": "sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", + "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz", + "integrity": "sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.11" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz", + "integrity": "sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-define-map": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.4.4", + "@babel/helper-split-export-declaration": "^7.4.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", + "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", + "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", + "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", + "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz", + "integrity": "sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz", + "integrity": "sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", + "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", + "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", + "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.1.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", + "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", + "dev": true, + "requires": { + "regexp-tree": "^0.1.6" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", + "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", + "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz", + "integrity": "sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-regenerator/-/plugin-transform-regenerator-7.4.5.tgz", + "integrity": "sha512-gBKRh5qAaCWntnd09S8QC7r3auLCqq5DI6O0DlfoyDjslSBVqBibrMdsqO+Uhmx3+BlOmE/Kw1HFxmGbv0N9dA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz", + "integrity": "sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-runtime/-/plugin-transform-runtime-7.4.4.tgz", + "integrity": "sha512-aMVojEjPszvau3NRg+TIH14ynZLvPewH4xhlCW1w6A3rkxTS1m4uwzRclYR9oS+rl/dr+kT+pzbfHuAWP/lc7Q==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.2.2", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2fplugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", + "integrity": "sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + } + }, + "@babel/preset-env": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fpreset-env/-/preset-env-7.4.5.tgz", + "integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-json-strings": "^7.2.0", + "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-json-strings": "^7.2.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-arrow-functions": "^7.2.0", + "@babel/plugin-transform-async-to-generator": "^7.4.4", + "@babel/plugin-transform-block-scoped-functions": "^7.2.0", + "@babel/plugin-transform-block-scoping": "^7.4.4", + "@babel/plugin-transform-classes": "^7.4.4", + "@babel/plugin-transform-computed-properties": "^7.2.0", + "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-exponentiation-operator": "^7.2.0", + "@babel/plugin-transform-for-of": "^7.4.4", + "@babel/plugin-transform-function-name": "^7.4.4", + "@babel/plugin-transform-literals": "^7.2.0", + "@babel/plugin-transform-member-expression-literals": "^7.2.0", + "@babel/plugin-transform-modules-amd": "^7.2.0", + "@babel/plugin-transform-modules-commonjs": "^7.4.4", + "@babel/plugin-transform-modules-systemjs": "^7.4.4", + "@babel/plugin-transform-modules-umd": "^7.2.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", + "@babel/plugin-transform-new-target": "^7.4.4", + "@babel/plugin-transform-object-super": "^7.2.0", + "@babel/plugin-transform-parameters": "^7.4.4", + "@babel/plugin-transform-property-literals": "^7.2.0", + "@babel/plugin-transform-regenerator": "^7.4.5", + "@babel/plugin-transform-reserved-words": "^7.2.0", + "@babel/plugin-transform-shorthand-properties": "^7.2.0", + "@babel/plugin-transform-spread": "^7.2.0", + "@babel/plugin-transform-sticky-regex": "^7.2.0", + "@babel/plugin-transform-template-literals": "^7.4.4", + "@babel/plugin-transform-typeof-symbol": "^7.2.0", + "@babel/plugin-transform-unicode-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "browserslist": "^4.6.0", + "core-js-compat": "^3.1.1", + "invariant": "^2.2.2", + "js-levenshtein": "^1.1.3", + "semver": "^5.5.0" + } + }, + "@babel/runtime": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2fruntime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2ftemplate/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.4.5", + "resolved": "http://localhost:4873/@babel%2ftraverse/-/traverse-7.4.5.tgz", + "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.4", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.4.5", + "@babel/types": "^7.4.4", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "http://localhost:4873/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "http://localhost:4873/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.4.4", + "resolved": "http://localhost:4873/@babel%2ftypes/-/types-7.4.4.tgz", + "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, "@types/events": { "version": "3.0.0", "resolved": "http://localhost:4873/@types%2fevents/-/events-3.0.0.tgz", @@ -495,6 +1334,18 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "babel-loader": { + "version": "8.0.6", + "resolved": "http://localhost:4873/babel-loader/-/babel-loader-8.0.6.tgz", + "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1", + "pify": "^4.0.1" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "http://localhost:4873/balanced-match/-/balanced-match-1.0.0.tgz", @@ -772,6 +1623,17 @@ "pako": "~1.0.5" } }, + "browserslist": { + "version": "4.6.2", + "resolved": "http://localhost:4873/browserslist/-/browserslist-4.6.2.tgz", + "integrity": "sha512-2neU/V0giQy9h3XMPwLhEY3+Ao0uHSwHvU8Q1Ea6AgLVL1sXbX3dzPrJ8NWe5Hi4PoTkCYXOtVR9rfRLI0J/8Q==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000974", + "electron-to-chromium": "^1.3.150", + "node-releases": "^1.1.23" + } + }, "buffer": { "version": "4.9.1", "resolved": "http://localhost:4873/buffer/-/buffer-4.9.1.tgz", @@ -886,6 +1748,12 @@ } } }, + "caniuse-lite": { + "version": "1.0.30000974", + "resolved": "http://localhost:4873/caniuse-lite/-/caniuse-lite-1.0.30000974.tgz", + "integrity": "sha512-xc3rkNS/Zc3CmpMKuczWEdY2sZgx09BkAxfvkxlAEBTqcMHeL8QnPqhKse+5sRTi3nrw2pJwToD2WvKn1Uhvww==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "http://localhost:4873/caseless/-/caseless-0.12.0.tgz", @@ -1181,6 +2049,15 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "http://localhost:4873/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cookie": { "version": "0.4.0", "resolved": "http://localhost:4873/cookie/-/cookie-0.4.0.tgz", @@ -1213,6 +2090,31 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js-compat": { + "version": "3.1.3", + "resolved": "http://localhost:4873/core-js-compat/-/core-js-compat-3.1.3.tgz", + "integrity": "sha512-EP018pVhgwsKHz3YoN1hTq49aRe+h017Kjz0NQz3nXV0cCRMvH3fLQl+vEPGr4r4J5sk4sU3tUC7U1aqTCeJeA==", + "dev": true, + "requires": { + "browserslist": "^4.6.0", + "core-js-pure": "3.1.3", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.1.1", + "resolved": "http://localhost:4873/semver/-/semver-6.1.1.tgz", + "integrity": "sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==", + "dev": true + } + } + }, + "core-js-pure": { + "version": "3.1.3", + "resolved": "http://localhost:4873/core-js-pure/-/core-js-pure-3.1.3.tgz", + "integrity": "sha512-k3JWTrcQBKqjkjI0bkfXS0lbpWPxYuHWfMMjC1VDmzU4Q58IwSbuXSo99YO/hUHlw/EB4AlfA2PVxOGkrIq6dA==", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "http://localhost:4873/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1623,6 +2525,12 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "electron-to-chromium": { + "version": "1.3.159", + "resolved": "http://localhost:4873/electron-to-chromium/-/electron-to-chromium-1.3.159.tgz", + "integrity": "sha512-bhiEr8/A97GUBcUzNb9MFNhzQOjakbKmEKBEAa6UMY45zG2e8PM63LOgAPXEJE9bQiaQH6nOdYiYf8X821tZjQ==", + "dev": true + }, "elliptic": { "version": "6.4.1", "resolved": "http://localhost:4873/elliptic/-/elliptic-6.4.1.tgz", @@ -1756,6 +2664,12 @@ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", "dev": true }, + "esutils": { + "version": "2.0.2", + "resolved": "http://localhost:4873/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "http://localhost:4873/etag/-/etag-1.8.1.tgz", @@ -2910,6 +3824,12 @@ "which": "^1.2.14" } }, + "globals": { + "version": "11.12.0", + "resolved": "http://localhost:4873/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, "globby": { "version": "6.1.0", "resolved": "http://localhost:4873/globby/-/globby-6.1.0.tgz", @@ -3381,6 +4301,15 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, + "invariant": { + "version": "2.2.4", + "resolved": "http://localhost:4873/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, "invert-kv": { "version": "2.0.0", "resolved": "http://localhost:4873/invert-kv/-/invert-kv-2.0.0.tgz", @@ -3664,12 +4593,30 @@ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "dev": true }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "http://localhost:4873/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "http://localhost:4873/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "jsbn": { "version": "0.1.1", "resolved": "http://localhost:4873/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "dev": true }, + "jsesc": { + "version": "2.5.2", + "resolved": "http://localhost:4873/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "http://localhost:4873/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3808,6 +4755,15 @@ "integrity": "sha512-Jt2MHrCNdtIe1W6co3tF5KXGRkzF+TYffiQstfXa04mrss9IKXzAAXYWak8LbZseAQY03sH2GzMCMU0ZOUc9bg==", "dev": true }, + "loose-envify": { + "version": "1.4.0", + "resolved": "http://localhost:4873/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "loud-rejection": { "version": "1.6.0", "resolved": "http://localhost:4873/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -4267,6 +5223,15 @@ } } }, + "node-releases": { + "version": "1.1.23", + "resolved": "http://localhost:4873/node-releases/-/node-releases-1.1.23.tgz", + "integrity": "sha512-uq1iL79YjfYC0WXoHbC/z28q/9pOl8kSHaXdWmAAc8No+bDwqkZbzIJz55g/MUsPgSGm9LZ7QSUbzTcH5tz47w==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, "node-sass": { "version": "4.12.0", "resolved": "http://localhost:4873/node-sass/-/node-sass-4.12.0.tgz", @@ -4926,6 +5891,12 @@ "utila": "~0.4" } }, + "private": { + "version": "0.1.8", + "resolved": "http://localhost:4873/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, "process": { "version": "0.11.10", "resolved": "http://localhost:4873/process/-/process-0.11.10.tgz", @@ -5172,6 +6143,35 @@ "strip-indent": "^1.0.1" } }, + "regenerate": { + "version": "1.4.0", + "resolved": "http://localhost:4873/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "http://localhost:4873/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "http://localhost:4873/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + }, + "regenerator-transform": { + "version": "0.14.0", + "resolved": "http://localhost:4873/regenerator-transform/-/regenerator-transform-0.14.0.tgz", + "integrity": "sha512-rtOelq4Cawlbmq9xuMR5gdFmv7ku/sFoB7sRiywx7aq53bc52b4j6zvH7Te1Vt/X2YveDKnCGUbioieU7FEL3w==", + "dev": true, + "requires": { + "private": "^0.1.6" + } + }, "regex-not": { "version": "1.0.2", "resolved": "http://localhost:4873/regex-not/-/regex-not-1.0.2.tgz", @@ -5182,6 +6182,49 @@ "safe-regex": "^1.1.0" } }, + "regexp-tree": { + "version": "0.1.10", + "resolved": "http://localhost:4873/regexp-tree/-/regexp-tree-0.1.10.tgz", + "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==", + "dev": true + }, + "regexpu-core": { + "version": "4.5.4", + "resolved": "http://localhost:4873/regexpu-core/-/regexpu-core-4.5.4.tgz", + "integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.0.2", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.0", + "resolved": "http://localhost:4873/regjsgen/-/regjsgen-0.5.0.tgz", + "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", + "dev": true + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "http://localhost:4873/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "http://localhost:4873/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, "relateurl": { "version": "0.2.7", "resolved": "http://localhost:4873/relateurl/-/relateurl-0.2.7.tgz", @@ -6349,6 +7392,12 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "http://localhost:4873/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "http://localhost:4873/to-object-path/-/to-object-path-0.3.0.tgz", @@ -6427,6 +7476,12 @@ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, + "trim-right": { + "version": "1.0.1", + "resolved": "http://localhost:4873/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, "true-case-path": { "version": "1.0.3", "resolved": "http://localhost:4873/true-case-path/-/true-case-path-1.0.3.tgz", @@ -6503,6 +7558,34 @@ } } }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "http://localhost:4873/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "http://localhost:4873/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "http://localhost:4873/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "http://localhost:4873/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, "union-value": { "version": "1.0.0", "resolved": "http://localhost:4873/union-value/-/union-value-1.0.0.tgz", diff --git a/package.json b/package.json index 77c657a..2683940 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,15 @@ "author": "", "license": "ISC", "dependencies": { + "@babel/runtime": "^7.4.5", "p5": "^0.8.0" }, "devDependencies": { + "@babel/core": "^7.4.5", + "@babel/plugin-proposal-class-properties": "^7.4.4", + "@babel/plugin-transform-runtime": "^7.4.4", + "@babel/preset-env": "^7.4.5", + "babel-loader": "^8.0.6", "css-loader": "^2.1.1", "html-webpack-plugin": "^3.2.0", "node-sass": "^4.12.0", diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index b27f260..c9a4123 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -1,19 +1,19 @@ import Worker from './sound.worker.js'; -import { getRMS, getPitch } from './util.js'; +import { getSpectrumData } from './util.js'; -export default class HorizontalLines { +export default class HorizontalLinesAnimation { VOLUME_THRESHOLD = 20; LINE_MARGIN = 20; - MAX_LINES = window.innerWidth / LINE_MARGIN; FFT_SIZE = 256; - BASE_WIDTH = 2; + BASE_WIDTH = 4; spectrumPointHeight = 20; + maxLines = 10; line; - fresh; - maxPitch; - minPitch; + fresh = false; + + averageSpectrum; worker; analyser; @@ -22,20 +22,28 @@ export default class HorizontalLines { constructor(audioCtx) { this.worker = new Worker(); this.analyser = new AnalyserNode(audioCtx); - this.analyser.fftSize = FFT_SIZE; + this.analyser.fftSize = this.FFT_SIZE; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); - audioProcessor.onaudioprocess = this.audioProcess; + this.audioProcessor.onaudioprocess = () => this.audioProcess(); this.analyser.connect(this.audioProcessor); + this.worker.onmessage = e => this.getAverageData(e.data); } setup(sketch) { - sketch.colorMode(sketch.HSL, 255); + sketch.colorMode(sketch.RGB, 255); sketch.background(0); this.resize(); } resize() { this.spectrumPointHeight = window.innerHeight / this.analyser.frequencyBinCount; + this.maxLines = window.innerWidth / this.LINE_MARGIN; + } + + getAverageData(data) { + this.averageSpectrum = data.averageSpectrum; + console.log('got average'); + console.log(this.averageSpectrum); } audioProcess() { @@ -44,21 +52,9 @@ export default class HorizontalLines { spectrum.reverse(); - const volume = getRMS(spectrum); - const pitch = getPitch(spectrum, volume); - this.line = { - spectrum, - volume, - pitch, - }; - - if (pitch > this.maxPitch) { - this.maxPitch = pitch; - } - - if (pitch < this.minPitch) { - this.minPitch = pitch; - } + const data = getSpectrumData(spectrum); + this.worker.postMessage(data); + this.line = data; this.fresh = true; } @@ -76,21 +72,26 @@ export default class HorizontalLines { sketch.fill(0); sketch.rect(0, 0, this.LINE_MARGIN, window.innerHeight); - const lineBaseWidth = this.BASE_WIDTH; sketch.fill(`rgba(255, 255, 255, 0.2)`); - sketch.stroke(((this.line.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 255); + sketch.stroke(255, 255, 255); sketch.strokeWeight(1); sketch.beginShape(); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, -10); - sketch.curveVertex(lineBaseWidth, 0); + sketch.curveVertex(this.BASE_WIDTH, -10); + sketch.curveVertex(this.BASE_WIDTH, -10); + sketch.curveVertex(this.BASE_WIDTH, 0); for (let i = 1; i < this.line.spectrum.length - 1; i++) { - const point = this.line.spectrum[i]; - sketch.curveVertex(lineBaseWidth + point, i * this.spectrumPointHeight); + let point; + if (this.averageSpectrum) { + point = Math.max(this.line.spectrum[i] - (this.averageSpectrum[i] * 0.8), 0); + // point = 5 * (this.line.spectrum[i] / this.averageSpectrum[i]); + } else { + point = this.line.spectrum[i]; + } + sketch.curveVertex(this.BASE_WIDTH + point, i * this.spectrumPointHeight); } - sketch.curveVertex(lineBaseWidth, window.innerHeight); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); - sketch.curveVertex(lineBaseWidth, window.innerHeight + 10); + sketch.curveVertex(this.BASE_WIDTH, window.innerHeight); + sketch.curveVertex(this.BASE_WIDTH, window.innerHeight + 10); + sketch.curveVertex(this.BASE_WIDTH, window.innerHeight + 10); sketch.endShape(); } } diff --git a/src/index.js b/src/index.js index 4f4a751..613f662 100644 --- a/src/index.js +++ b/src/index.js @@ -1,22 +1,34 @@ import p5 from 'p5'; import './index.scss'; -import HorizontalLines from './horizontal-lines.js'; +import HorizontalLinesAnimation from './horizontal-lines.js'; +import SquaresAnimation from './squares.js'; async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); const input = audioCtx.createMediaStreamSource(microphone); - let activeAnimation; - const instance = new p5(( sketch ) => { + let activeAnimation; + + window.onresize = () => { + sketch.resizeCanvas(window.innerWidth, window.innerHeight); + activeAnimation.resize(); + }; + + function activate(animation) { + activeAnimation = animation; + input.connect(animation.analyser); + animation.setup(sketch); + } + sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); - const horizontalLines = new HorizontalLines(analyser, sketch); - activeAnimation = horizontalLines; - input.connect(activeAnimation.analyser); + const horizontalLines = new HorizontalLinesAnimation(audioCtx); + const squares = new SquaresAnimation(audioCtx); + activate(horizontalLines); }; sketch.draw = () => { @@ -24,7 +36,7 @@ async function main() { }; setTimeout(() => { - activactiveAnimation = + // activactiveAnimation = }, 1 * 60 * 1000); }); } diff --git a/src/sound.worker.js b/src/sound.worker.js index d323cd6..1f6347c 100644 --- a/src/sound.worker.js +++ b/src/sound.worker.js @@ -1,26 +1,33 @@ -const PERIOD = 60 * 1000; +const PERIOD = 20 * 1000; const data = []; let firstRun = true; function onDataPoint(event) { - console.log(event); + if (!firstRun) { + data.shift(); + } + data.push(event.data); } -self.addEventListener('message', onDataPoint); +self.onmessage = onDataPoint; function calculateAndSend() { firstRun = false; + if (!data.length) { + return; + } + let averagePitch = 0; let averageVolume = 0; - let averageSpectrum = []; + let averageSpectrum = (new Array(data[0].spectrum.length)).fill(0); + let minPitch = data[0].spectrum.length; let maxPitch = 0; - let minPitch = spectrum.length; - let maxVolume = 0; let minVolume = 500; + let maxVolume = 0; - for (const { volume, pitch, spectrum } in data) { + for (const { volume, pitch, spectrum } of data) { if (pitch > maxPitch) { maxPitch = pitch; } else if (pitch < minPitch) { @@ -43,7 +50,7 @@ function calculateAndSend() { averageVolume /= data.length; averagePitch /= data.length; - averageSpectrum = averageSpectrum.map(p => p / spectrum.length); + averageSpectrum = averageSpectrum.map(p => p / data.length); self.postMessage({ minVolume, diff --git a/src/squares.js b/src/squares.js index 97a8293..b74c689 100644 --- a/src/squares.js +++ b/src/squares.js @@ -1,37 +1,101 @@ -const VOLUME_THRESHOLD = 20; -const GRID_SIZE = 20; -const MAX_WIDTH = 5; -const MAX_HEIGHT = 8; -const FFT_SIZE = 1024; +import Worker from './sound.worker.js'; +import { getSpectrumData, getRandomBetween } from './util.js'; -export function setup(sketch, analyser) { - sketch.background('rgb(20, 30, 30)'); - sketch.colorMode(sketch.HSB); - analyser.fftSize = FFT_SIZE; -} +export default class SquaresAnimation { + VOLUME_THRESHOLD = 20; + GRID_SIZE = 20; + MAX_WIDTH = 5; + MAX_HEIGHT = 8; + FFT_SIZE = 1024; + + worker; + analyser; + audioProcessor; -export function draw(sketch) { - sketch.background('rgba(20, 30, 30, 0.01)'); - if (!circle) { - return; + minVolume = 360; + maxVolume = 0; + minPitch = 360; + maxPitch = 0; + + circle; + fresh = false; + + constructor(audioCtx) { + this.worker = new Worker(); + this.analyser = new AnalyserNode(audioCtx); + this.analyser.fftSize = this.FFT_SIZE; + this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); + this.audioProcessor.onaudioprocess = () => this.audioProcess(); + this.analyser.connect(this.audioProcessor); + this.worker.onmessage = e => this.getAverageData(e.data); } - const color = ((circle.pitch - minPitch) / (maxPitch - minPitch)) * 360; - const brightness = ((circle.volume - minVolume) / (maxVolume - minVolume)); - sketch.stroke(color, 100, 100, brightness); - for (let i = 0; i < circle.spectrum.length + 2; i++) { - const p = circle.spectrum[i - 2] * i * i * i * i / 2; - const x = getRandomBetween(0, Math.floor(window.innerWidth / GRID_SIZE)) * GRID_SIZE; - const y = getRandomBetween(0, Math.floor(window.innerHeight / GRID_SIZE)) * GRID_SIZE; - // const width = getRandomBetween(1, MAX_WIDTH) * GRID_SIZE; - // const height = getRandomBetween(1, MAX_HEIGHT) * GRID_SIZE; + setup(sketch) { + sketch.background('rgb(20, 30, 30)'); + sketch.colorMode(sketch.HSB); + this.resize(); + } + + resize() {} + + getAverageData(data) { + this.minVolume = data.minVolume; + this.maxVolume = data.maxVolume; + + this.minPitch = data.minPitch; + this.maxPitch = data.maxPitch; + } + + audioProcess() { + const spectrum = new Uint8Array(this.analyser.frequencyBinCount); + this.analyser.getByteFrequencyData(spectrum); + + spectrum.reverse(); + + const data = getSpectrumData(spectrum); + const { volume, pitch } = data; + this.worker.postMessage(data); + + if (volume > this.maxVolume) { + this.maxVolume = volume; + } + + if (volume !== 0 && volume < this.minVolume) { + this.minVolume = volume; + } + + if (pitch > this.maxPitch) { + this.maxPitch = pitch; + } + + if (pitch < this.minPitch) { + this.minPitch = pitch; + } + + this.circle = data; + this.fresh = true; + } + + draw(sketch) { + sketch.background('rgba(20, 30, 30, 0.01)'); + if (!this.fresh) { + return; + } + + const color = ((this.circle.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360; + const brightness = ((this.circle.volume - this.minVolume) / (this.maxVolume - this.minVolume)); + sketch.stroke(color, 100, 100, brightness); + for (let i = 0; i < spectrum.length + 2; i++) { + const x = getRandomBetween(0, Math.floor(window.innerWidth / this.GRID_SIZE)) * this.GRID_SIZE; + const y = getRandomBetween(0, Math.floor(window.innerHeight / this.GRID_SIZE)) * this.GRID_SIZE; sketch.fill(color, 100, 100, brightness * 0.5); - sketch.beginShape(); - sketch.vertex(x, y); - sketch.vertex(x, y + GRID_SIZE); - sketch.vertex(x + GRID_SIZE, y + GRID_SIZE); - sketch.vertex(x + GRID_SIZE, y); - sketch.vertex(x, y); - sketch.endShape(); + sketch.beginShape(); + sketch.vertex(x, y); + sketch.vertex(x, y + this.GRID_SIZE); + sketch.vertex(x + this.GRID_SIZE, y + this.GRID_SIZE); + sketch.vertex(x + this.GRID_SIZE, y); + sketch.vertex(x, y); + sketch.endShape(); + } } } diff --git a/src/util.js b/src/util.js index e3189e4..bd434b6 100644 --- a/src/util.js +++ b/src/util.js @@ -1,24 +1,29 @@ -export function getRMS(spectrum) { +export function getSpectrumData(spectrum) { + let highestPitch = 0; let rms = 0; - for (let i = 0; i < spectrum.length; i++) { - rms += spectrum[i] * spectrum[i]; - } - rms /= spectrum.length; - rms = Math.sqrt(rms); - return rms; -} - -export function getPitch(spectrum) { let cg = 0; let weight = 0; for (let i = 0; i < spectrum.length; i++) { + rms += spectrum[i] * spectrum[i]; cg += spectrum[i] * i; weight += spectrum[i]; + if (spectrum[i] > highestPitch) { + highestPitch = spectrum[i]; + } } - return Math.floor(cg / weight); + rms /= spectrum.length; + rms = Math.sqrt(rms); + const pitch = Math.floor(cg / weight); + return { + spectrum, + volume: rms, + pitch, + highestPitch, + }; } -function getRandomBetween(min, max) { + +export function getRandomBetween(min, max) { return Math.floor(Math.random() * (max - min)) + min; } diff --git a/webpack.config.js b/webpack.config.js index 3fe4f49..196b74b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,8 @@ module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', + globalObject: `typeof self !== 'undefined' ? self : this`, }, module: { rules: [ @@ -17,6 +18,23 @@ module.exports = { { loader: "sass-loader" }, ], }, + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + plugins: [ + '@babel/plugin-transform-runtime', + [ + '@babel/plugin-proposal-class-properties', + { loose: true }, + ], + ], + }, + }, + }, { test: /\.worker\.js$/, use: { loader: 'worker-loader' }, From 70a21d274fc1bfae55afab06fcffab7e193995cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 16:46:42 +0200 Subject: [PATCH 14/22] Combining --- src/circles.js | 111 +++++++++++++++++++++++++++++++--------- src/horizontal-lines.js | 2 - src/index.js | 26 ++++++++-- src/sound.worker.js | 12 +++-- 4 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/circles.js b/src/circles.js index 19b85db..51576dd 100644 --- a/src/circles.js +++ b/src/circles.js @@ -1,30 +1,93 @@ -const VOLUME_THRESHOLD = 20; -const CIRCLE_RADIUS = 100; -const CIRCLE_POINTS = 256; -const FFT_SIZE = CIRCLE_POINTS; -const MAX_CIRCLES = 20; +import Worker from './sound.worker.js'; +import { getSpectrumData } from './util.js'; -export function setup(sketch, analyser) { - sketch.createCanvas(window.innerWidth, window.innerHeight); - sketch.colorMode(sketch.HSL, 255); -} +export default class CirclesAnimation { + VOLUME_THRESHOLD = 20; + CIRCLE_RADIUS = 100; + CIRCLE_POINTS = 256; + FFT_SIZE = 256; + MAX_CIRCLES = 20; -export function draw(sketch) { - sketch.background(30); - sketch.noFill(); - sketch.strokeWeight(2); - sketch.stroke(230); + baseHeight; + baseWidth; + maxVolume = 0; + maxPitch = 0; + minPitch = 0; + circles = []; - for (let c = 0; c < circles.length; c++) { - const circle = circles[c]; - sketch.beginShape(); - for (let i = 0; i < circle.spectrum.length; i++) { - const p = circle.spectrum[i]; - sketch.curveVertex( - Math.cos((i / CIRCLE_POINTS) * 2 * Math.PI) * (CIRCLE_RADIUS + c * 15) + baseWidth + (maxVolume / 2 - circle.volume) + p, - Math.sin((i / CIRCLE_POINTS) * 2 * Math.PI) * (CIRCLE_RADIUS + c * 15) + baseHeight + (maxPitch / 2 - circle.pitch) - p, - ); + averageSpectrum; + + constructor(audioCtx) { + this.worker = new Worker(); + this.analyser = new AnalyserNode(audioCtx); + this.analyser.fftSize = this.FFT_SIZE; + this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); + this.audioProcessor.onaudioprocess = () => this.audioProcess(); + this.analyser.connect(this.audioProcessor); + this.worker.onmessage = e => this.getAverageData(e.data); + } + + setup(sketch) { + sketch.createCanvas(window.innerWidth, window.innerHeight); + sketch.colorMode(sketch.HSL, 255); + this.resize(); + } + + resize() { + this.baseWidth = window.innerWidth / 2; + this.baseHeight = window.innerHeight / 2; + } + + getAverageData(data) { + this.averageSpectrum = data.averageSpectrum; + } + + audioProcess() { + const spectrum = new Uint8Array(this.analyser.frequencyBinCount); + this.analyser.getByteFrequencyData(spectrum); + + spectrum.reverse(); + + const data = getSpectrumData(spectrum); + this.worker.postMessage(data); + const { volume, pitch } = data; + + if (volume > this.maxVolume) { + this.maxVolume = volume; + } + + if (pitch > this.maxPitch) { + this.maxPitch = pitch; + } + + if (pitch < this.minPitch) { + this.minPitch = pitch; + } + + this.circles.unshift(data); + + if (this.circles.length > this.MAX_CIRCLES) { + this.circles.pop(); + } + } + + draw(sketch) { + sketch.background(30); + sketch.noFill(); + sketch.strokeWeight(2); + sketch.stroke(230); + + for (let c = 0; c < this.circles.length; c++) { + const circle = this.circles[c]; + sketch.beginShape(); + for (let i = 0; i < circle.spectrum.length; i++) { + const p = circle.spectrum[i]; + sketch.curveVertex( + Math.cos((i / this.CIRCLE_POINTS) * 2 * Math.PI) * (this.CIRCLE_RADIUS + c * 15) + this.baseWidth + (this.maxVolume / 2 - circle.volume) + p, + Math.sin((i / this.CIRCLE_POINTS) * 2 * Math.PI) * (this.CIRCLE_RADIUS + c * 15) + this.baseHeight + (this.maxPitch / 2 - circle.pitch) - p, + ); + } + sketch.endShape(sketch.CLOSE); } - sketch.endShape(sketch.CLOSE); } } diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index c9a4123..d519fa0 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -42,8 +42,6 @@ export default class HorizontalLinesAnimation { getAverageData(data) { this.averageSpectrum = data.averageSpectrum; - console.log('got average'); - console.log(this.averageSpectrum); } audioProcess() { diff --git a/src/index.js b/src/index.js index 613f662..b7795ad 100644 --- a/src/index.js +++ b/src/index.js @@ -3,12 +3,25 @@ import './index.scss'; import HorizontalLinesAnimation from './horizontal-lines.js'; import SquaresAnimation from './squares.js'; +import CirclesAnimation from './circles.js'; async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); const input = audioCtx.createMediaStreamSource(microphone); + const horizontalLines = new HorizontalLinesAnimation(audioCtx); + const squares = new SquaresAnimation(audioCtx); + const circles = new CirclesAnimation(audioCtx); + + const animations = [ + horizontalLines, + squares, + circles, + ]; + + let animationNum = 0; + const instance = new p5(( sketch ) => { let activeAnimation; @@ -19,16 +32,23 @@ async function main() { function activate(animation) { activeAnimation = animation; + input.disconnect(); input.connect(animation.analyser); animation.setup(sketch); } sketch.setup = () => { sketch.createCanvas(window.innerWidth, window.innerHeight); + activate(animations[animationNum]); - const horizontalLines = new HorizontalLinesAnimation(audioCtx); - const squares = new SquaresAnimation(audioCtx); - activate(horizontalLines); + setInterval(() => { + animationNum++; + if (animationNum > animations.length - 1) { + animationNum = 0; + } + + activate(animations[animationNum]); + }, 10000); }; sketch.draw = () => { diff --git a/src/sound.worker.js b/src/sound.worker.js index 1f6347c..5cc6584 100644 --- a/src/sound.worker.js +++ b/src/sound.worker.js @@ -1,9 +1,13 @@ -const PERIOD = 20 * 1000; +const PERIOD = 1000; +const CAPTURE_PERIOD = 60 * 1000; const data = []; -let firstRun = true; + +let capture = true; + +setTimeout(() => capture = false, CAPTURE_PERIOD); function onDataPoint(event) { - if (!firstRun) { + if (!capture) { data.shift(); } data.push(event.data); @@ -12,8 +16,6 @@ function onDataPoint(event) { self.onmessage = onDataPoint; function calculateAndSend() { - firstRun = false; - if (!data.length) { return; } From 192feb41f84d7fd6fef29c0c00e55e0e4b94262e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 21:42:49 +0200 Subject: [PATCH 15/22] wtf --- src/horizontal-lines.js | 12 ++++++------ src/index.js | 6 +----- src/squares.js | 10 +++++----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index d519fa0..e2b1354 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -10,7 +10,7 @@ export default class HorizontalLinesAnimation { spectrumPointHeight = 20; maxLines = 10; - line; + data; fresh = false; averageSpectrum; @@ -52,7 +52,7 @@ export default class HorizontalLinesAnimation { const data = getSpectrumData(spectrum); this.worker.postMessage(data); - this.line = data; + this.data = data; this.fresh = true; } @@ -77,13 +77,13 @@ export default class HorizontalLinesAnimation { sketch.curveVertex(this.BASE_WIDTH, -10); sketch.curveVertex(this.BASE_WIDTH, -10); sketch.curveVertex(this.BASE_WIDTH, 0); - for (let i = 1; i < this.line.spectrum.length - 1; i++) { + for (let i = 1; i < this.data.spectrum.length - 1; i++) { let point; if (this.averageSpectrum) { - point = Math.max(this.line.spectrum[i] - (this.averageSpectrum[i] * 0.8), 0); - // point = 5 * (this.line.spectrum[i] / this.averageSpectrum[i]); + point = Math.max(this.data.spectrum[i] - (this.averageSpectrum[i] * 0.8), 0); + // point = 5 * (this.data.spectrum[i] / this.averageSpectrum[i]); } else { - point = this.line.spectrum[i]; + point = this.data.spectrum[i]; } sketch.curveVertex(this.BASE_WIDTH + point, i * this.spectrumPointHeight); } diff --git a/src/index.js b/src/index.js index b7795ad..1c524cc 100644 --- a/src/index.js +++ b/src/index.js @@ -33,7 +33,7 @@ async function main() { function activate(animation) { activeAnimation = animation; input.disconnect(); - input.connect(animation.analyser); + input.connect(activeAnimation.analyser); animation.setup(sketch); } @@ -54,10 +54,6 @@ async function main() { sketch.draw = () => { activeAnimation.draw(sketch); }; - - setTimeout(() => { - // activactiveAnimation = - }, 1 * 60 * 1000); }); } diff --git a/src/squares.js b/src/squares.js index b74c689..6d44d2b 100644 --- a/src/squares.js +++ b/src/squares.js @@ -17,7 +17,7 @@ export default class SquaresAnimation { minPitch = 360; maxPitch = 0; - circle; + data; fresh = false; constructor(audioCtx) { @@ -72,7 +72,7 @@ export default class SquaresAnimation { this.minPitch = pitch; } - this.circle = data; + this.data = data; this.fresh = true; } @@ -82,10 +82,10 @@ export default class SquaresAnimation { return; } - const color = ((this.circle.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360; - const brightness = ((this.circle.volume - this.minVolume) / (this.maxVolume - this.minVolume)); + const color = ((this.data.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360; + const brightness = ((this.data.volume - this.minVolume) / (this.maxVolume - this.minVolume)); sketch.stroke(color, 100, 100, brightness); - for (let i = 0; i < spectrum.length + 2; i++) { + for (let i = 0; i < this.data.spectrum.length + 2; i++) { const x = getRandomBetween(0, Math.floor(window.innerWidth / this.GRID_SIZE)) * this.GRID_SIZE; const y = getRandomBetween(0, Math.floor(window.innerHeight / this.GRID_SIZE)) * this.GRID_SIZE; sketch.fill(color, 100, 100, brightness * 0.5); From 81bafdf09aeebf4b70725949b1c037690c6ae029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 21:52:05 +0200 Subject: [PATCH 16/22] Tryna reduce framerate --- src/circles.js | 18 +++++++++++++----- src/horizontal-lines.js | 18 +++++++++++++----- src/index.js | 5 ++++- src/squares.js | 18 +++++++++++++----- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/circles.js b/src/circles.js index 51576dd..22722b2 100644 --- a/src/circles.js +++ b/src/circles.js @@ -18,19 +18,27 @@ export default class CirclesAnimation { averageSpectrum; constructor(audioCtx) { - this.worker = new Worker(); this.analyser = new AnalyserNode(audioCtx); this.analyser.fftSize = this.FFT_SIZE; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); this.audioProcessor.onaudioprocess = () => this.audioProcess(); - this.analyser.connect(this.audioProcessor); - this.worker.onmessage = e => this.getAverageData(e.data); } - setup(sketch) { + start(sketch) { + this.resize(); + + this.analyser.connect(this.audioProcessor); + + this.worker = new Worker(); + this.worker.onmessage = e => this.getAverageData(e.data); + sketch.createCanvas(window.innerWidth, window.innerHeight); sketch.colorMode(sketch.HSL, 255); - this.resize(); + } + + stop() { + this.analyser.disconnect(); + this.worker.terminate(); } resize() { diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index e2b1354..5a710d2 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -20,19 +20,27 @@ export default class HorizontalLinesAnimation { audioProcessor; constructor(audioCtx) { - this.worker = new Worker(); this.analyser = new AnalyserNode(audioCtx); this.analyser.fftSize = this.FFT_SIZE; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); this.audioProcessor.onaudioprocess = () => this.audioProcess(); - this.analyser.connect(this.audioProcessor); - this.worker.onmessage = e => this.getAverageData(e.data); } - setup(sketch) { + start(sketch) { + this.resize(); + + this.analyser.connect(this.audioProcessor); + + this.worker = new Worker(); + this.worker.onmessage = e => this.getAverageData(e.data); + sketch.colorMode(sketch.RGB, 255); sketch.background(0); - this.resize(); + } + + stop() { + this.analyser.disconnect(); + this.worker.terminate(); } resize() { diff --git a/src/index.js b/src/index.js index 1c524cc..a9e7e3f 100644 --- a/src/index.js +++ b/src/index.js @@ -31,10 +31,13 @@ async function main() { }; function activate(animation) { + if (activeAnimation) { + activeAnimation.stop(); + } activeAnimation = animation; input.disconnect(); input.connect(activeAnimation.analyser); - animation.setup(sketch); + animation.start(sketch); } sketch.setup = () => { diff --git a/src/squares.js b/src/squares.js index 6d44d2b..ee689ce 100644 --- a/src/squares.js +++ b/src/squares.js @@ -21,19 +21,27 @@ export default class SquaresAnimation { fresh = false; constructor(audioCtx) { - this.worker = new Worker(); this.analyser = new AnalyserNode(audioCtx); this.analyser.fftSize = this.FFT_SIZE; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); this.audioProcessor.onaudioprocess = () => this.audioProcess(); - this.analyser.connect(this.audioProcessor); - this.worker.onmessage = e => this.getAverageData(e.data); } - setup(sketch) { + start(sketch) { + this.resize(); + + this.analyser.connect(this.audioProcessor); + + this.worker = new Worker(); + this.worker.onmessage = e => this.getAverageData(e.data); + sketch.background('rgb(20, 30, 30)'); sketch.colorMode(sketch.HSB); - this.resize(); + } + + stop() { + this.analyser.disconnect(); + this.worker.terminate(); } resize() {} From 259dd18f41d9ed5b633bcfc01207def6e0ba0457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 23:57:48 +0200 Subject: [PATCH 17/22] Ayy --- src/index.html | 2 +- src/index.js | 4 +++- webpack.config.js | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/index.html b/src/index.html index 975c4ee..e498229 100644 --- a/src/index.html +++ b/src/index.html @@ -1,7 +1,7 @@ - + Untitled diff --git a/src/index.js b/src/index.js index a9e7e3f..3a4298d 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,8 @@ import HorizontalLinesAnimation from './horizontal-lines.js'; import SquaresAnimation from './squares.js'; import CirclesAnimation from './circles.js'; +const ANIMATION_TIME = 10 * 60 * 1000; + async function main() { const audioCtx = new AudioContext(); const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); @@ -51,7 +53,7 @@ async function main() { } activate(animations[animationNum]); - }, 10000); + }, ANIMATION_TIME); }; sketch.draw = () => { diff --git a/webpack.config.js b/webpack.config.js index 196b74b..66d9241 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -47,6 +47,7 @@ module.exports = { devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, + host: '0.0.0.0', port: 8080, hot: true, } From 5673cad5f7f6a468c2d356fbcc0210a226e590db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Fri, 14 Jun 2019 23:57:05 +0200 Subject: [PATCH 18/22] Added remote refresh --- refresh.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 refresh.sh diff --git a/refresh.sh b/refresh.sh new file mode 100755 index 0000000..77a092c --- /dev/null +++ b/refresh.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -o errexit +set -o nounset + +keystroke="CTRL+F5" + +# set to whatever's given as argument, defaults to firefox +BROWSER="${1:-firefox}" + +# find all visible browser windows +browser_windows="$(xdotool search --sync --all --onlyvisible --name ${BROWSER})" + +# Send keystroke +for bw in $browser_windows; do + xdotool key --window "$bw" "$keystroke" +done From 013ec58d8a647e79910a11446464d65c893fd2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 15 Jun 2019 00:30:54 +0200 Subject: [PATCH 19/22] FIxed lines --- src/horizontal-lines.js | 55 ++++++++++++++++++++++------------------- src/index.js | 3 ++- src/sound.worker.js | 6 ++--- src/squares.js | 5 ++-- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/horizontal-lines.js b/src/horizontal-lines.js index 5a710d2..2b8af67 100644 --- a/src/horizontal-lines.js +++ b/src/horizontal-lines.js @@ -3,14 +3,14 @@ import { getSpectrumData } from './util.js'; export default class HorizontalLinesAnimation { VOLUME_THRESHOLD = 20; - LINE_MARGIN = 20; + LINE_MARGIN = 30; FFT_SIZE = 256; BASE_WIDTH = 4; spectrumPointHeight = 20; maxLines = 10; - data; + lines = []; fresh = false; averageSpectrum; @@ -22,6 +22,7 @@ export default class HorizontalLinesAnimation { constructor(audioCtx) { this.analyser = new AnalyserNode(audioCtx); this.analyser.fftSize = this.FFT_SIZE; + this.analyser.timeSmoothingConstant = 0.2; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); this.audioProcessor.onaudioprocess = () => this.audioProcess(); } @@ -60,11 +61,15 @@ export default class HorizontalLinesAnimation { const data = getSpectrumData(spectrum); this.worker.postMessage(data); - this.data = data; + this.lines.unshift(data); + if (this.lines.length > this.maxLines + 1) { + this.lines.pop(); + } this.fresh = true; } + draw(sketch) { if (!this.fresh) { return; @@ -72,32 +77,32 @@ export default class HorizontalLinesAnimation { this.fresh = false; - const image = sketch.drawingContext.getImageData(0, 0, (window.innerWidth - this.LINE_MARGIN) * 2, window.innerHeight * 2); - sketch.drawingContext.putImageData(image, this.LINE_MARGIN * 2, 0); - sketch.noStroke(); - sketch.fill(0); - sketch.rect(0, 0, this.LINE_MARGIN, window.innerHeight); + sketch.background(0); - sketch.fill(`rgba(255, 255, 255, 0.2)`); + sketch.fill(`rgba(255, 255, 255, 0.1)`); sketch.stroke(255, 255, 255); sketch.strokeWeight(1); - sketch.beginShape(); - sketch.curveVertex(this.BASE_WIDTH, -10); - sketch.curveVertex(this.BASE_WIDTH, -10); - sketch.curveVertex(this.BASE_WIDTH, 0); - for (let i = 1; i < this.data.spectrum.length - 1; i++) { - let point; - if (this.averageSpectrum) { - point = Math.max(this.data.spectrum[i] - (this.averageSpectrum[i] * 0.8), 0); - // point = 5 * (this.data.spectrum[i] / this.averageSpectrum[i]); - } else { - point = this.data.spectrum[i]; + for (let l = 0; l < this.lines.length; l++) { + const line = this.lines[l]; + const baseWidth = this.BASE_WIDTH + this.LINE_MARGIN * l; + sketch.beginShape(); + sketch.curveVertex(baseWidth, -10); + sketch.curveVertex(baseWidth, -10); + sketch.curveVertex(baseWidth, 0); + for (let i = 1; i < line.spectrum.length - 1; i++) { + let point; + if (this.averageSpectrum) { + point = Math.max(line.spectrum[i] - (this.averageSpectrum[i] * 0.8), 0) * 2; + // point = 5 * (line.spectrum[i] / this.averageSpectrum[i]); + } else { + point = line.spectrum[i]; + } + sketch.curveVertex(baseWidth + point, i * this.spectrumPointHeight); } - sketch.curveVertex(this.BASE_WIDTH + point, i * this.spectrumPointHeight); + sketch.curveVertex(baseWidth, window.innerHeight); + sketch.curveVertex(baseWidth, window.innerHeight + 10); + sketch.curveVertex(baseWidth, window.innerHeight + 10); + sketch.endShape(); } - sketch.curveVertex(this.BASE_WIDTH, window.innerHeight); - sketch.curveVertex(this.BASE_WIDTH, window.innerHeight + 10); - sketch.curveVertex(this.BASE_WIDTH, window.innerHeight + 10); - sketch.endShape(); } } diff --git a/src/index.js b/src/index.js index 3a4298d..0546b62 100644 --- a/src/index.js +++ b/src/index.js @@ -6,6 +6,7 @@ import SquaresAnimation from './squares.js'; import CirclesAnimation from './circles.js'; const ANIMATION_TIME = 10 * 60 * 1000; +// const ANIMATION_TIME = 10 * 1000; async function main() { const audioCtx = new AudioContext(); @@ -17,8 +18,8 @@ async function main() { const circles = new CirclesAnimation(audioCtx); const animations = [ - horizontalLines, squares, + horizontalLines, circles, ]; diff --git a/src/sound.worker.js b/src/sound.worker.js index 5cc6584..d809831 100644 --- a/src/sound.worker.js +++ b/src/sound.worker.js @@ -1,5 +1,5 @@ -const PERIOD = 1000; -const CAPTURE_PERIOD = 60 * 1000; +const REPORT_PERIOD = 1 * 1000; +const CAPTURE_PERIOD = 3 * 60 * 1000; const data = []; let capture = true; @@ -67,4 +67,4 @@ function calculateAndSend() { }); } -setInterval(calculateAndSend, PERIOD); +setInterval(calculateAndSend, REPORT_PERIOD); diff --git a/src/squares.js b/src/squares.js index ee689ce..db7b1d6 100644 --- a/src/squares.js +++ b/src/squares.js @@ -23,6 +23,7 @@ export default class SquaresAnimation { constructor(audioCtx) { this.analyser = new AnalyserNode(audioCtx); this.analyser.fftSize = this.FFT_SIZE; + this.analyser.timeSmoothingConstant = 0.2; this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1); this.audioProcessor.onaudioprocess = () => this.audioProcess(); } @@ -85,7 +86,7 @@ export default class SquaresAnimation { } draw(sketch) { - sketch.background('rgba(20, 30, 30, 0.01)'); + sketch.background('rgba(20, 30, 30, 0.02)'); if (!this.fresh) { return; } @@ -93,7 +94,7 @@ export default class SquaresAnimation { const color = ((this.data.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360; const brightness = ((this.data.volume - this.minVolume) / (this.maxVolume - this.minVolume)); sketch.stroke(color, 100, 100, brightness); - for (let i = 0; i < this.data.spectrum.length + 2; i++) { + for (let i = 0; i < this.data.spectrum.length; i += 4) { const x = getRandomBetween(0, Math.floor(window.innerWidth / this.GRID_SIZE)) * this.GRID_SIZE; const y = getRandomBetween(0, Math.floor(window.innerHeight / this.GRID_SIZE)) * this.GRID_SIZE; sketch.fill(color, 100, 100, brightness * 0.5); From 75aec8bbb2632c63036dedeaa4274090b9056e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 15 Jun 2019 00:58:50 +0200 Subject: [PATCH 20/22] Squares --- src/squares.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/squares.js b/src/squares.js index db7b1d6..24f93e6 100644 --- a/src/squares.js +++ b/src/squares.js @@ -12,11 +12,16 @@ export default class SquaresAnimation { analyser; audioProcessor; + maxX; + maxY; + minVolume = 360; maxVolume = 0; minPitch = 360; maxPitch = 0; + averageSpectrum = []; + data; fresh = false; @@ -36,7 +41,7 @@ export default class SquaresAnimation { this.worker = new Worker(); this.worker.onmessage = e => this.getAverageData(e.data); - sketch.background('rgb(20, 30, 30)'); + sketch.background(0); sketch.colorMode(sketch.HSB); } @@ -45,7 +50,10 @@ export default class SquaresAnimation { this.worker.terminate(); } - resize() {} + resize() { + this.maxX = Math.floor(window.innerWidth / this.GRID_SIZE); + this.maxY = Math.floor(window.innerHeight / this.GRID_SIZE); + } getAverageData(data) { this.minVolume = data.minVolume; @@ -53,6 +61,8 @@ export default class SquaresAnimation { this.minPitch = data.minPitch; this.maxPitch = data.maxPitch; + + this.averageSpectrum = data.averageSpectrum; } audioProcess() { @@ -86,18 +96,20 @@ export default class SquaresAnimation { } draw(sketch) { - sketch.background('rgba(20, 30, 30, 0.02)'); + sketch.background('rgba(0, 0, 0, 0.02)'); if (!this.fresh) { return; } + const brightness = ((this.data.volume - this.minVolume) / (this.maxVolume - this.minVolume)) * 80; const color = ((this.data.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360; - const brightness = ((this.data.volume - this.minVolume) / (this.maxVolume - this.minVolume)); - sketch.stroke(color, 100, 100, brightness); - for (let i = 0; i < this.data.spectrum.length; i += 4) { - const x = getRandomBetween(0, Math.floor(window.innerWidth / this.GRID_SIZE)) * this.GRID_SIZE; - const y = getRandomBetween(0, Math.floor(window.innerHeight / this.GRID_SIZE)) * this.GRID_SIZE; - sketch.fill(color, 100, 100, brightness * 0.5); + for (let i = 0; i < brightness; i++) { + const p = this.data.spectrum[i]; + sketch.stroke(color, 100, 100); + + sketch.fill(color, 100, brightness); + const x = getRandomBetween(0, this.maxX) * this.GRID_SIZE; + const y = getRandomBetween(0, this.maxY) * this.GRID_SIZE; sketch.beginShape(); sketch.vertex(x, y); sketch.vertex(x, y + this.GRID_SIZE); From a112a9ac9751717e453d4caf6000889dd113be9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 15 Jun 2019 01:38:23 +0200 Subject: [PATCH 21/22] Last changes --- src/circles.js | 2 +- src/index.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/circles.js b/src/circles.js index 22722b2..e0c05c8 100644 --- a/src/circles.js +++ b/src/circles.js @@ -89,7 +89,7 @@ export default class CirclesAnimation { const circle = this.circles[c]; sketch.beginShape(); for (let i = 0; i < circle.spectrum.length; i++) { - const p = circle.spectrum[i]; + const p = Math.max(circle.spectrum[i] - (this.averageSpectrum[i] * 0.6), 0); sketch.curveVertex( Math.cos((i / this.CIRCLE_POINTS) * 2 * Math.PI) * (this.CIRCLE_RADIUS + c * 15) + this.baseWidth + (this.maxVolume / 2 - circle.volume) + p, Math.sin((i / this.CIRCLE_POINTS) * 2 * Math.PI) * (this.CIRCLE_RADIUS + c * 15) + this.baseHeight + (this.maxPitch / 2 - circle.pitch) - p, diff --git a/src/index.js b/src/index.js index 0546b62..280580c 100644 --- a/src/index.js +++ b/src/index.js @@ -5,8 +5,8 @@ import HorizontalLinesAnimation from './horizontal-lines.js'; import SquaresAnimation from './squares.js'; import CirclesAnimation from './circles.js'; -const ANIMATION_TIME = 10 * 60 * 1000; -// const ANIMATION_TIME = 10 * 1000; +// const ANIMATION_TIME = 10 * 60 * 1000; +const ANIMATION_TIME = 20 * 1000; async function main() { const audioCtx = new AudioContext(); From b5ae06ace69cdd9056b11b40cff6141f78d50a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Sat, 15 Jun 2019 01:40:01 +0200 Subject: [PATCH 22/22] Added GPL --- LICENSE.md | 662 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a871fcf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,662 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. +