音频可视化器是一种调剂你的音乐听觉体验的奇妙方式,这已不是什么秘密。在这篇文章中,我们将讨论如何从头开始创建一个音频可视化器,只使用虚构的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
,其中的id
,container
。这就是我们要存放canvas
和audio
元素的地方。
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 source
和analyzer
节点。
现在让我们来设置我们的音频源、分析器和上下文。
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
对象的实例。我们还声明了另外两个变量,audioSource
和analyser
,我们将用它们来创建我们的音源和分析器节点。
接下来,我们使用我们的音频上下文创建了我们的audioSource
和analyser
对象节点。
最后,我们将我们的audioSource
和analyser
节点相互连接,并连接到音频上下文的目的地(扬声器)。这个设置形成了一个节点链,音频将流经这个节点。
现在我们有了音频上下文和节点的设置,我们可以开始为我们的可视化软件编写代码了。
首先,让我们弄清楚我们要显示多少条,每条应该有多宽。
analyser.fftSize = 128;
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
const barWidth = canvas.width / bufferLength;
我们将分析器的fftSize
属性设置为128
。这将决定我们从声音中收集多少个数据点。数字越大,我们得到的数据点就越多,显示的条形图就越多。
我们要创建一个新的bufferLength
变量,并将其设置为等于分析器的frequencyBinCount
属性。这个属性告诉我们有多少个数据点是基于我们之前设置的fftSize
。frequenceBinCount
总是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
的位置画一个矩形,用我们之前设置的barWidth
和barHeight
。
然后我们用barWidth
来增加x
变量。这样做可以确保每个条形都是紧挨着前一个条形画出来的。
最后,我们调用requestAnimationFrame
方法,并将其传递给我们的animate
函数。这样,浏览器就会在每一帧上调用我们的animate
函数。
注意,由于我们是在
animate
函数中调用requestAnimationFrame
,这将导致animate
函数被反复调用,形成一个循环。
下面是最终结果的样子。
祝贺你!你已经建立了一个简单的条形图显示器!在下一节中,我们将对它进行一些改进。
添加颜色和打磨用户界面
本教程中最难的部分已经过去了。在这一节中,我们将为我们的可视化器添加一些颜色,并对用户界面进行一些抛光。
添加颜色
我们目前的展示台充满了白色的矩形,这并不令人兴奋。让我们给它添加一些颜色吧!
这是你可以自行试验的地方。没有一种方法可以增加随机性,所以我们将利用我们在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;
}
你可以自由地用上面的值进行实验,或者想出你自己的值
下面是它的样子。
中间的柱子更高了
我们可以做的另一个改进是使我们的条形图在中间高一些,在两边短一些。这就是你看到的大多数条形图的样子。
同样,在这一点上,我们只对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
}
};
在这里,我们对我们的数据点进行了两次循环。第一次,我们从画布的中心开始,从右到左做动画,第二次,我们从左到右。正如我提到的,左半边的频率较高的条形图在右边,反之,右半边也是如此。
这是我的最终结果。
就这样了!现在你知道如何使用 Web Audio API 和 Canvas 从头开始创建一个音频可视化工具了。
总结
在这篇文章中,我们已经介绍了如何从头开始创建我们自己的音频展示台。
我们首先讨论了分析器节点的不同部分以及每个部分的作用。从那里,我们继续创建我们的数据数组,并在animate
函数的帮助下使其成为动画。最后,我们对我们的可视化工具做了一些小的改进,改变了颜色并使我们的条形图在中间变高。
当然,你还可以用它做更多的事情,所以请随意发挥,尽情地发挥吧