写在最前:
最近在做一些新技术的调研,不巧在掘金上看到有人分享系列关于h5技术的运用,主要包括一些音频,媒体流,用户位置,环境光等H5核心设备接口的运用,所以自己准备照着那个目录动手做一番。这篇就是关于音频对象AudioContext做的小小DEMO,当然看了下别人的DEMO了,希望各位读者喜欢~
照
成果展示:
功能说明:
1.根据你在页面载入的音乐,以可视化的方式展示音乐的律动; 2.点击模式切换,以时域波形的方式展示当前音乐的节奏。
实现细节:
整个DEMO其实比较简单,主要就是熟悉一些跟Web Audio相关的API,所以讲解的时候我会介绍下本次DEMO用到的一些API的使用方式。还有,本次DEMO的一个难点就是你看到的条形图上顶部小方块儿的下落特效,这个我也会做下解释。页面中所看到的动画都是基于canvas绘制,这个我就不多说了。接下来,进入主题。
创建基于audio的语音环境,代码如下:
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
var audioContext = new AudioContext();
var analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
var audio = document.getElementById('audio');
var audioSrc = audioContext.createMediaElementSource(audio);
audioSrc.connect(analyser);
analyser.connect(audioContext.destination);
复制代码
代码就这么几行,挨个进行解释。
1.window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;
2.var audioContext = new AudioContext();
复制代码
创建语音环境上下文,以后关于语音的一切操作都是在这个环境下进行。其实就跟canvas的getContext操作一个意思。为了兼容不同的浏览器,加了不同的前缀。
3.var analyser = audioContext.createAnalyser();
4.analyser.fftSize = 2048;
复制代码
创建一个Analyser节点,可以通过该节点获取音频信号的事件和频率数据。然后通过语句4可以设置频域FFT(快速傅里叶变换)取样点的多少。它的取值fftSize 属性的值必须是从32到32768范围内的2的非零幂,默认值是2048。但是,我们用不到这么多的点,所以后来只是选取了部分点的值用于展示音乐节奏。
5.var audioSrc = audioContext.createMediaElementSource(audio);
复制代码
创建一个MediaElementAudioSourceNode接口来关联HTMLMediaElement. 这可以用来播放和处理来自video或audio 元素的音频。而且,我们还可以通过createMediaStreamSource()接口来获取计算机麦克风或者其他来源的音频流MediaStream信息。
6.audioSrc.connect(analyser);
7.analyser.connect(audioContext.destination);
复制代码
将analyser节点关联到目标音频环境上来,这里audioContext.destination返回AudioDestinationNode对象,AudioDestinationNode接口表示音频图形在特定情况下的最终输出地址,通常为扬声器。意思就是和音频设备关联。
获取音频数据,渲染可视化律动界面:
环境创建结束之后,我们就该取相关音频数据,基于canvas渲染可视化律动界面了。代码如下:
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
复制代码
analyser.frequencyBinCount该值获取fftSize值的一半,通常情况下是等于将要用于可视化的数据数值的数量。然后,通过analyser.getByteFrequencyData(array)函数,将当前频域数据拷贝进Uint8Array数组中。然后,就是本次的难点,如何渲染顶部降落方块儿。
var barWidth = 10;
var gap = 2;
var barNum = Math.round(cWidth / (barWidth + gap)); //绘制多少个条形
for (var i = 0; i < barNum; i++) {
var value = array[i];
if (topArr.length < barNum) {
topArr.push(value)
}
if (value < topArr[i]) {
ctx.fillRect(i * (barWidth + gap), cHeight - (topArr[i] -= 1) - topHeight, barWidth, topHeight);
} else {
ctx.fillRect(i * (barWidth + gap), cHeight - value - topHeight, barWidth, topHeight);
topArr[i] = value;
}
}
复制代码
根据canvas的宽度以及条形的宽度和间距我们计算了将要绘制的条形数量,然后该数量值也就决定了我们用来选取的音频数据的多少。对于顶部小方块儿,我们创建了一个数组,数组初始化值为音频数据,如果新的音频数据比之前对应的初始化的数据值要大,我们就直接绘制出顶部的小方块儿,然后根据新的音频数据更新初始化的数据值。反之,如果新来的音频数据比初始化的数据要小,这时候,通过初始值的逐渐减一效果,达到小方块儿不断降落的特效。最后,对于条形的渲染如下:
var grd = ctx.createLinearGradient(i * barWidth, cHeight - value, i * barWidth, cHeight);
grd.addColorStop(0, "yellow");
grd.addColorStop(0.3, "rgb(255,0,0)");
grd.addColorStop(0.5, "rgb(200,0,0)");
grd.addColorStop(0.7, "rgb(150,20,20)");
grd.addColorStop(1, "rgb(100,0,0)");
ctx.fillStyle = grd;
ctx.fillRect(i * (barWidth + gap), cHeight - value, barWidth, topArr[i]);
ctx.fill();
复制代码
这里,我们给条形的颜色加了一个线性的渐变,让其看上去稍微那么好看些哈~(好像并没有好看多少...)
波形模式的切换:
有了前面音频数据的条形渲染,这个就显得更简单了,就是数据源取值的不同而已,通过以下代码将时域数据拷贝进array数组里面,剩下的绘制就是通过canvas的moveTo以及lineTo的API了,我就不做详细的介绍了。
analyser.getByteTimeDomainData(array);
复制代码