音谱可视化:Audio API开发
效果展示&介绍
本篇的可视化代码主要是根据MIT的2014年2月15日的开源代码修改而来的
MIT音谱可视化->Github
MIT音谱可视化图:

进行修改的音谱可视化图

MIT的音频其实是从通过input标签来获取的,存入缓存中.但是其实我们用的比较多的是html5中的Audio标签,所以将其修改过后,便可对audio的src资源进行读取,并且可以用audio的控制按钮进行音频流的控制.

分析过程
要想做到音谱可视化的结果,那么我们需要什么?
1.获取音频,那么我们可以用audio直接获取音频资源(当然也可以input获取,本篇不做相关介绍)
2.得到音频的频率大小,我们可以使用web Audio API来获取到数值
3.通过频率数值画出波形线,我们可以用画布(canvas)来描绘图形.
获取音频
我们可以直接在html文档中加入audio标签即可:
<audio src="./mp3/Approaching Nirvana - You.mp3" id="audio"></audio>
audio的更多信息参考W3cschool
得到音频的频率大小
这里需要使用到的就是audio api了.我们可以在MSD了解到audio api 的使用方法(网址:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API)
在这里我们做简要的介绍:首先创建audioContext对象->需考虑浏览器不同厂商的兼容问题.然后从audio标签中获得媒体资源,并通过audioContext对象的创造媒体节点的函数创造节点.将媒体节点连接到分析音频的处理上,最后输出(audioContext.deestination).我们在分析音频的这个节点上对音频的频率再做提取,并将其可视化.
画出可视化图形
很明显这里需要用到的就是canvas标签和其后的javascript的相关知识.在这里我们描绘的是曲线所以要了解如何用canvas描绘巴塞尔曲线:
绘制一条二次巴塞尔曲线
代码呈现
在编写代码时,参考MIT的开源代码,我们可以创建一个Visualizer对象来实现我们的功能:
var Visualizer = function()
{
this.audioContext = null;//音频上下文
this.source = null;//音频资源
this.animationId = null;//动画ID
this.status = 0;//标志来判断是否在播放
this.forceStop = false;
}
Visualizer.prototype =
{
ini: function()//初始化函数
{
this._preparetionAPI();
},
_preparetionAPI: function()//准备Audio API
{
//针对不同浏览器厂商
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame;
//requestAnimationFrame->请求动画
//异常处理,若创建audioContext对象出错则警告其error
try
{
this.audioContext = new AudioContext();
}catch( e ){
alert( e );
console.log(e);
}
//调用开始函数
this._start();
},
_start:function()
{
var audioContext = this.audioContext;
if( audioContext === null )
{
return ;
}
//开始可视化
this._visualize(audioContext);
},
_visualize:function(audioContext)
{
var myAudio = document.getElementById("audio");//获取音频资源
var audioSourceNode = audioContext.createMediaElementSource(myAudio);//生成了媒体资源节点
var gainNode = audioContext.createGain();//创建Gain节点
var analyser = audioContext.createAnalyser();//创造分析节点
var that = this;
audioSourceNode.connect(gainNode);
gainNode.connect(analyser);
analyser.connect(audioContext.destination);//节点间的连接操作
if (!audioSourceNode.start) {
audioSourceNode.start = audioSourceNode.noteOn //in old browsers use noteOn method
audioSourceNode.stop = audioSourceNode.noteOff //in old browsers use noteOff method
};
if( this.animationId !== null )
{
cancelAnimationFrame( this.animationId );//针对变换曲目的重新可视化的操作,取消上一曲目的可视化
}
if( this.source !== null )
{
this.source.stop(0);
}
this.status = 1;
this.source = audioSourceNode;
audioSourceNode.onended =function()
{
that._audioEnd(that);//结束操作
};
this._drawQuadratic(analyser);//开始描绘
},
_drawQuadratic: function( analyser )
{
var that = this;
var canvas = document.getElementById("canvas");//获取画布对象
var cwidth = canvas.width;
var cheight = canvas.height;//获取画布的长和宽
var meterNum = 110;//波形图的波数
var ctx = canvas.getContext('2d'),
gradient = ctx.createLinearGradient(0,0,0,300);
gradient.addColorStop(1, "#bd4cce");
gradient.addColorStop(0.6, '#005c58');
gradient.addColorStop(0, '#6e00ff');//创建渐变style
var drawMeter =function()//单次的描绘函数
{
var array = new Uint8Array( analyser.frequencyBinCount );
analyser.getByteFrequencyData(array);//获取频率大小,并集合为一个数组
if( !that.status )
{
cancelAnimationFrame( that.animationId );//判断是否终止动画
return;
}
var step = Math.round( array.length / meterNum );//根据我们需要的波数设置跨度
ctx.clearRect(0, 0, cwidth, cheight );//每次调用的清屏操作
var field_ini = 5;//设置每个波的半个周期
var sum_field = 0;//总的周期
var value_low = 1;//降低波的频率大小,以适应画布的大小
for( var i = 0; i < meterNum; i++ )//开始描绘
{
var value = array[array.length - (i+1) * step ];//获取频率,这里是从低频开始获取
value /= value_low;//降低频率值
if( i >= meterNum - 2 )//最后两个波不显示为直线,为了好看
{
value = 0;
}
if( i % 2 == 0 )//震荡效果
{
value = -value;
}
ctx.beginPath();
ctx.strokeStyle = gradient; //set the strokeStyle to gradient for a better look
//一下四步操作是为了描绘一个波的操作,这里不做详解,自行研究
ctx.moveTo(sum_field, cheight / 2 );
ctx.quadraticCurveTo(sum_field + 2 * ( field_ini / 6 ), cheight / 2 + value , sum_field + field_ini / 2, cheight / 2 + value);
ctx.moveTo( sum_field + field_ini, cheight / 2);
ctx.quadraticCurveTo(sum_field + 4 * ( field_ini / 6 ), cheight / 2 + value , sum_field + field_ini / 2, cheight / 2 + value);
sum_field += field_ini;
field_ini += 0;
if( i < 10 )
{
value += 0.05;
}
else if( i > 10 && i < 45 )
{
value_low += 0.5;
}
else if( i > 80 )
{
value_low -= 0.55;
}
ctx.stroke();
}
//获取动画ID
that.animationId = requestAnimationFrame(drawMeter);
}
//调用函数,并获取动画ID
this.animationId = requestAnimationFrame(drawMeter);
},
_audioEnd: function()
{
if( this.forceStop )
{
this.forceStop = false;
this.status = 1;
return ;
};
this.status = 0;
}
}
当然我们需要在页面加载的时候就生成这个对象,所以:
window.onload = function()
{
new Visualizer().ini();
}
浙公网安备 33010602011771号