到目前为止,我们仅讨论了音频的合成与处理,但这仅是 Web Audio API 提供的一半功能。另一半功能则是音频的分析,它播放起来应该是什么样子的。它最典型的例子就是音频可视化,但其实有更多的其它应用场景,包括声调检测,节减检测,语音识别等,这些已大大超出本书范围。

对于游戏或交互式应用开发者来说,这是一个重要的主题,原因有几点。首先,一个好的可视化分析器可以用于类似调式工具(显然这是除了你耳朵之外,良好的计量工具)用于调音。其次,对于某些关于音乐相关的游戏或应用来说可视化是重点比如游戏“吉它英雄”或者应用软件 GarageBand (苹果电脑上吉它教学软件)

频率分析

在 Web Audio API 分析声音是最主要的方式利用 AnalyserNodes。这些节点不会对声音本身做任何改变,可以在音频上下文任意处调用。一旦在音频图中创建了这样的节点,它就会提供两种主要方式用于查看声音波形:时域和频域上

得到的结果是基于特定缓冲区大小的 FFT 分析。我们有一些定制化节点输出的属性可用:

  • fftSize

    定义缓冲区大小用于实现分析。大小一定是2的幂。较高的值将导致对信号进行更细粒度的分析,但代价是一些性能损失。

  • frequencyBinCount

    这个属性是只读的,自动为 fftSize / 2。

  • smoothingTimeConstant

    值范围是 0 - 1. 值为1会导致较大的移动平均平滑结果。值为零意味着没有移动平均线,结果波动很快。

最基本的设置就是把分析节点插到我们感兴趣的音频图谱中:

// 假设节点A与B普普通通相连
var analyser = context.createAnalyser();
A.connect(analyser);
analyser.connect(B);

然后我们就可以得到时域或频域的数组了:

var freqDomain = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatFrequencyData(freqDomain);

在上面的代码例子中,freqDomain 是一个频域 32 位浮点数组。这些数组内存储的值都被标准化为 0-1。输出的指标可以在0和奈奎斯特频率之间线性映射,奈奎斯特频率被定义为采样率的一半(在 Web Audio API 中通过 context.sampleRate 获取)。下面的代码片段将 frequency 映射到频率数组中的正确位置:

奈奎斯特频率是离散信号系统采样频率的一半也就是 1/2 fs,fs为采样频率

function getFrequencyValue(frequency) {
var nyquist = context.sampleRate/2;
var index = Math.round(frequency/nyquist * freqDomain.length);
return freqDomain[index];
}

如果我们分析的是一个 1000 Hz 的正弦波,举例,我们期望调用 getFrequencyValue(1000) 时返回图像内的峰值,如图 5-1。

频域通过调用 getByteFrequencyData 使用8位无符号存储也可以。 这些值就是无符号整型,在分析节点( analyzer node)它会缩放以适配在最大分贝和最小分贝之间(即在 dBFS中,decibels full scale)。因此可以根据需要调整这些参数以缩放输出。

图 5-1,一个 1000Hz 的可视化声音(全频域是从 0 至 22050Hz)

requestAnimationFrame 实现动画

如果我们想要对我们的声音进行可视化,我们需要周期性的查询分析节点(analyzer node), 处理返回的结果,并渲染出来。我们可以利用 JavaScript 的定时器实现,setInterval, setTimeout, 但现在有更好用的:requestAnimationFrame。该 API 允许浏览器将你的自定义绘制函数合并到浏览器本地渲染循环中,这对性能来讲会有很大提升。不同于指定固定绘制间隔需要等待浏览器空闲时才来处理你的定时器不同,你只需要将它提交到队列中,浏览器会以最快的速度执行它。

由于 requestAnimationFrame 还是处于实验性质,你需要为其指定浏览器前缀,且给它定一个相似功能的 setTimeout 来兜底。代码如下:

window.requestAnimationFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();

一但定义好了 requestAnimationFrame 函数,我们需要利用它来查询分析节点得到音频流的详细信息。

requestAnimationFrame 现在早就加入肯德基豪华午餐了直接用就可以了

声音可视化

把它们全组合在一起,设置一个渲染循环用于查询和渲染之前用到的分析节点,将存进 freqDomain 数组:

var freqDomain = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqDomain);
for (var i = 0; i < analyser.frequencyBinCount; i++) {
var value = freqDomain[i];
var percent = value / 256;
var height = HEIGHT * percent;
var offset = HEIGHT - height - 1;
var barWidth = WIDTH/analyser.frequencyBinCount;
var hue = i/analyser.frequencyBinCount * 360;
drawContext.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
drawContext.fillRect(i * barWidth, offset, barWidth, height);
}

对时域也可以进行类似的操作

var timeDomain = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteTimeDomainData(timeDomain);
for (var i = 0; i < analyser.frequencyBinCount; i++) {
var value = timeDomain[i];
var percent = value / 256;
var height = HEIGHT * percent;
var offset = HEIGHT - height - 1;
var barWidth = WIDTH/analyser.frequencyBinCount;
drawContext.fillStyle = 'black';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}

此代码将时域内的值利用 HTML5 canvas 绘制,创建一个简单的可视化图形,在代表频域数据的彩色色条状图的顶部绘制了一个波形线条。

结果在 canvas 上绘制出来应该如图 5-2

图 5-2 某一时刻的可视化截图

以上分析器节点的代码实现 demo 可参考 https://github.com/willian12345/WebAudioAPI/tree/master/examples/ch05/demo.html

我们处理可视化方案遗漏了很多数据。但对音乐的可视化来说足够了。当然如果我们想综合全面分析整个音频缓冲区,我们需要看看其它的方法。

额外部分--显示整个声音文件的音量高低图

这一部分并非 Web Audio API 书说中述,是译者本人所述

这是我在项目中遇到的一个问题

网上一堆例子都是显示实时音频信号的,就像上一节中的那样

可是如果我想要的是显示整段 mp3 文件的音量高低图呢?

即如何分析整断音频的数据?

原理是加载 mp3 文件后解码分析音频数据,获取某一段音频数据的采样最高和最低点并绘制出来

首先就是从获取音频文件开始 利用 html 的 <input type="file" /> 标签获取 file 后:

const reader = new FileReader();
reader.onload = function (e) {
const audioContext = new (window.AudioContext ||
window.webkitAudioContext)();
audioContext.decodeAudioData(e.target.result, function (buffer) {
// 获取音频缓冲区数据
const channelData = buffer.getChannelData(0);
// 绘制波形
drawWaveform(channelData);
});
};
reader.readAsArrayBuffer(file);

利用 FileReader 以 ArrayBuffer 形式读取文件内容

再使用 audioContext.decodeAudioData 解码

解码后获取 channelData, 此时的 channelData 就包含该通道的音频样本数据,可以理解为标准化后的 PCM 数据

如果忘记了什么是 PCM 可以回顾第一章的内容

如图 5-5 只要解析这个 channelData 内的 PCM 数据并绘制出来就行了 drawWaveform(channelData)

function drawWaveform(data) {
const canvas = document.getElementById("waveform"); // 获取canvas元素
const ctx = canvas.getContext("2d"); // 获取2D绘图上下文
const width = canvas.width; // canvas的宽度
const height = canvas.height; // canvas的高度
const step = Math.ceil(data.length / width); // 计算每个画布像素对应的音频样本数
const amp = height / 2; // 放大因子,用于控制波形在画布上的高度 ctx.fillStyle = "#fff"; // 设置填充颜色为白色
ctx.fillRect(0, 0, width, height); // 填充整个画布为白色 ctx.beginPath(); // 开始绘制新的路径
ctx.moveTo(0, amp); // 将绘图游标移动到画布中央的起始点 // 绘制波形
for (let i = 0; i < width; i += 4) {
// 遍历画布的每一个像素
let min = 1.0; // 初始化最小值
let max = -1.0; // 初始化最大值
for (let j = 0; j < step; j++) {
// 遍历与当前像素对应的音频样本 const datum = data[i * step + j]; // 获取单个音频样本
if (datum < min) min = datum; // 更新最小值
if (datum > max) max = datum; // 更新最大值
} ctx.lineTo(i, (1 + min) * amp); // 绘制从当前位置到最小值的线 ctx.lineTo(i, (1 + max) * amp); // 绘制从当前位置到最大值的线 }
ctx.stroke(); // 根据路径绘制线条 }

图 5-5 加载 test1.mp3 后显示的图

可参考 https://github.com/willian12345/WebAudioAPI/tree/master/examples/ch05/volume-visualization1.html

步骤:

  1. 根据 canvas 的 width 确定采样数据范围()宽度

    const step = Math.ceil(data.length / width);

  2. 在 step 采样数据范围循环找出最高与最低音量

for (let j = 0; j < step; j++) {
// 遍历与当前像素对应的音频样本 const datum = data[i * step + j]; // 获取单个音频样本
if (datum < min) min = datum; // 更新最小值
if (datum > max) max = datum; // 更新最大值
}
  1. 有了音量高低的值,直接绘制线条或柱型就可以了
ctx.lineTo(i, (1 + min) * amp); // 绘制从当前位置到最小值的线
ctx.lineTo(i, (1 + max) * amp); // 绘制从当前位置到最大值的线

把线条独立开后加点色彩或许更好看

function drawWaveform(data) {
const canvas = document.getElementById("waveform"); // 获取canvas元素
const ctx = canvas.getContext("2d"); // 获取2D绘图上下文
const width = canvas.width; // canvas的宽度
const height = canvas.height; // canvas的高度
const step = Math.ceil(data.length / width); // 计算每个画布像素对应的音频样本数
const amp = height / 2; // 放大因子,用于控制波形在画布上的高度 ctx.fillStyle = "#fff"; // 设置填充颜色为白色
ctx.fillRect(0, 0, width, height); // 填充整个画布为白色 ctx.beginPath(); // 开始绘制新的路径
ctx.moveTo(0, amp); // 将绘图游标移动到画布中央的起始点 // 绘制波形
for (let i = 0; i < width; i += 4) {
// 根据 i 遍历画布的宽度
let min = 1.0; // 初始化最小值
let max = -1.0; // 初始化最大值
ctx.moveTo(i, amp);
for (let j = 0; j < step; j++) {
// 遍历与当前像素对应的音频样本 const datum = data[i * step + j]; // 获取单个音频样本
if (datum < min) min = datum; // 更新最小值
if (datum > max) max = datum; // 更新最大值
}
var hue = (i / width) * 360; ctx.beginPath()
ctx.strokeStyle = "hsl(" + hue + ", 100%, 50%)";
ctx.moveTo(i, amp);
ctx.lineTo(i, (1 + min) * amp); // 绘制从当前位置到最小值的线
ctx.stroke(); // 根据路径绘制线条 ctx.beginPath()
ctx.moveTo(i, amp);
ctx.lineTo(i, (1 + max) * amp); // 绘制从当前位置到最大值的线
ctx.stroke(); // 根据路径绘制线条
}
}

图 5-3

可参考 https://github.com/willian12345/WebAudioAPI/tree/master/examples/ch05/volume-visualization2.html

还可以把 i 的间隔缩小,再把 step 的大小也缩小试试,我得到了下面 图 5-4 效果

图 5-4

在网上见过有人用扇形或者螺旋形来可视化音频。效果也是相当的酷

请自行搜索,只要得到数据了实现起来还是比较简单的


注:转载请注明出处博客园:王二狗Sheldon池中物 (willian12345@126.com)

Web Audio API 第5章 音频的分析与可视化的更多相关文章

  1. 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况

    对比audio标签 和 Web Audio API 各平台浏览器的支持情况:   audio element Web Audio API desktop browsers Chrome 14 Yes  ...

  2. 【HTML5】Web Audio API打造超炫的音乐可视化效果

    HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我!  源码已经挂到github上了,有兴趣的同学也可以去st ...

  3. 关于Web Audio API的入门

    Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...

  4. 使用Web Audio API绘制音波图

    摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...

  5. H5的Web Audio Api

    概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...

  6. Web Audio API之手把手教你用web api处理声音信号:可视化音乐demo

    1.Web Audio API 介绍 Web Audio API 提供了在Web上控制音频的一个非常有效通用的系统 ,这些通用系统通俗的讲就是我们可以利用Web Audio API提供的各种方法操作各 ...

  7. HTML5 ——web audio API 音乐可视化(二)

    上一篇 web audio API 音乐可视化(一)介绍了一些基本的API,以及如何简单的播放一个音频,本篇介绍一下怎么对获取到的音频进行分析,并将分析后的数据绘制成图像. 最终效果请戳这里; 完整版 ...

  8. HTML5 ——web audio API 音乐可视化(一)

    使用Web Audio API可以对音频进行分析和操作,最终实现一个音频可视化程序. 最终效果请戳这里; 完整版代码请戳这里,如果还看得过眼,请给一个start⭐ 一.API AudioContext ...

  9. 【Web Audio API】 — 那些年的 web audio

    转 TAT.Jdo:[Web Audio API] - 那些年的 web audio 这主题主要是早期对 web audio api的一些尝试,这里整理一下以便以后翻阅,如有错误,诚请指正. 在这之前 ...

  10. [Javascript] Intro to the Web Audio API

    An introduction to the Web Audio API. In this lesson, we cover creating an audio context and an osci ...

随机推荐

  1. mysql标识列和事务

    1 #标识列 2 /* 3 又称为自增长列 4 含义:可以不用手动的插入值,系统提供默认的序列值 5 6 7 特点: 8 1.标识列必须和主键搭配吗?不一定,但要求是一个key 9 2.一个表可以有几 ...

  2. npm install 的执行顺序,和 安装包的源死磕

    npm install 源的地址加载执行顺序 从近到远 lock文件 这里直接就记录了 包的下载地址 .npmrc 里面的内容 registry=http://registry.npm.xxxx.co ...

  3. YCProgress自定义百分比进度条

    目录介绍 1.本库优势亮点 2.使用介绍 2.1 圆环百分比进度条 2.2 直线百分比进度条 2.3 仿杀毒类型百分比进度条 3.注意要点 4.效果展示 5.其他介绍 1.本库优势亮点 圆环百分比进度 ...

  4. 三维模型3DTile格式轻量化的跨平台兼容性问题分析

    三维模型3DTile格式轻量化的跨平台兼容性问题分析 三维模型3DTile格式是一种开放的.高效的和互操作的空间信息数据格式.然而,它作为一种新兴的技术,其在轻量化与跨平台兼容性方面存在着一些问题. ...

  5. 恶意软件开发(一)Reverse Shell

    什么是Reverse Shell? 反向 Shell(Reverse Shell)是指远程攻击者在攻击成功后,通过建立一个反向连接,让受害者的机器连接到攻击者的机器上,从而达到控制受害者机器的目的.通 ...

  6. Tableau 绘制圆环图

    一.对应数据如下所示 二.打开tableau连接对应Excel数据源,将记录数字段连续拖动两次到行,显示设置按整个视图显示,标记里面设置按饼图显示 三.设置两个值按度量值平均值显示,并调整第一个图稍微 ...

  7. 《Go程序设计语言》学习笔记之defer

    <Go程序设计语言>学习笔记之defer 一. 环境 Centos8.5, go1.17.5 linux/amd64 二. 概念 语法上,一个 defer 语句就是一个普通的函数或方法调用 ...

  8. OREPA:阿里提出训练也很快的重参数策略,内存减半,速度加倍 | CVPR 2022

    论文提出了在线重参数方法OREPA,在训练阶段就能将复杂的结构重参数为单卷积层,从而降低大量训练的耗时.为了实现这一目标,论文用线性缩放层代替了训练时的BN层,保持了优化方向的多样性和特征表达能力.从 ...

  9. 如何安装和使用Docker

    本文深入解析Docker,一种革命性的容器化技术,从其基本概念.架构和组件,到安装.配置和基本命令操作.文章探讨了Docker在虚拟化.一致性环境搭建及微服务架构中的关键作用,以及其在云计算领域的深远 ...

  10. 【已解决】初始化 Hive 元数据库报错slf4j-log4j12-1.7.25.jar包冲突

    错误log描述 [root@hadoop102 hive]# schematool -initSchema -dbType mysql -verboseSLF4J: Class path contai ...