基于Web Audio API实现音频可视化效果


Posted in Javascript onJune 12, 2020

网页音频接口最有趣的特性之一它就是可以获取频率、波形和其它来自声源的数据,这些数据可以被用作音频可视化。这篇文章将解释如何做到可视化,并提供了一些基础使用案例。
基本概念节
要从你的音频源获取数据,你需要一个 AnalyserNode节点,它可以用 AudioContext.createAnalyser() 方法创建,比如:

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();

然后把这个节点(node)连接到你的声源:

source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);

// etc.
注意: 分析器节点(Analyser Node) 不一定输出到另一个节点,不输出时也可以正常使用。但前提是它必须与一个声源相连(直接或者通过其他节点间接相连都可以)。

分析器节点(Analyser Node) 将在一个特定的频率域里使用快速傅立叶变换(Fast Fourier Transform (FFT) )来捕获音频数据,这取决于你给 AnalyserNode.fftSize 属性赋的值(如果没有赋值,默认值为2048)。

注意: 你也可以为FFT数据缩放范围指定一个最小值和最大值,使用AnalyserNode.minDecibels 和AnalyserNode.maxDecibels进行设置,要获得不同数据的平均常量,使用 AnalyserNode.smoothingTimeConstant。阅读这些页面以获得更多如何使用它们的信息。

要捕获数据,你需要使用 AnalyserNode.getFloatFrequencyData()AnalyserNode.getByteFrequencyData() 方法来获取频率数据,用 AnalyserNode.getByteTimeDomainData() 或 AnalyserNode.getFloatTimeDomainData() 来获取波形数据。

这些方法把数据复制进了一个特定的数组当中,所以你在调用它们之前要先创建一个新数组。第一个方法会产生一个32位浮点数组,第二个和第三个方法会产生8位无符号整型数组,因此一个标准的JavaScript数组就不能使用 —— 你需要用一个 Float32Array 或者 Uint8Array 数组,具体需要哪个视情况而定。

那么让我们来看看例子,比如我们正在处理一个2048尺寸的FFT。我们返回 AnalyserNode.frequencyBinCount 值,它是FFT的一半,然后调用Uint8Array(),把frequencyBinCount作为它的长度参数 —— 这代表我们将对这个尺寸的FFT收集多少数据点。

analyser.fftSize = 2048;
var bufferLength = analyser.frequencyBinCount;
var dataArray = new Uint8Array(bufferLength);

要正确检索数据并把它复制到我们的数组里,就要调用我们想要的数据收集方法,把数组作为参数传递给它,例如:

analyser.getByteTimeDomainData(dataArray);

现在我们就获取了那时的音频数据,并存到了我们的数组里,而且可以把它做成我们喜欢的可视化效果了,比如把它画在一个HTML5 <canvas> 画布上。

创建一个频率条形图节
另一种小巧的可视化方法是创建频率条形图,
现在让我们来看看它是如何实现的。

首先,我们设置好解析器和空数组,之后用 clearRect() 清空画布。与之前的唯一区别是我们这次大大减小了FFT的大小,这样做的原因是为了使得每个频率条足够宽,让它们看着像“条”而不是“细杆”。

analyser.fftSize = 256;
 var bufferLength = analyser.frequencyBinCount;
 console.log(bufferLength);
 var dataArray = new Uint8Array(bufferLength);
 canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

接下来我们写好 draw() 函数,再一次用 requestAnimationFrame() 设置一个循环,这样显示的数据就可以保持刷新,并且每一帧都清空一次画布。

function draw() {
  drawVisual = requestAnimationFrame(draw);
  analyser.getByteFrequencyData(dataArray);
  canvasCtx.fillStyle = 'rgb(0, 0, 0)';
  canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
  }

现在我们来设置一个 barWidth 变量,它等于每一个条形的宽度。理论上用花布宽度除以条的个数就可以得到它,但是在这里我们还要乘以2.5。这是因为有很多返回的频率区域中是没有声音的,我们每天听到的大多数声音也只是在一个很小的频率区域当中。在条形图中我们肯定不想看到大片的空白条,所以我们就把一些能正常显示的条形拉宽来填充这些空白区域。

我们还要设置一个条高度变量 barHeight,还有一个 x 变量来记录当前条形的位置。

var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;

像之前一样,我们进入循环来遍历 dataArray 数组中的数据。在每一次循环过程中,我们让条形的高度 barHeight 等于数组的数值,之后根据高度设置条形的填充色(条形越高,填充色越亮),然后在横坐标 x 处按照设置的宽度和高度的一半把条形画出来(我们最后决定只画高度的一半因为这样条形看起来更美观)。

需要多加解释的一点是每个条形竖直方向的位置,我们在 HEIGHT-barHeight/2 的位置画每一条,这是因为我想让每个条形从底部向上伸出,而不是从顶部向下(如果我们把竖直位置设置为0它就会这样画)。所以,我们把竖直位置设置为画布高度减去条形高度的一半,这样每个条形就会从中间向下画,直到画布最底部。

for(var i = 0; i < bufferLength; i++) {
  barHeight = dataArray[i]/2;
  canvasCtx.fillStyle = 'rgb(' + (barHeight+100) + ',50,50)';
  canvasCtx.fillRect(x,HEIGHT-barHeight/2,barWidth,barHeight);
  x += barWidth + 1;
  }
 };

和刚才一样,我们在最后调用 draw() 函数来开启整个可视化过程。

draw();

这些代码会带来下面的效果:

基于Web Audio API实现音频可视化效果

源码:

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8"/>
 <title>可视化音乐播放器</title>
</head>
<body>
<input type="file" name="" value="" id="musicFile">
<p id="tip"></p>
<canvas id="casvased" width="500" height="500"></canvas>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
//随机变颜色
function randomRgbColor() { //随机生成RGB颜色
 var r = Math.floor(Math.random() * 256); //随机生成256以内r值
 var g = Math.floor(Math.random() * 256); //随机生成256以内g值
 var b = Math.floor(Math.random() * 256); //随机生成256以内b值
 return `rgb(${r},${g},${b})`; //返回rgb(r,g,b)格式颜色
}
//随机数 0-255
function sum (m,n){
var num = Math.floor(Math.random()*(m - n) + n);
 
}
console.log(sum(0,100));
console.log(sum(100,255));
//展示音频可视化
var canvas = document.getElementById("casvased");
var canvasCtx = canvas.getContext("2d");
//首先实例化AudioContext对象 很遗憾浏览器不兼容,只能用兼容性写法;audioContext用于音频处理的接口,并且工作原理是将AudioContext创建出来的各种节点(AudioNode)相互连接,音频数据流经这些节点并作出相应处理。
//总结就一句话 AudioContext 是音频对象,就像 new Date()是一个时间对象一样
var AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
if (!AudioContext) {
 alert("您的浏览器不支持audio API,请更换浏览器(chrome、firefox)再尝试,另外本人强烈建议使用谷歌浏览器!")
}
var audioContext = new AudioContext();//实例化
// 总结一下接下来的步骤
// 1 先获取音频文件(目前只支持单个上传)
// 2 读取音频文件,读取后,获得二进制类型的音频文件
// 3 对读取后的二进制文件进行解码
$('#musicFile').change(function(){
 if (this.files.length == 0) return;
 var file = $('#musicFile')[0].files[0];//通过input上传的音频文件
 var fileReader = new FileReader();//使用FileReader异步读取文件
 fileReader.readAsArrayBuffer(file);//开始读取音频文件
 fileReader.onload = function(e) {//读取文件完成的回调
 //e.target.result 即为读取的音频文件(此文件为二进制文件)
 //下面开始解码操作 解码需要一定时间,这个时间应该让用户感知到
 var count = 0;
 $('#tip').text('开始解码')
 var timer = setInterval(function(){
  count++;
  $('#tip').text('解码中,已用时'+count+'秒')
 },1000)
 //开始解码,解码成功后执行回调函数
 audioContext.decodeAudioData(e.target.result, function(buffer) {
  clearInterval(timer)
  $('#tip').text('解码成功,用时共计:'+count+'秒')
  // 创建AudioBufferSourceNode 用于播放解码出来的buffer的节点
  var audioBufferSourceNode = audioContext.createBufferSource();
  // 创建AnalyserNode 用于分析音频频谱的节点
  var analyser = audioContext.createAnalyser();
  //fftSize (Fast Fourier Transform) 是快速傅里叶变换,一般情况下是固定值2048。具体作用是什么我也不太清除,但是经过研究,这个值可以决定音频频谱的密集程度。值大了,频谱就松散,值小就密集。
  analyser.fftSize = 256;
  // 连接节点,audioContext.destination是音频要最终输出的目标,
  // 我们可以把它理解为声卡。所以所有节点中的最后一个节点应该再
  // 连接到audioContext.destination才能听到声音。
  audioBufferSourceNode.connect(analyser);
  analyser.connect(audioContext.destination);
  console.log(audioContext.destination)
  // 播放音频
  audioBufferSourceNode.buffer = buffer; //回调函数传入的参数
  audioBufferSourceNode.start(); //部分浏览器是noteOn()函数,用法相同
  //可视化 创建数据
  // var dataArray = new Uint8Array(analyser.fftSize);
  // analyser.getByteFrequencyData(dataArray)//将数据放入数组,用来进行频谱的可视化绘制
  // console.log(analyser.getByteFrequencyData)
  var bufferLength = analyser.frequencyBinCount;
  console.log(bufferLength);
  var dataArray = new Uint8Array(bufferLength);
  console.log(dataArray)
  canvasCtx.clearRect(0, 0, 500, 500);
  function draw() {
  drawVisual = requestAnimationFrame(draw);
  analyser.getByteFrequencyData(dataArray);
  canvasCtx.fillStyle = 'rgb(0, 0, 0)';
		//canvasCtx.fillStyle = ;
  canvasCtx.fillRect(0, 0, 500, 500);
  var barWidth = (500 / bufferLength) * 2.5;
  var barHeight;
  var x = 0;
  for(var i = 0; i < bufferLength; i++) {
   barHeight = dataArray[i];
		 //随机数0-255 Math.floor(Math.random()*255) 
		 // 随机数 10*Math.random()
   canvasCtx.fillStyle = 'rgb(' + (barHeight+100) + ','+Math.floor(Math.random()*(20- 120) + 120)+','+Math.floor(Math.random()*(10 - 50) + 50)+')';
   canvasCtx.fillRect(x,500-barHeight/2,barWidth,barHeight/2);
   x += barWidth + 1;
  }
  };
  draw();
 });
 }
})
</script>
</html>

 注意:

本文中的案例展现了 AnalyserNode.getByteFrequencyData() 和 AnalyserNode.getByteTimeDomainData() 的用法。如果想要查看AnalyserNode.getFloatFrequencyData() 和 AnalyserNode.getFloatTimeDomainData() 的用法,请参考我们的 Voice-change-O-matic-float-data 演示(也能看到 源代码 )——它和本文中出现的 Voice-change-O-matic 功能完全相同,唯一区别就是它使用的是浮点数作数据,而不是本文中的无符号整型数。

《参考:https://github.com/mdn/voice-change-o-matic  》
《案例一:https://mdn.github.io/voice-change-o-matic/  》
《案例二:http://margox.github.io/vudio.js/  》

总结

到此这篇关于基于Web Audio API实现音频可视化效果的文章就介绍到这了,更多相关Web Audio API实现音频可视化内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript获取鼠标点击元素对象(示例代码)
Dec 20 Javascript
js实现用户离开页面前提示是否离开此页面的方法(包括浏览器按钮事件)
Jul 18 Javascript
jQuery进行组件开发完整实例
Dec 15 Javascript
javascript产生随机数方法汇总
Jan 25 Javascript
关于List.ToArray()方法的效率测试
Sep 30 Javascript
AngularJS Phonecat实例讲解
Nov 21 Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 Javascript
JS 调试中常见的报错问题解决方法
May 20 Javascript
jQuery实现简单的滑动导航代码(移动端)
May 22 jQuery
JavaScript基于扩展String实现替换字符串中index处字符的方法
Jun 13 Javascript
JS中跳出循环的示例代码
Sep 14 Javascript
js实现多图和单图上传显示
Dec 18 Javascript
js实现mp3录音通过websocket实时传送+简易波形图效果
Jun 12 #Javascript
学前端,css与javascript重难点浅析
Jun 11 #Javascript
微信小程序 scroll-view的使用案例代码详解
Jun 11 #Javascript
基于ajax及jQuery实现局部刷新过程解析
Sep 12 #jQuery
微信小程序scroll-view实现滚动到锚点左侧导航栏点餐功能(点击种类,滚动到锚点)
Jun 11 #Javascript
多页vue应用的单页面打包方法(内含打包模式的应用)
Jun 11 #Javascript
VUE页面中通过双击实现复制表格中内容的示例代码
Jun 11 #Javascript
You might like
php json_encode奇怪问题说明
2011/09/27 PHP
PHP获取用户的浏览器与操作系统信息的代码
2012/09/04 PHP
浏览器预览PHP文件时顶部出现空白影响布局分析原因及解决办法
2013/01/11 PHP
php使用mb_check_encoding检查字符串在指定的编码里是否有效
2013/11/07 PHP
文本框只能选择数据到文本框禁止手动输入
2013/11/22 Javascript
jQuery中事件对象e的事件冒泡用法示例介绍
2014/04/25 Javascript
jquery 选取方法都有哪些
2014/05/18 Javascript
jquery中获取元素里某一特定子元素的代码
2014/12/02 Javascript
基于jquery实现发送文章到手机的代码
2014/12/26 Javascript
JS 通过系统时间限定动态添加 select option的实例代码
2016/06/09 Javascript
jQuery为某个div加入行样式
2017/06/09 jQuery
微信小程序页面跳转功能之从列表的item项跳转到下一个页面的方法
2017/11/27 Javascript
vue项目中axios使用详解
2018/02/07 Javascript
jQuery控制input只能输入数字和两位小数的方法
2019/05/16 jQuery
vue自动化路由的实现代码
2019/09/30 Javascript
JavaScript 正则应用详解【模式、欲查、反向引用等】
2020/05/13 Javascript
Vue是怎么渲染template内的标签内容的
2020/06/05 Javascript
[52:31]VP vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
python迭代器的使用方法实例
2013/11/21 Python
python正则表达式匹配[]中间为任意字符的实例
2018/12/25 Python
Django渲染Markdown文章目录的方法示例
2019/01/02 Python
python实现移位加密和解密
2019/03/22 Python
python实现定时发送邮件
2020/12/23 Python
HTML5 canvas基本绘图之绘制五角星
2016/06/27 HTML / CSS
如何用Lucene索引数据库
2016/02/23 面试题
最新计算机专业自荐信
2013/10/16 职场文书
中医临床专业自我鉴定范文
2014/01/15 职场文书
初二物理教学反思
2014/01/29 职场文书
高考备战决心书
2014/03/11 职场文书
歌颂祖国演讲稿
2014/05/04 职场文书
学校查摆问题整改措施
2014/09/28 职场文书
2014年幼儿园学期工作总结
2014/12/05 职场文书
暑期实践个人总结
2015/03/06 职场文书
2015年信访维稳工作总结
2015/04/07 职场文书
小学生暑假生活总结
2015/07/13 职场文书
职工宿舍管理制度
2015/08/05 职场文书