“沧海一声唱 滔滔两岸潮
浮沉随浪歌舞今朝
苍天唱 纷纷世上潮
谁负谁胜出天知晓
江山唱 烟雨遥
涛浪淘尽红尘俗世几多娇
清风唱 竟惹寂寥
豪情还剩了一襟晚照
苍生唱 不再寂寥
豪情仍在痴痴唱唱”
—— 题记,《沧海一声唱》
正文
在上文 JS | Web Audio API (上) 你的音谱 中,我们了解到Audio API简单的音频知识点,重在理论,今天搞点有趣的试验,偏重实践。大家知道,光有光谱,电磁波有频谱,音乐呢?当然也有自己的谱。想奥斯特实验揭示电流周围存在磁场,分散的铁屑显现磁铁的磁场分布,那音乐如何看到自身的频率,所以,本文的主题来了,音频可视化,让你的音乐浪起来。先附上效果图,接下来会主要围绕效果例子出发:
这种音波似的效果,我们可能会在音乐室或音乐人或音乐播放器那儿看到,并不少见,当第一次发现可以实现时,ohMyGod,震撼,神奇,而对于喜欢的事物,总会想为我所用,闲话不多说,一起看看它是怎么实现的吧。根据已有的web audio API知识,实践音频可视化,自我总结,步骤大致分为以下几步:
- 创建音频环境
- 获取音频,创建buffer节点
- 解码音频,分析音频
- 连接音频输入输出
- canvas绘制频谱
- 连接播放
创建音频环境AudioContext
音频环境是所有音效操作的前提,好比canvas的画布,先有个做画之地,再来笔墨横姿
// Webkit/blink browser require a prefix, and it needs the window object specifically declared to work in Safari
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
// declare new audio context
try {
var audioCtx = new AudioContext();
} catch (e) {
alert('Your browser does not support AudioContext!');
console.log(e);
}
获取音频,创建buffer节点
首先获取音频,也就是说拿到这个素材输入之后,我们可以赶制加工,这里通过XMLHttpRequest获取,将请求的返回类型设为“arraybuffer”,方便音频数据处理;另外,创建音频节点createBufferSource,来获取输入的音频。
// use XHR to load an audio track, and
// decodeAudioData to decode it and stick it in a buffer.
// Then we put the buffer into the source
var xhr = new XMLHttpRequest();
// 初始化 HTTP 请求参数, 配置请求类型,文件路径等
xhr.open('GET', 'audio/music1.mp3');
// 将responseType设为arraybuffer,二进制数据
xhr.responseType = "arraybuffer";
// 获取完成,对音频进一步操作,解码
xhr.onload = function() {
var audioData = xhr.response;
// Get an AudioBufferSourceNode.
// This is the AudioNode to use when we want to play an AudioBuffer
var source = audioCtx.createBufferSource();
……
}
解码音频,分析音频
好的,现在我们拿到了音乐,但计算机仍然不懂,需要对其进行解码decodeAudioData。
一看到解码后的数据,我们不能让计算机“啪啪啪”就来吧,观个全局,做个自我分析,createAnalyser。
audioCtx.decodeAudioData(audioData, function(buffer) {
// set the buffer in the AudioBufferSourceNode
source.buffer = buffer;
// create audio node to play the audio in the buffer
var analyser = audioCtx.createAnalyser();
}
连接音频输入输出
必经之路,input ——> 音频处理 ——> 输出,connect连接。
// connect the analyser to the destination(the speaker), or we won't hear the sound
// from audioCtx.createBuffer, or audioCtx.decodeAudioData
source.connect(analyser);
analyser.connect(audioCtx.destination);
canvas绘制频谱
大头戏,音乐播放捣鼓捣鼓还是有声音的,频谱怎么着,一头雾水。不着急,慢慢来,首先我们需要数据,数据怎么来:
var bufferLength = analyser.frequencyBinCount,
dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
好,数据有了,计算机也能懂,怎么画,先说个简单的:
var canvas = document.getElementById('audio_canvas'),
ctx = canvas.getContext("2d"),
c_width = canvas.width,
c_height = canvas.height;
**************
for(var i = 0; i < bufferLength; i++) {
value = dataArray[i];
ctx.fillStyle = '#f99';
ctx.fillRect(i, c_height - value, 1, value);
}
好了,频谱图有了,但没有动效,不会变化,别急,利用requestAnimationFrame,同时这侧面反应了获取的dataArray数组的数值,出来的效果如此这般:
可是我们想,如果把所有的数值都展现出来,一来太多,二来更耗资源,而且频率邻值是相似的,非智者所为,怎么处理呢?数学中有学过采样频率的方法,采样对于信息信号来说,是个常用的方式。根据画布长度,美观起见,让每一频占据一定宽度,各个频之间留些空隙,同时用数学逻辑思维换算,计算出画布可放的频数,也就是说画布上选择哪几个频率值显示,取相对应“编号”的频率,进行绘制。
// 条形的宽度
var bar_width = 10,
bar_gap = 2,
bar_part = bar_width + bar_gap,
bar_num = Math.round(c_width / bar_part);
***************************************
function drawVisual() {
var i = 0, value;
var bufferLength = analyser.frequencyBinCount,
dataArray = new Uint8Array(bufferLength);
// 每段包含的频谱宽
var array_width = Math.round(bufferLength / bar_num);
analyser.getByteFrequencyData(dataArray);
ctx.clearRect(0,0,c_width,c_height)
for(i; i < bar_num; i++) {
value = dataArray[i * array_width];
ctx.fillStyle = '#f99';
ctx.fillRect(bar_part * i, c_height - value, bar_width, value);
}
animation_id = requestAnimationFrame(drawVisual);
// console.log(animation_id)
}
思考
如此一来,大致效果已经实现。在做的过程中,有一个问题需要思考: 动画什么时候停止,也就是说,如何在音乐播放结束的情况下,页面频谱流畅地回归空白,浏览器也不会继续动画,做到“该停止时就停止”。【实践结果证明,如果在音乐播放结束就停止动画或者清空,达不到想要的效果】
为了美观及更有趣味性,我们可以加个缓慢降落的条形;甚者,采取上传文件的形式,根据上传的音乐“舞动”自己的音浪,因频制浪。这里有个稍难的点:已经播放一首音乐的时候,如何做到继续上传,原音乐停止,新音乐播放并出现相应的频谱。
【代码存在于 github,仅供参考,敬请交流】
参考文章:
https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwj9xpzE2K_SAhUNtJQKHQQMCu4QFggaMAA&url=https%3a%2f%2fdeveloper%2emozilla%2eorg%2fzh-CN%2fdocs%2fWeb%2fAPI%2fFileReader&usg=AFQjCNGz5Veo8Ux5iQ_w_1oFQc3fqNlynA
http://www.cnblogs.com/Wayou/p/html5_audio_api_visualizer.html
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API
https://forestmist.org/blog/web-audio-api-loops#source