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; }