<audio>
标签是HTML5的新标签,经过添加src
属性实现音乐播放。javascript
AudioContext
是音频播放环境,原理与canvas的绘制环境相似,都是须要建立环境上下文,经过上下文的调用相关的建立音频节点,控制音频流播放暂停操做等操做,这一些操做都须要发生在这个环境之中。html
try{
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}catch(e){
alert('Web Audio API is not supported in this browser');
}
复制代码
AudioNode
接口是一个处理音频的通用模块,它能够是音频音源模块,音频播放设备模块,也能够是中间音频处理模块。不一样的音频节点的链接(经过AudioContext.connect()
),以及终点链接AudioContext.destination
(能够看做是链接到耳机或扬声器设备)完成后,才能输出音乐。java
常见的音频节点:
AudioBufferSourceNode: 播放和处理音频数据
AnalyserNode: 显示音频时间和频率数据 (经过分析频率数据能够绘制出波形图之类的视图,可视化的主要途径)
GainNode: 音量节点,控制音频的总音量
MediaElementAudioSourceNode: 关联HTMLMediaElement,播放和处理来自<video>和<audio>元素的音频
OscillatorNode: 一个周期性波形,只建立一个音调
...
复制代码
try{
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}catch(e){
alert('Web Audio API is not supported in this browser');
}
复制代码
因为音频文件的数据是二进制(非文本),因此要设置请求头的responseType
为arraybuffer
,将.mp3音频文件转换成数组缓冲区ArrayBuffergit
当AudioContext.decodeAudioData
解码成功以后获取buffer
,执行回调函数,将数据放入AudioBufferSourceNode
中github
方法一采用流式加载音乐文件,简单易懂,缺点是经过createMediaElementSource
加载的src文件必须是同源,不容许跨域web
下面步骤主要根据方法2。canvas
<audio src="1.mp3"></audio>
<script>
let audio = document.querySelector('audio');
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
audio.addEventListener('canplay', function () {
let source = audioCtx.createMediaElementSource(audio);
source.connect(audioCtx.destination);
audio.play()
})
</script>
复制代码
let xhr = new XMLHttpRequest();
xhr.open('GET', '1.mp3', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
audioCtx.decodeAudioData(xhr.response, function (buffer) {
getBufferSuccess(buffer)
})
}
复制代码
let input = document.querySelector('input');
input.addEventListener('change', function () {
if (this.files.length !== 0) {
let file = this.files[0];
let fr = new FileReader();
fr.onload = function () {
let fileRet = e.target.result;
audioCtx.decodeAudioData(fileRet, function (buffer) {
getBufferSuccess(buffer);
}, function (err) {
console.log(err)
})
}
fr.readAsArrayBuffer(file);
}
})
复制代码
function getBufferSuccess(buffer) {
// 建立频率分析节点
let analyser = audioCtx.createAnalyser();
// 肯定频域的快速傅里叶变换大小
analyser.fftSize = 2048;
// 这个属性可让最后一个分析帧的数据随时间使值之间的过渡更平滑。
analyser.smoothingTimeConstant = 0.6;
// 建立播放对象节点
let source = audioCtx.createBufferSource();
// 填充音频buffer数据
source.buffer = buffer;
// 建立音量节点(若是你须要用调整音量大小的话)
let gainNode = audioCtx.createGain();
// 链接节点对象
source.connect(gainNode);
gainNode.connect(analyser);
analyser.connect(audioCtx.destination);
}
复制代码
// 此方法须要补充节点的链接
let javascriptNode = audioCtx.createScriptProcessor(2048, 1, 1);
javascriptNode.connect(audioCtx.destination);
analyser.connect(javascriptNode);
this.javascriptNode.onaudioprocess = function () {
currData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(currData);
}
复制代码
获取AnalyserNode
节点里的频率长度frequencyBinCount
,实例化长度为8位的整型数组,经过AnalyserNode.getByteFrequencyData
将节点中的频率数据拷贝到数组中去,值的大小在0 - 256
之间,数值越高代表频率越高;AnalyserNode.getByteTimeDomainData
原理同样,不过获取的是频率大小,两种方法根据需求选一种便可。api
function getData () {
// analyser.frequencyBinCount 可视化值的数量,是前面fftSize的一半
let currData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(currData);
analyser.getByteTimeDomainData(currData);
}
复制代码
AudioBufferSourceNode.start(n)
n表示开始的时间,默认为0,开始播放音频 AudioBufferSourceNode.stop(n)
音频在第n秒时间中止,若没有传值表示当即中止跨域
AudioContext.resume()
控制音频的播放
AudioContext.suspend()
控制音频的暂停
AudioContext.currentTime
获取当前音频播放时间
AudioBufferSourceNode.buffer.duration
获取音频的播放总时长
GainNode.gain.value
控制音量大小 [0, 1]
GainNode.gain.linearRampToValueAtTime
实现音量的渐入渐出数组
了解上面的api,就能够来着手绘制啦~,你想绘啥就绘啥,频繁的调用canvas的api很耗性能问题,这里讲下我在测试中提升性能的小技巧。
在切换歌曲中,遇到了这个报错Failed to set the 'buffer' property on 'AudioBufferSourceNode': Cannot set buffer to non-null after it has been already been set to a non-null buffer at AudioContext
,大体是讲AudioBufferSourceNode
的buffer属性在以前我已经设置过了,不能被从新设置新的buffer值,因为播放歌曲主要是经过其数组缓冲区ArrayBuffer来进行,可看看issue,解决办法就是当须要切换歌曲状况下,将当前的AudioBufferSourceNode
销毁,从新建立上下文环境,音频节点,链接等操做。
源码在这,交互部分写得有点乱,由于当时原来只是想练练可视化,以后想到啥功能就加,因此致使代码看起来冗余繁琐,你们能够参考看看audio
实现,主要在MusicPlay
对象。
小白第一次发表博文,发现写博文比写一个demo还要时间长,怕写出来的东西有错误会误导你们(有错误请你们评论指出~),因此会去查不少相关资料,这个过程也是学习的过程,之后会常常写写博文滴!最后,但愿你们经过这篇文章也能学会本身作这种可视化的效果,配合一些可视化库还能作出很酷炫的效果呢,一块儿互相学习进步吧,加油!(。・д・。)