Average
This commit is contained in:
parent
5389f22ff2
commit
add2c28363
1083
package-lock.json
generated
1083
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -11,9 +11,15 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.4.5",
|
||||||
"p5": "^0.8.0"
|
"p5": "^0.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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",
|
"css-loader": "^2.1.1",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"node-sass": "^4.12.0",
|
"node-sass": "^4.12.0",
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import Worker from './sound.worker.js';
|
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;
|
VOLUME_THRESHOLD = 20;
|
||||||
LINE_MARGIN = 20;
|
LINE_MARGIN = 20;
|
||||||
MAX_LINES = window.innerWidth / LINE_MARGIN;
|
|
||||||
FFT_SIZE = 256;
|
FFT_SIZE = 256;
|
||||||
BASE_WIDTH = 2;
|
BASE_WIDTH = 4;
|
||||||
|
|
||||||
spectrumPointHeight = 20;
|
spectrumPointHeight = 20;
|
||||||
|
maxLines = 10;
|
||||||
|
|
||||||
line;
|
line;
|
||||||
fresh;
|
fresh = false;
|
||||||
maxPitch;
|
|
||||||
minPitch;
|
averageSpectrum;
|
||||||
|
|
||||||
worker;
|
worker;
|
||||||
analyser;
|
analyser;
|
||||||
|
@ -22,20 +22,28 @@ export default class HorizontalLines {
|
||||||
constructor(audioCtx) {
|
constructor(audioCtx) {
|
||||||
this.worker = new Worker();
|
this.worker = new Worker();
|
||||||
this.analyser = new AnalyserNode(audioCtx);
|
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);
|
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.analyser.connect(this.audioProcessor);
|
||||||
|
this.worker.onmessage = e => this.getAverageData(e.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(sketch) {
|
setup(sketch) {
|
||||||
sketch.colorMode(sketch.HSL, 255);
|
sketch.colorMode(sketch.RGB, 255);
|
||||||
sketch.background(0);
|
sketch.background(0);
|
||||||
this.resize();
|
this.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
resize() {
|
resize() {
|
||||||
this.spectrumPointHeight = window.innerHeight / this.analyser.frequencyBinCount;
|
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() {
|
audioProcess() {
|
||||||
|
@ -44,21 +52,9 @@ export default class HorizontalLines {
|
||||||
|
|
||||||
spectrum.reverse();
|
spectrum.reverse();
|
||||||
|
|
||||||
const volume = getRMS(spectrum);
|
const data = getSpectrumData(spectrum);
|
||||||
const pitch = getPitch(spectrum, volume);
|
this.worker.postMessage(data);
|
||||||
this.line = {
|
this.line = data;
|
||||||
spectrum,
|
|
||||||
volume,
|
|
||||||
pitch,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pitch > this.maxPitch) {
|
|
||||||
this.maxPitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pitch < this.minPitch) {
|
|
||||||
this.minPitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fresh = true;
|
this.fresh = true;
|
||||||
}
|
}
|
||||||
|
@ -76,21 +72,26 @@ export default class HorizontalLines {
|
||||||
sketch.fill(0);
|
sketch.fill(0);
|
||||||
sketch.rect(0, 0, this.LINE_MARGIN, window.innerHeight);
|
sketch.rect(0, 0, this.LINE_MARGIN, window.innerHeight);
|
||||||
|
|
||||||
const lineBaseWidth = this.BASE_WIDTH;
|
|
||||||
sketch.fill(`rgba(255, 255, 255, 0.2)`);
|
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.strokeWeight(1);
|
||||||
sketch.beginShape();
|
sketch.beginShape();
|
||||||
sketch.curveVertex(lineBaseWidth, -10);
|
sketch.curveVertex(this.BASE_WIDTH, -10);
|
||||||
sketch.curveVertex(lineBaseWidth, -10);
|
sketch.curveVertex(this.BASE_WIDTH, -10);
|
||||||
sketch.curveVertex(lineBaseWidth, 0);
|
sketch.curveVertex(this.BASE_WIDTH, 0);
|
||||||
for (let i = 1; i < this.line.spectrum.length - 1; i++) {
|
for (let i = 1; i < this.line.spectrum.length - 1; i++) {
|
||||||
const point = this.line.spectrum[i];
|
let point;
|
||||||
sketch.curveVertex(lineBaseWidth + point, i * this.spectrumPointHeight);
|
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(lineBaseWidth, window.innerHeight);
|
sketch.curveVertex(this.BASE_WIDTH + point, i * this.spectrumPointHeight);
|
||||||
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();
|
sketch.endShape();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
24
src/index.js
24
src/index.js
|
@ -1,22 +1,34 @@
|
||||||
import p5 from 'p5';
|
import p5 from 'p5';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
import HorizontalLines from './horizontal-lines.js';
|
import HorizontalLinesAnimation from './horizontal-lines.js';
|
||||||
|
import SquaresAnimation from './squares.js';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const audioCtx = new AudioContext();
|
const audioCtx = new AudioContext();
|
||||||
const microphone = await navigator.mediaDevices.getUserMedia({ audio: true });
|
const microphone = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
const input = audioCtx.createMediaStreamSource(microphone);
|
const input = audioCtx.createMediaStreamSource(microphone);
|
||||||
|
|
||||||
|
const instance = new p5(( sketch ) => {
|
||||||
let activeAnimation;
|
let activeAnimation;
|
||||||
|
|
||||||
const instance = new p5(( sketch ) => {
|
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.setup = () => {
|
||||||
sketch.createCanvas(window.innerWidth, window.innerHeight);
|
sketch.createCanvas(window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
const horizontalLines = new HorizontalLines(analyser, sketch);
|
const horizontalLines = new HorizontalLinesAnimation(audioCtx);
|
||||||
activeAnimation = horizontalLines;
|
const squares = new SquaresAnimation(audioCtx);
|
||||||
input.connect(activeAnimation.analyser);
|
activate(horizontalLines);
|
||||||
};
|
};
|
||||||
|
|
||||||
sketch.draw = () => {
|
sketch.draw = () => {
|
||||||
|
@ -24,7 +36,7 @@ async function main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
activactiveAnimation =
|
// activactiveAnimation =
|
||||||
}, 1 * 60 * 1000);
|
}, 1 * 60 * 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,33 @@
|
||||||
const PERIOD = 60 * 1000;
|
const PERIOD = 20 * 1000;
|
||||||
const data = [];
|
const data = [];
|
||||||
let firstRun = true;
|
let firstRun = true;
|
||||||
|
|
||||||
function onDataPoint(event) {
|
function onDataPoint(event) {
|
||||||
console.log(event);
|
if (!firstRun) {
|
||||||
|
data.shift();
|
||||||
|
}
|
||||||
|
data.push(event.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('message', onDataPoint);
|
self.onmessage = onDataPoint;
|
||||||
|
|
||||||
function calculateAndSend() {
|
function calculateAndSend() {
|
||||||
firstRun = false;
|
firstRun = false;
|
||||||
|
|
||||||
|
if (!data.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let averagePitch = 0;
|
let averagePitch = 0;
|
||||||
let averageVolume = 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 maxPitch = 0;
|
||||||
let minPitch = spectrum.length;
|
|
||||||
let maxVolume = 0;
|
|
||||||
let minVolume = 500;
|
let minVolume = 500;
|
||||||
|
let maxVolume = 0;
|
||||||
|
|
||||||
for (const { volume, pitch, spectrum } in data) {
|
for (const { volume, pitch, spectrum } of data) {
|
||||||
if (pitch > maxPitch) {
|
if (pitch > maxPitch) {
|
||||||
maxPitch = pitch;
|
maxPitch = pitch;
|
||||||
} else if (pitch < minPitch) {
|
} else if (pitch < minPitch) {
|
||||||
|
@ -43,7 +50,7 @@ function calculateAndSend() {
|
||||||
|
|
||||||
averageVolume /= data.length;
|
averageVolume /= data.length;
|
||||||
averagePitch /= data.length;
|
averagePitch /= data.length;
|
||||||
averageSpectrum = averageSpectrum.map(p => p / spectrum.length);
|
averageSpectrum = averageSpectrum.map(p => p / data.length);
|
||||||
|
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
minVolume,
|
minVolume,
|
||||||
|
|
108
src/squares.js
108
src/squares.js
|
@ -1,37 +1,101 @@
|
||||||
const VOLUME_THRESHOLD = 20;
|
import Worker from './sound.worker.js';
|
||||||
const GRID_SIZE = 20;
|
import { getSpectrumData, getRandomBetween } from './util.js';
|
||||||
const MAX_WIDTH = 5;
|
|
||||||
const MAX_HEIGHT = 8;
|
|
||||||
const FFT_SIZE = 1024;
|
|
||||||
|
|
||||||
export function setup(sketch, analyser) {
|
export default class SquaresAnimation {
|
||||||
sketch.background('rgb(20, 30, 30)');
|
VOLUME_THRESHOLD = 20;
|
||||||
sketch.colorMode(sketch.HSB);
|
GRID_SIZE = 20;
|
||||||
analyser.fftSize = FFT_SIZE;
|
MAX_WIDTH = 5;
|
||||||
|
MAX_HEIGHT = 8;
|
||||||
|
FFT_SIZE = 1024;
|
||||||
|
|
||||||
|
worker;
|
||||||
|
analyser;
|
||||||
|
audioProcessor;
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function draw(sketch) {
|
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)');
|
sketch.background('rgba(20, 30, 30, 0.01)');
|
||||||
if (!circle) {
|
if (!this.fresh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = ((circle.pitch - minPitch) / (maxPitch - minPitch)) * 360;
|
const color = ((this.circle.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360;
|
||||||
const brightness = ((circle.volume - minVolume) / (maxVolume - minVolume));
|
const brightness = ((this.circle.volume - this.minVolume) / (this.maxVolume - this.minVolume));
|
||||||
sketch.stroke(color, 100, 100, brightness);
|
sketch.stroke(color, 100, 100, brightness);
|
||||||
for (let i = 0; i < circle.spectrum.length + 2; i++) {
|
for (let i = 0; i < spectrum.length + 2; i++) {
|
||||||
const p = circle.spectrum[i - 2] * i * i * i * i / 2;
|
const x = getRandomBetween(0, Math.floor(window.innerWidth / this.GRID_SIZE)) * this.GRID_SIZE;
|
||||||
const x = getRandomBetween(0, Math.floor(window.innerWidth / GRID_SIZE)) * GRID_SIZE;
|
const y = getRandomBetween(0, Math.floor(window.innerHeight / this.GRID_SIZE)) * this.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.fill(color, 100, 100, brightness * 0.5);
|
||||||
sketch.beginShape();
|
sketch.beginShape();
|
||||||
sketch.vertex(x, y);
|
sketch.vertex(x, y);
|
||||||
sketch.vertex(x, y + GRID_SIZE);
|
sketch.vertex(x, y + this.GRID_SIZE);
|
||||||
sketch.vertex(x + GRID_SIZE, y + GRID_SIZE);
|
sketch.vertex(x + this.GRID_SIZE, y + this.GRID_SIZE);
|
||||||
sketch.vertex(x + GRID_SIZE, y);
|
sketch.vertex(x + this.GRID_SIZE, y);
|
||||||
sketch.vertex(x, y);
|
sketch.vertex(x, y);
|
||||||
sketch.endShape();
|
sketch.endShape();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
29
src/util.js
29
src/util.js
|
@ -1,24 +1,29 @@
|
||||||
export function getRMS(spectrum) {
|
export function getSpectrumData(spectrum) {
|
||||||
|
let highestPitch = 0;
|
||||||
let rms = 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 cg = 0;
|
||||||
let weight = 0;
|
let weight = 0;
|
||||||
for (let i = 0; i < spectrum.length; i++) {
|
for (let i = 0; i < spectrum.length; i++) {
|
||||||
|
rms += spectrum[i] * spectrum[i];
|
||||||
cg += spectrum[i] * i;
|
cg += spectrum[i] * i;
|
||||||
weight += spectrum[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;
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ module.exports = {
|
||||||
entry: './src/index.js',
|
entry: './src/index.js',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
filename: 'bundle.js'
|
filename: 'bundle.js',
|
||||||
|
globalObject: `typeof self !== 'undefined' ? self : this`,
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -17,6 +18,23 @@ module.exports = {
|
||||||
{ loader: "sass-loader" },
|
{ 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$/,
|
test: /\.worker\.js$/,
|
||||||
use: { loader: 'worker-loader' },
|
use: { loader: 'worker-loader' },
|
||||||
|
|
Loading…
Reference in a new issue