在 React 中将音频可视化为波形
音频可视化是为音乐、播客或其他任何音频内容增添全新维度的绝佳方式。一种常见的音频可视化方法是显示其波形,波形可以显示声波在每个时间点的振幅。在本教程中,我们将学习如何在不使用任何库的情况下,使用 React 为音频文件生成波形。
演示
先决条件
开始之前,请确保您的计算机上已安装以下软件:
- Node.js 和 npm(或 yarn)
项目设置
我们先来创建一个新的 React 项目。打开终端并运行以下命令:
npx create-react-app audio-visualizer
cd audio-visualizer
npm install
分析音频数据
要创建波形,我们需要分析音频数据。我们可以使用 Web Audio API 来实现这一点,它提供了一个 AnalyserNode 对象,可用于分析音频源的频域和时域数据。
在 App.js 文件中,我们创建了一个 audioAnalyzer 函数,该函数会创建一个 AnalyserNode 对象并将其连接到音频源。我们还创建了一个 dataArray 来存储用于绘制波形的频率数据。
// App.js
import { useRef, useState } from "react";
import "./styles.css";
import WaveForm from "./WaveForm";
export default function App() {
const [audioUrl, setAudioUrl] = useState();
const [analyzerData, setAnalyzerData] = useState(null);
const audioElmRef = useRef(null);
// audioAnalyzer function analyzes the audio and sets the analyzerData state
const audioAnalyzer = () => {
// create a new AudioContext
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// create an analyzer node with a buffer size of 2048
const analyzer = audioCtx.createAnalyser();
analyzer.fftSize = 2048;
const bufferLength = analyzer.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const source = audioCtx.createMediaElementSource(audioElmRef.current);
source.connect(analyzer);
source.connect(audioCtx.destination);
source.onended = () => {
source.disconnect();
};
// set the analyzerData state with the analyzer, bufferLength, and dataArray
setAnalyzerData({ analyzer, bufferLength, dataArray });
};
// onFileChange function handles the file input and triggers the audio analysis
const onFileChange = (e) => {
const file = e.target.files?.[0];
if (!file) return;
setAudioUrl(URL.createObjectURL(file));
audioAnalyzer();
};
return (
<div className="App">
<h1>Audio Visualizer</h1>
{analyzerData && <WaveForm analyzerData={analyzerData} />}
<div
style={{
height: 80,
display: "flex",
justifyContent: "space-around",
alignItems: "center"
}}
>
<input type="file" accept="audio/*" onChange={onFileChange} />
<audio src={audioUrl ?? ""} controls ref={audioElmRef} />
</div>
</div>
);
}
绘制波形
接下来,我们将创建一个 WaveForm 组件,该组件接收 analyzerData 作为 prop,并渲染一个 canvas 元素。我们使用 useSize hook 根据窗口尺寸设置 canvas 的大小。
// useSize.js
import { useCallback, useEffect, useState } from 'react';
// custom hook to get the width and height of the browser window
const useSize = () => {
// initialize width and height to 0
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
// setSizes callback function to update width and height with current window dimensions
const setSizes = useCallback(() => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
}, [setWidth, setHeight]);
// add event listener for window resize and call setSizes
useEffect(() => {
window.addEventListener('resize', setSizes);
setSizes();
return () => window.removeEventListener('resize', setSizes);
}, [setSizes]);
// return width and height
return [width, height];
};
export default useSize;
在 WaveForm 组件中,我们创建了一个 animateBars 函数,该函数使用 AnalyserNode 获取频率数据并绘制波形条。然后,我们创建一个 draw 函数,该函数设置 canvas 上下文并在每个动画帧调用 animateBars。
// This function takes in the audio data, analyzes it, and generates a waveform
// that is visualized on a canvas element.
function animateBars(analyser, canvas, canvasCtx, dataArray, bufferLength) {
// Analyze the audio data using the Web Audio API's `getByteFrequencyData` method.
analyser.getByteFrequencyData(dataArray);
// Set the canvas fill style to black.
canvasCtx.fillStyle = '#000';
// Calculate the height of the canvas.
const HEIGHT = canvas.height / 2;
// Calculate the width of each bar in the waveform based on the canvas width and the buffer length.
var barWidth = Math.ceil(canvas.width / bufferLength) * 2.5;
// Initialize variables for the bar height and x-position.
let barHeight;
let x = 0;
// Loop through each element in the `dataArray`.
for (var i = 0; i < bufferLength; i++) {
// Calculate the height of the current bar based on the audio data and the canvas height.
barHeight = (dataArray[i] / 255) * HEIGHT;
// Generate random RGB values for each bar.
const maximum = 10;
const minimum = -10;
var r = 242 + Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
var g = 104 + Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
var b = 65 + Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
// Set the canvas fill style to the random RGB values.
canvasCtx.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')';
// Draw the bar on the canvas at the current x-position and with the calculated height and width.
canvasCtx.fillRect(x, HEIGHT - barHeight, barWidth, barHeight);
// Update the x-position for the next bar.
x += barWidth + 1;
}
}
`animateBars` 函数负责根据传入的音频数据生成波形。它使用 Web Audio API 的 `getByteFrequencyData` 方法分析音频数据,然后遍历 `dataArray` 中的每个元素,为每个频率生成一个条形图。每个条形图的高度根据音频数据和画布高度计算,宽度根据画布宽度和缓冲区长度计算。该函数还会为每个条形图生成随机 RGB 值,并在绘制每个条形图之前相应地设置画布填充样式。
// WaveForm.jsx
import { useRef, useEffect } from "react";
// Function to animate the bars
function animateBars(analyser, canvas, canvasCtx, dataArray, bufferLength) {
// ...
}
// Component to render the waveform
const WaveForm = ({ analyzerData }) => {
// Ref for the canvas element
const canvasRef = useRef(null);
const { dataArray, analyzer, bufferLength } = analyzerData;
// Function to draw the waveform
const draw = (dataArray, analyzer, bufferLength) => {
const canvas = canvasRef.current;
if (!canvas || !analyzer) return;
const canvasCtx = canvas.getContext("2d");
const animate = () => {
requestAnimationFrame(animate);
canvas.width = canvas.width;
animateBars(analyzer, canvas, canvasCtx, dataArray, bufferLength);
};
animate();
};
// Effect to draw the waveform on mount and update
useEffect(() => {
draw(dataArray, analyzer, bufferLength);
}, [dataArray, analyzer, bufferLength]);
// Return the canvas element
return (
<canvas
style={{
position: "absolute",
top: "0",
left: "0",
zIndex: "-10"
}}
ref={canvasRef}
width={window.innerWidth}
height={window.innerHeight}
/>
);
};
export default WaveForm;
结论
我们探索了如何在不使用任何外部库的情况下,在 React 应用中可视化音频波形。首先,我们使用 Web Audio API 分析音频,并使用 AnalyserNode 提取频率数据。然后,我们使用 Canvas API 在 canvas 元素上绘制波形。最后,我们实现了一个函数,通过为每个波形条生成随机颜色和高度来为其添加动画效果。本项目充分展现了 React 和 Web Audio API 的强大功能和多功能性。
要查看此项目的完整代码,请访问Codesandbox(链接在此)。感谢阅读!
文章来源:https://dev.to/ssk14/visualizing-audio-as-a-waveform-in-react-o67