diff --git a/js/components/Visualizer.tsx b/js/components/Visualizer.tsx index 33f517db..33d645c6 100644 --- a/js/components/Visualizer.tsx +++ b/js/components/Visualizer.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback } from "react"; +import React, { useMemo, useCallback, useState } from "react"; import * as Actions from "../actionCreators"; import * as Selectors from "../selectors"; @@ -10,11 +10,33 @@ const PIXEL_DENSITY = 2; const BAR_WIDTH = 3 * PIXEL_DENSITY; const GRADIENT_COLOR_COUNT = 16; const PEAK_COLOR_INDEX = 23; +const NUM_BARS = 20; type Props = { analyser: AnalyserNode; }; +function octaveBucketsForBufferLength(bufferLength: number): number[] { + const octaveBuckets = new Array(NUM_BARS).fill(0); + const minHz = 200; + const maxHz = 22050; + const octaveStep = Math.pow(maxHz / minHz, 1 / NUM_BARS); + + octaveBuckets[0] = 0; + octaveBuckets[1] = minHz; + for (let i = 2; i < NUM_BARS - 1; i++) { + octaveBuckets[i] = octaveBuckets[i - 1] * octaveStep; + } + octaveBuckets[NUM_BARS - 1] = maxHz; + + for (let i = 0; i < NUM_BARS; i++) { + const octaveIdx = Math.floor((octaveBuckets[i] / maxHz) * bufferLength); + octaveBuckets[i] = octaveIdx; + } + + return octaveBuckets; +} + // Return the average value in a slice of dataArray function sliceAverage( dataArray: Uint8Array, @@ -127,6 +149,13 @@ function Visualizer(props: Props) { } }, [props.analyser.fftSize, props.analyser.frequencyBinCount, style]); + const dataArray = useMemo(() => { + return new Uint8Array(bufferLength); + }, [bufferLength]); + const octaveBuckets = useMemo(() => { + return octaveBucketsForBufferLength(bufferLength); + }, [bufferLength]); + const bgCanvas = useMemo(() => { return preRenderBg( width, @@ -170,7 +199,7 @@ function Visualizer(props: Props) { ); const paintOscilloscopeFrame = useCallback( - (canvasCtx: CanvasRenderingContext2D, dataArray: Uint8Array) => { + (canvasCtx: CanvasRenderingContext2D) => { props.analyser.getByteTimeDomainData(dataArray); canvasCtx.lineWidth = PIXEL_DENSITY; @@ -208,7 +237,15 @@ function Visualizer(props: Props) { } canvasCtx.stroke(); }, - [bufferLength, colors, height, props.analyser, renderWidth, width] + [ + bufferLength, + colors, + dataArray, + height, + props.analyser, + renderWidth, + width, + ] ); const innerProps = { @@ -225,6 +262,8 @@ function Visualizer(props: Props) { printBar, paintOscilloscopeFrame, bufferLength, + dataArray, + octaveBuckets, }; return ; } diff --git a/js/components/VisualizerInner.js b/js/components/VisualizerInner.js index 22da0686..80078a82 100644 --- a/js/components/VisualizerInner.js +++ b/js/components/VisualizerInner.js @@ -6,28 +6,6 @@ const PIXEL_DENSITY = 2; const NUM_BARS = 20; const BAR_WIDTH = 3 * PIXEL_DENSITY; const BAR_PEAK_DROP_RATE = 0.01; - -function octaveBucketsForBufferLength(bufferLength) { - const octaveBuckets = new Array(NUM_BARS).fill(0); - const minHz = 200; - const maxHz = 22050; - const octaveStep = Math.pow(maxHz / minHz, 1 / NUM_BARS); - - octaveBuckets[0] = 0; - octaveBuckets[1] = minHz; - for (let i = 2; i < NUM_BARS - 1; i++) { - octaveBuckets[i] = octaveBuckets[i - 1] * octaveStep; - } - octaveBuckets[NUM_BARS - 1] = maxHz; - - for (let i = 0; i < NUM_BARS; i++) { - const octaveIdx = Math.floor((octaveBuckets[i] / maxHz) * bufferLength); - octaveBuckets[i] = octaveIdx; - } - - return octaveBuckets; -} - class VisualizerInner extends React.Component { componentDidMount() { this.barPeaks = new Array(NUM_BARS).fill(0); @@ -88,24 +66,13 @@ class VisualizerInner extends React.Component { // TODO: Split this into to methods. One for skin update, one for style // update. this.props.analyser.fftSize = 2048; - if (this.props.style === VISUALIZERS.OSCILLOSCOPE) { - this.dataArray = new Uint8Array(this.props.bufferLength); - } else if (this.props.style === VISUALIZERS.BAR) { - this.dataArray = new Uint8Array(this.props.bufferLength); - - if (!this.octaveBuckets) { - this.octaveBuckets = octaveBucketsForBufferLength( - this.props.bufferLength - ); - } - } } paintFrame() { switch (this.props.style) { case VISUALIZERS.OSCILLOSCOPE: this.canvasCtx.drawImage(this.props.bgCanvas, 0, 0); - this.props.paintOscilloscopeFrame(this.canvasCtx, this.dataArray); + this.props.paintOscilloscopeFrame(this.canvasCtx); break; case VISUALIZERS.BAR: this.canvasCtx.drawImage(this.props.bgCanvas, 0, 0); @@ -117,15 +84,15 @@ class VisualizerInner extends React.Component { } _paintBarFrame() { - this.props.analyser.getByteFrequencyData(this.dataArray); + this.props.analyser.getByteFrequencyData(this.props.dataArray); const heightMultiplier = this._renderHeight() / 256; const xOffset = BAR_WIDTH + PIXEL_DENSITY; // Bar width, plus a pixel of spacing to the right. for (let j = 0; j < NUM_BARS - 1; j++) { - const start = this.octaveBuckets[j]; - const end = this.octaveBuckets[j + 1]; + const start = this.props.octaveBuckets[j]; + const end = this.props.octaveBuckets[j + 1]; let amplitude = 0; for (let k = start; k < end; k++) { - amplitude += this.dataArray[k]; + amplitude += this.props.dataArray[k]; } amplitude /= end - start;