发布于 2026-01-06 2 阅读
0

在 React 中将音频可视化为波形

在 React 中将音频可视化为波形

音频可视化是为音乐、播客或其他任何音频内容增添全新维度的绝佳方式。一种常见的音频可视化方法是显示其波形,波形可以显示声波在每个时间点的振幅。在本教程中,我们将学习如何在不使用任何库的情况下,使用 React 为音频文件生成波形。

演示

先决条件

开始之前,请确保您的计算机上已安装以下软件:

  • Node.js 和 npm(或 yarn)

项目设置

我们先来创建一个新的 React 项目。打开终端并运行以下命令:

npx create-react-app audio-visualizer
cd audio-visualizer
npm install
Enter fullscreen mode Exit fullscreen mode

分析音频数据

要创建波形,我们需要分析音频数据。我们可以使用 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

绘制波形

接下来,我们将创建一个 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;
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

`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;
Enter fullscreen mode Exit fullscreen mode

结论

我们探索了如何在不使用任何外部库的情况下,在 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