用vanilla JavaScript从头开始写一个音频可视化工具教程

338 阅读8分钟

音频可视化器是一种调剂你的音乐听觉体验的奇妙方式,这已不是什么秘密。在这篇文章中,我们将讨论如何从头开始创建一个音频可视化器,只使用虚构的JavaScript和内置的浏览器Canvas和Web Audio API。

在本文结束时,你将对如何创建自己的音频可视化工具有一个扎实的了解,并能够尝试不同的音频数据可视化方式。

数字音频如何工作

让我们先对数字音频的工作原理做个基本解释。

计算机并不像我们在物理世界中那样理解声音。为了把声音变成可以存储的数据,计算机做了一个叫做采样的事情。采样是通过测量撞击麦克风的声波作为数据点来完成的。一旦计算机有了这些数据点,也称为样本,它就可以把它们储存在文件中。

当我们想回放音频时,计算机会颠覆这个过程:它从这些记录的数据点中重现声音。我们将使用这些数据点来绘制我们的动态条形图。

现在的理论已经足够了--让我们进入一些代码吧

设置项目

尽管我们将使用vanilla JavaScript,但我们仍然需要设置一个服务器来跟随。我们需要一个服务器的原因是为了绕过CORS问题。

CORS防止从其域外访问资源。因此,为了分析音频数据,我们需要将我们的音频文件托管在与我们的网页相同的服务器上。

在本教程中,我使用Vite,这是一个简单明了的开发服务器,但也可以自由地使用你选择的任何其他服务器。

构建可视化器

为了建立可视化器,我们将使用两个内建的浏览器API。Canvas和Web Audio API。

Canvas是一个HTML5元素,允许我们在网页上绘制图形。在这种情况下,我们将使用它来绘制我们的动态条形图。

Canvas也被分成两个不同的API。2D和WebGL。我们将在本教程中使用2D版本,因为WebGL更多处理的是3D图形。

Web Audio API允许我们在浏览器中直接处理和播放音频文件。我们将用它来加载和播放我们的音频文件,并提取我们需要的原始数据来生成我们的可视化。

把这些必要的理论讲完了,让我们开始写代码吧!

首先,我们需要设置我们的HTML文档。我们将创建一个简单的HTML文档,其中包含画布和音频元素。

让我们把文件命名为index.html ;它将作为我们项目的一个入口。当你创建一个vanilla Vite项目时,它已经为你设置了一个index.html 文件。

<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="stylesheet.css" />
<title>Audio Visualizer</title>
</head>
<body>
<div id="container">
<canvas id="canvas"></canvas>
<audio id="audio"></audio>
</div>
<script src="script.js"></script>
</body>
</html>

重要的部分是在文件的body 。我们有一个div ,其中的idcontainer 。这就是我们要存放canvasaudio 元素的地方。

canvas 元素是我们要绘制动态条形图的地方。audio 元素是我们要用来播放音频文件的。我们要添加一个对script.js 文件的引用。这个文件将包含我们分析音频和生成视觉效果的所有代码。

我们还添加了简单的CSS样式,它添加了一个黑色背景以获得更好的对比度,并将我们的画布元素置于屏幕的中心。

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

#container {
position: absolute;
top:0;
left: 0;
background: #000;
width: 100%;
height: 100%;
}

#canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

现在让我们开始添加代码,使所有这些元素真正发挥作用。

首先,让我们设置对画布和音频元素的引用,并创建一些我们需要的其他变量。

let audio1 = new Audio();
audio1.src = "tune.mp3";

const container = document.getElementById("container");
const canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

我们正在创建一个新的Audio 对象,其src 属性被设置为我们想要播放的音频文件的位置。我们要把我们的canvas 元素的宽度和高度设置为等于浏览器窗口的大小。这样一来,我们的可视化将始终充满整个屏幕。

接下来,我们需要设置我们的音频源、分析器和音频背景对象。但首先,让我们介绍一下这些对象各自代表什么。

音频源是一个AudioNode对象,代表音频的来源。在我们的例子中,它将是我们添加的audio 元素。

分析器节点帮助我们了解声音的情况。它使我们有可能看到并使用来自声音的数据,这样我们就可以创建我们的可视化工具。分析器包含各种属性,如frequencyBinCount ,我们以后会用它来计算出我们需要收集多少数据点。

音频上下文负责管理所有的网络音频API节点。我们将用它来创建我们的audio sourceanalyzer 节点。

现在让我们来设置我们的音频源、分析器和上下文。

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let audioSource = null;
let analyser = null;

audio1.play();
audioSource = audioCtx.createMediaElementSource(audio1);
analyser = audioCtx.createAnalyser();
audioSource.connect(analyser);
analyser.connect(audioCtx.destination);

我们创建了一个新的AudioContext 对象的实例。我们还声明了另外两个变量,audioSourceanalyser ,我们将用它们来创建我们的音源和分析器节点。

接下来,我们使用我们的音频上下文创建了我们的audioSourceanalyser 对象节点。

最后,我们将我们的audioSourceanalyser 节点相互连接,并连接到音频上下文的目的地(扬声器)。这个设置形成了一个节点链,音频将流经这个节点。

现在我们有了音频上下文和节点的设置,我们可以开始为我们的可视化软件编写代码了。

首先,让我们弄清楚我们要显示多少条,每条应该有多宽。

analyser.fftSize = 128;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const barWidth = canvas.width / bufferLength;

我们将分析器的fftSize 属性设置为128 。这将决定我们从声音中收集多少个数据点。数字越大,我们得到的数据点就越多,显示的条形图就越多。

我们要创建一个新的bufferLength 变量,并将其设置为等于分析器的frequencyBinCount 属性。这个属性告诉我们有多少个数据点是基于我们之前设置的fftSizefrequenceBinCount 总是fftSize 的一半。

我们正在创建一个新的dataArray 变量,并将其设置为等于bufferLength 大小的Uint8Array 。这个数组将保存我们从声音中收集的所有数据点。

最后,我们要创建一个新的barWidth 变量。这个变量决定了我们的可视化器中每个条形图的宽度是多少。我们把它设置为等于画布的宽度除以bufferLength

现在让我们来编写执行实际动画的函数。

function animate() {
x = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
analyser.getByteFrequencyData(dataArray);
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
ctx.fillStyle = "white";
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth;
}

requestAnimationFrame(animate);
}

x 当我们绘制条形图时,我们用它来跟踪我们在X轴上的位置。我们清除画布,这样我们就可以从每一帧的空白处开始绘制。然后我们在 节点上调用 方法。这个方法接受一个数组作为其参数。我们将我们的 传递给这个方法。这将用声音的数据填充我们的 。analyser getByteFrequencyData dataArray dataArray

然后,我们用一个for 循环来浏览dataArray 数组。

对于每个项目,我们设置barHeight 变量等于数据点。我们用fillRect 方法在x,y 的位置画一个矩形,用我们之前设置的barWidthbarHeight

然后我们用barWidth 来增加x 变量。这样做可以确保每个条形都是紧挨着前一个条形画出来的。

最后,我们调用requestAnimationFrame 方法,并将其传递给我们的animate 函数。这样,浏览器就会在每一帧上调用我们的animate 函数。

注意,由于我们是在animate 函数中调用requestAnimationFrame ,这将导致animate 函数被反复调用,形成一个循环。

下面是最终结果的样子。

Simple Bar Visualizer

祝贺你!你已经建立了一个简单的条形图显示器!在下一节中,我们将对它进行一些改进。

添加颜色和打磨用户界面

本教程中最难的部分已经过去了。在这一节中,我们将为我们的可视化器添加一些颜色,并对用户界面进行一些抛光。

添加颜色

我们目前的展示台充满了白色的矩形,这并不令人兴奋。让我们给它添加一些颜色吧!

这是你可以自行试验的地方。没有一种方法可以增加随机性,所以我们将利用我们在for 循环中的动态值来产生一些随机的颜色。

let barHeight;
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
const red = (i * barHeight) / 10;
const green = i * 4;
const blue = barHeight / 4 - 12;
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth;
}

你可以自由地用上面的值进行实验,或者想出你自己的值

下面是它的样子。

Adding Color To Graphs

中间的柱子更高了

我们可以做的另一个改进是使我们的条形图在中间高一些,在两边短一些。这就是你看到的大多数条形图的样子。

同样,在这一点上,我们只对animate 。首先,让我们把我们的for 循环分离到自己的函数中,以提高可读性。

function animate() {
x = 0;
ctx.clearRect(0, 0, canvas.width, canvas.height);
analyser.getByteFrequencyData(dataArray);
drawVisualizer({ bufferLength, dataArray, barWidth });
requestAnimationFrame(animate);
}

const drawVisualizer = ({ bufferLength, dataArray, barWidth }) => {
let barHeight;
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
const red = (i * barHeight) / 10;
const green = i * 4;
const blue = barHeight / 4 - 12;
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
x += barWidth;
}
};

我们可以通过将我们的数据点分成两半,使我们的高位柱从中间上升。我们将把左边的高频率条放在右边,右边的高频率条放在左边的动画。

如果我们看一下代码,这将更有意义。

const drawVisualizer = ({ bufferLength, dataArray, barWidth }) => {
let barHeight;
for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
const red = (i * barHeight) / 10;
const green = i * 4;
const blue = barHeight / 4 - 12;
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
ctx.fillRect(
canvas.width / 2 - x, // this will start the bars at the center of the canvas and move from right to left
canvas.height - barHeight,
barWidth,
barHeight
);
x += barWidth; // increases the x value by the width of the bar
}

for (let i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
const red = (i * barHeight) / 10;
const green = i * 4;
const blue = barHeight / 4 - 12;
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); // this will continue moving from left to right
x += barWidth; // increases the x value by the width of the bar
}
};

在这里,我们对我们的数据点进行了两次循环。第一次,我们从画布的中心开始,从右到左做动画,第二次,我们从左到右。正如我提到的,左半边的频率较高的条形图在右边,反之,右半边也是如此。

这是我的最终结果。

Taller Bars In The Middle

就这样了!现在你知道如何使用 Web Audio API 和 Canvas 从头开始创建一个音频可视化工具了。

总结

在这篇文章中,我们已经介绍了如何从头开始创建我们自己的音频展示台。

我们首先讨论了分析器节点的不同部分以及每个部分的作用。从那里,我们继续创建我们的数据数组,并在animate 函数的帮助下使其成为动画。最后,我们对我们的可视化工具做了一些小的改进,改变了颜色并使我们的条形图在中间变高。

当然,你还可以用它做更多的事情,所以请随意发挥,尽情地发挥吧