效果展示&介紹
本篇的可視化代碼主要是根據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();
}