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] 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: [