1. 效果演示
- 上图演示了音频的波形随着音乐的节奏而跌宕起伏
2. 基本概念说明
2.1 音频源节点
- 音频数据的来源,是音频处理的其中一个环节
2.2 时域图
- 声音信号是一维的时域信号,无法观察出频率随时间的变化规律
- 横轴代表时间, 纵轴是信号的变化(振幅)
2.3 频谱图
- 任何信号(当然要满足一定的数学条件),都可以通过傅立叶变换而分解成一个直流分量(也就是一个常数)和若干个(一般是无穷多个)正弦信号的和
- 每个正弦分量都有自己的频率和幅值,频率值作横轴,幅值作纵轴
3. 案例代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas在音频可视化的应用</title>
</head>
<body>
<canvas></canvas>
<audio src="./1.mp3" controls></audio>
<script>
const audio = document.querySelector('audio')
const canvas = document.querySelector('canvas')
const con = canvas.getContext('2d')
// 初始化canvas的尺寸
canvas.width = window.innerWidth // 窗口的内部宽度
canvas.height = window.innerHeight / 2 //窗口的内部高度
document.body.style.backgroundColor = '#000000' // 背景颜色设置为黑色
// 定义数组和音频分析器
let dataArray, analyser
audio.onplay = () => {
// 初始化
const audCon = new AudioContext() // 创建音频上下文
const source = audCon.createMediaElementSource(audio) // 创建音频源节点
// 创建音频分析器
analyser = audCon.createAnalyser()
// 快速傅里叶变换的长度必须为2的整数幂
analyser.fftSize = 512 // 这里把快速傅里叶变换的长度设置为512
// 连接音频源节点到音频分析器上
source.connect(analyser)
// 连接音频分析器到音频终端上
analyser.connect(audCon.destination)
// 创建数组,用于接收分析器节点的分析数据
// Uint8Array 数组类型表示一个 8 位无符号整型数组
// frequencyBinCount是fftSize值的一半作为可视化音频的数组长度
dataArray = new Uint8Array(analyser.frequencyBinCount)
}
// 把分析出的波形绘制到canvas上
(function draw() {
// 动画帧,按帧绘制canvas
requestAnimationFrame(draw)
// 清空画布
const { width, height } = canvas
con.clearRect(0, 0, width, height)
// 让分析器节点分析出数据到数组中
analyser.getByteFrequencyData(dataArray)
// 设置canvas上下文绘制的颜色
con.fillStyle = 'skyblue'
// len表示获取分析到的音频数据数组长度的
// 这里除以2.5是剔除不经常出现的高频的部分
const len = dataArray.length / 2.5
// barWidth表示每个波形矩形的宽度
// 这里除以2是为了绘制对称的波形图
const barWidth = width / len / 2
for (let i = 0; i < len; i++) {
// data是8位数组的每个数据,因为是一个字节,即data的值都是 <= 255
const data = dataArray[i]
// barHeight表示每个波形矩形的高度,值为单位长度乘canvas容器的高
const barHeight = (data / 255) * height
// 绘制点y
const y = height - barHeight
// 绘制点x1
const x1 = i * barWidth + width / 2
// 绘制点x2
const x2 = width / 2 - (i + 1) * barWidth
// 绘制右半部分波形图
con.fillRect(x1, y, barWidth - 2, barHeight)
// 绘制左半部分波形图
con.fillRect(x2, y, barWidth - 2, barHeight)
}
})()
</script>
</body>
</html>