先上个demo
音乐频谱AudioAPI的基本概念
H5的Web Audio API可以很方便的使用(虽然各家浏览器实现的标准都不太一样)各种音频功能,比如可以用麦克风录制声音然后生成音频文件,还可以对声源定位,做那种3D音效,混音等等,听过3D音效的都知道,声音一会左,一会右的,还支持实时的声音分析,根据分析的数据可以做一些可视化效果等等。
其中AudioContext是Web Audio API的基石,AudioContext对象可以“线性”处理字节流。
这个线性的意思就是用Web Audio的信号传递途径:
如图
AudioAPI的大部分操作都是通过一个个连接的节点完成的,这些节点需要连通,从声源节点到输出节点都必须保持通畅才能正常工作。就像在电路上连接一个个元件来完成不同的工作,最后连接到扬声器上。
最简单的路线就是从声源直接连接到输出节点:source → destination
输出节点在AudioContext实例的destination上可以找到,而声源节点却有很多。MediaElementSource、MediaStreamSource、BufferSource、Oscillator、createScriptProcessor,这些都是声源节点。它们都是可以产生波,并传输给下一个节点的。所以要让AudioAPI发出声音,最简单的代码可以这么写:
- var AudioContext = AudioContext||webkitAudioContext; var context = new AudioContext; var oscillator = context.createOscillator(); oscillator.connect(context.destination); oscillator.start(0);
直接从声源连接到输出节点是最简单的用法,AudioAPI还提供了很多节点用于对音频的特殊处理。比如Gain节点用来调整音量、BiquadFilter节点用来过滤一些数据。当数据从这些节点上流过时就会被做相应的处理。
这些节点的连接也不是一对一那么简单的,它可能一对多,比如一个声源头可以同时给多个节点处理,之后再把这些中间节点汇总起来,这就要关心这些节点的分支与汇总的问题了。
总之,AudioAPI的设计就像是电路设计,有机地组合它所提供的节点就可以完成几乎所有音频处理。
直接上代码
通过HTML Media元素流式加载
使用audio元素流式加载音乐文件, 在JavaScript中调用createMediaElementSource方法, 直接操作HTMLMediaElement。
<!--写一个audio标签,给一个音乐地址,
地址必须是本地音乐,不能使用跨域的音乐资源,运行代码还必须是服务器环境,
不然没效果,因为跨域限制,
加上controls(显示控制面板) autoplay(自动播放) loop(循环播放)这3个属性
-->
<audio id="audio" src="1.mp3" controls autoplay loop></audio>
现在audio标签可以用来播放音乐了。
创建canvas画布
- <style type="text/css"> #canvas { position: absolute; left: 0; top: 0; z-index: -1; background: #000; } </style> <canvas id="canvas"></canvas>
编写js脚本
>var audio = document.getElementById("audio");
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
//创建境况
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext();
//创建输入源
var source = audioContext.createMediaElementSource(audio);
//用createAnalyser方法,获取音频时间和频率数据,实现数据可视化。
var analyser = audioContext.createAnalyser();
//连接:source → analyser → destination
source.connect(analyser);
//声音连接到扬声器
analyser.connect(audioContext.destination);
/*存储频谱数据,Uint8Array数组创建的时候必须制定长度,
长度就从analyser.frequencyBinCount里面获取,长度是1024*/
var arrData = new Uint8Array(analyser.frequencyBinCount),
count = Math.min(500,arrData.length), //能量柱个数,不能大于数组长度1024,没意义
/*计算步长,每隔多少取一个数据用于绘画,意抽取片段数据来反映整体频谱规律,
乘以0.6是因为,我测试发现数组长度600以后的数据基本都是0了,
画出来能量柱高度就是0了,为了效果好一点,所以只取前60%,
如果为了真实可以不乘以0.6
*/
step = Math.round(arrData.length * 0.6 / count),
value = 0, //每个能量柱的值
drawX = 0, //能量柱X轴位置
drawY = 0, //能量柱Y轴坐标
height = canvas.height = window.innerHeight,//canvas高度
width = canvas.width = window.innerWidth,//canvas宽度
//能量柱宽度,设置线条宽度
lineWidth = context.lineWidth = canvas.width / count;
//设置线条宽度
context.lineWidth = lineWidth;
//渲染函数
function render() {
//每次要清除画布
context.clearRect(0, 0, width, height);
//获取频谱值
analyser.getByteFrequencyData(arrData);
for(var i = 0; i < count; i++) {
//前面已经计算好步长了
value = arrData[i * step + step];
//X轴位置计算
drawX = i * lineWidth;
/*能量柱的高度,从canvas的底部往上画,那么Y轴坐标就是画布的高度减去能量柱的高度,
而且经测试发现value正常一般都比较小,要画的能量柱高一点,所以就乘以2,
又防止太高,取了一下最大值,并且canvas里面尽量避免小数值,取整一下
*/
drawY = parseInt(Math.max((height - value * 2), 10));
//开始一条路径
context.beginPath();
/*设置画笔颜色,hsl通过这个公式出来的是很漂亮的彩虹色
H:Hue(色调)。0(或360)表示红色,120表示绿色,240表示蓝色,
也可取其他数值来指定颜色。取值为:0 - 360
S:Saturation(饱和度)。取值为:0.0% - 100.0%
L:Lightness(亮度)。取值为:0.0% - 100.0%
*/
context.strokeStyle = "hsl( " + Math.round((i * 360) / count) + ", 100%, 50%)";
//从X轴drawX,Y轴就是canvas的高度,也就是canvas的底部
context.moveTo(drawX, height);
//从X轴drawX,Y轴就是计算好的Y轴,是从下往上画,这么理解
context.lineTo(drawX, drawY);
/*stroke方法才是真正的绘制方法,顺便也相当于结束了这次的绘画路径,
就不用调用closePath方法了
*/
context.stroke();
}
//用requestAnimationFrame做动画
requestAnimationFrame(render);
}
//调用render函数
render();
//自适应处理
function resize(){
height = canvas.height = window.innerHeight;
width = canvas.width = window.innerWidth;
//能量柱宽度,设置线条宽度
context.lineWidth = lineWidth = canvas.width / count;
}
window.addEventListener("resize",resize,false);
最后其实我还有一个功能更完善的demo
音乐频谱(鼠标悬浮到页面左侧凸出的那里可以搜索,支持回车搜索,双击列表播放)a'ゞ『完美』版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明做一个酷酷的音乐频谱!