使用Web Audio與three.js實現音樂視覺化
簡介
HTML5可以不借助其他的外掛僅僅通過瀏覽器實現非常酷炫的事情,而這篇文章就是關於藉助three.js以及Web Audio介面來實現聲音視覺化的。Web Audio允許你在瀏覽器端操作音訊,需要了解更多關於Web Audio的相關資訊請移步至這 MDN Web Audio API。而關於THREE.JS,官網上說是一個讓建立WebGL應用變得簡單的javascript 3D庫。這篇文章的目的就是以Web Audio介面獲取音訊資訊並且通過three.js實現3D視覺化。
讓我們首先把過程分成單個的任務:
- 建立一個Three.js場景並新增一些長方形條進去
- 獲取聲音檔案資訊
- 將音訊資訊解析並且渲染在檢視上
…
示例
可以點這來檢視已經完成的示例
…
建立1個Three.js場景
建立一個3d場景並能讓其看到需要下面三個最主要的元素:
- 場景
- 相機
- 渲染器
function AudioVisualizer(){
//渲染
this.scene;
this.camera;
this.renderer;
this.controls;
}
AudioVisualizer類裡面放置了所有必要的東西,下面需要一個一個地初始化three.js的必要元素,這裡展示的函式用於建立一個Threejs場景,一個WebGL渲染器,一個相機以及一個光源,如果你想要更多關於Three.js的資訊,。當然官網
AudioVisualizer.prototype.initialize = function(){
//建立ThreeJS 場景
this.scene = new THREE.Scene();
//獲取視窗長寬
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
//獲取渲染器
this.renderer = new THREE.WebGLRenderer({antialias:true});
this.renderer.setSize(WIDTH,HEIGHT);
//建立並且加入相機
this.camera = new THREE.PerspectiveCamera(40,WIDTH/HEIGHT,0.1,20000);
this.camera.position.set(0,45,0);
this.scene.add(this.camera);
var that = this;
//update renderer size, aspect ratio and projection matrix on resize
//更新渲染器大小,方向(橫豎)以及投影矩陣
window.addEventListener('resize', function () {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
that.renderer.setSize(WIDTH, HEIGHT);
that.camera.aspect = WIDTH / HEIGHT;
that.camera.updateProjectionMatrix();
});
//背景顏色
this.renderer.setClearColor(0x333F47, 1);
//建立光源並新增到場景中
var light = new THREE.PointLight(0xffffff);
light.position.set(-100, 200, 100);
this.scene.add(light);
}
一旦3d場景建立好之後,我們就能在裡面加入3d幾何體,createBars 函式建立了0.5x0.5x0.5的長方體(當然目前是正方體),每個長方體都有一個隨機的顏色,這樣看起來更酷炫一點23333,每個建立的條狀物(長方體)都儲存在一個數組中,便於之後根據音訊資訊來改變它們的形狀。
//建立視覺化所需的條狀物(長方體)
AudioVisualizer.prototype.createBars = function () {
//重複建立長方體
for (var i = 0; i < this.numberOfBars; i++) {
//長方體
var barGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
//材質
var material = new THREE.MeshPhongMaterial({
color: this.getRandomColor(),
ambient: 0x808080,
specular: 0xffffff
});
//建立幾何體框架並且放到特定的位置上
this.bars[i] = new THREE.Mesh(barGeometry, material);
this.bars[i].position.set(i - this.numberOfBars/2, 0, 0);
//將長方體放到場景中
this.scene.add(this.bars[i]);
}
};
…
讀取音訊資訊內容
現在我們有用來視覺化的3d場景和3d塊狀物(條狀物/長方體),下面就要從伺服器或者使用者自己的電腦裡獲得音訊檔案了,從伺服器請求一個特定的檔案並讓它視覺化確實沒啥問題,但是如果使用者自己可以把自己喜歡的音樂拉到裡面,讓他能夠親眼看到這牛逼的畫面不是更有趣麼。
下面的程式碼塊可以直接讀取拖到瀏覽器頁面的檔案,HTML真是牛逼壞了=。=
AudioVisualizer.prototype.handleDrop = function () {
//丟下檔案
document.body.addEventListener("drop", function (e) {
e.stopPropagation();
e.preventDefault();
//獲取檔案
var file = e.dataTransfer.files[0];
var fileName = file.name;
$("#guide").text("Playing " + fileName);
var fileReader = new FileReader();
fileReader.onload = function (e) {
var fileResult = e.target.result;
visualizer.start(fileResult); //這裡其實並沒有真的開始
};
fileReader.onerror = function (e) {
debugger
};
fileReader.readAsArrayBuffer(file);
}, false);
}
…
解析音訊資訊並渲染到場景中
終於到了最為關鍵的時刻了!音訊檔案資訊獲取之後,我們得把它傳遞給之前建立的3d場景中,為了能夠視覺化,還需建立一些Web Audio元素並且將它們連線起來,沒錯,你沒聽錯就是連線
這些元素是:
- AudioContext
- javascript Node
- Buffer Source
- Analyser
這些節點要以下面這樣的方式連結起來,我們不需要知道這些功能的具體細節,因為我們又不是給好萊塢做一個重磅音訊視覺化產品-。-
我們需要建立一個指令碼處理器(Script Processor),指令碼處理器是一個可以用javascript來做音訊輸出處理的音訊處理節點。
提醒一下,指令碼處理器已經被棄用了,具體細節請檢視這裡,本譯者將在另外一篇部落格中介紹更好的方法。
注意:這個特性在2014年8月29日釋出的Web Audio API規範中已經標記為不推薦,將很快會被Audio Workers代替.
下面我們將建立一個源緩衝來放置音訊,分析器(analyser)可以提供音訊的實時資訊,便於我們渲染與之相關的3d場景
首先將源緩衝與分析器連線起來,分析器連線javascript節點源緩衝連線destination,destination其實就是音訊輸出,如果不將destination與源緩衝連線起來的話,就算我們能看到顯示出來的東西,也不能聽到聲音
AudioVisualizer.prototype.setupAudioProcessing = function () {
//獲取AudioContext
this.audioContext = new AudioContext();
//建立javascript節點
this.javascriptNode = this.audioContext.createScriptProcessor(2048, 1, 1);//此處引數為緩衝區大小(2的倍數),輸入聲道與輸出聲道
this.javascriptNode.connect(this.audioContext.destination);
//建立音源
this.sourceBuffer = this.audioContext.createBufferSource();
//建立分析器
this.analyser = this.audioContext.createAnalyser();
this.analyser.smoothingTimeConstant = 0.3;
this.analyser.fftSize = 512;
//將音源與分析器連線
this.sourceBuffer.connect(this.analyser);
//分析器與javascript節點連線
this.analyser.connect(this.javascriptNode);
//分析器與音訊輸出連線(就是喇叭--。耳機等等)
this.sourceBuffer.connect(this.audioContext.destination);
var that = this;
//這裡就是將喇叭資訊經過javascript處理並且視覺化的過程
this.javascriptNode.onaudioprocess = function () {
// 通過分析器解析出音訊頻率與音強資訊
var array = new Uint8Array(that.analyser.frequencyBinCount);
that.analyser.getByteFrequencyData(array);
//渲染
visualizer.renderer.render(visualizer.scene, visualizer.camera);
var step = Math.round(array.length / visualizer.numberOfBars);
//迴圈改變不同長方體在z軸上的縮放
for (var i = 0; i < visualizer.numberOfBars; i++) {
var value = array[i * step] / 4;
value = value < 1 ? 1 : value;
visualizer.bars[i].scale.z = value;
}
}
};
'onaudioprocess’事件會在有聲音播放的時候一直觸發並提供音訊資訊,這個方法可以從分析器那獲取頻率資訊,渲染場景,根據不同頻率的音強變化改變長方體的z軸上的縮放
(譯者按:此處使用的onaudioprocess事件有效能上的問題,當建立的ScriptProcessor緩衝區大小為2048時,每秒只有22次左右的觸發,使用谷歌瀏覽器69.0,所以在顯示的時候長方體的變化看起來有點卡卡的,而且這個函式在沒有播放聲音的時候也會觸發,將其改為1024會好很多 (值越小則可能導致一定的延遲)(取值256, 512, 1024, 2048, 4096, 8192, 16384),其實也可以不寫)
…
開始音訊處理
所有用於處理的函式都設定好了,下面就要開始進行音訊處理。我們從檔案讀取器(fileReader)讀取檔案並且用"audioContext.decodeAudioData"這個方法來解析出音訊資訊。這個方法有兩個回撥,一個是解析失敗,一個是解析成功,解析成功之後將其放置在一個緩衝之中,然後就可以通過"start()"函式開始播放音訊,當音訊播放的時候"javascriptNode.onaudioprocess"將會被持續觸發並輸出當前時間的音訊頻率資訊,在3d場景中獲得這些資訊並把它展現出來
//開始處理音訊資訊
AudioVisualizer.prototype.start = function (buffer) {
this.audioContext.decodeAudioData(buffer, decodeAudioDataSuccess, decodeAudioDataFailed);
var that = this;
function decodeAudioDataSuccess(decodedBuffer) {
//decodedBuffer為解析成功之後的音訊緩衝
that.sourceBuffer.buffer = decodedBuffer
that.sourceBuffer.start(0);
}
function decodeAudioDataFailed() {
debugger
}
};
…
工具
下面的方法用於生成一個隨機的顏色,來源於stackoverflow上面的一個問題的答案:隨機顏色生成
AudioVisualizer.prototype.getRandomColor = function () {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
可以讓場景放大縮小旋轉的three.js小工具,OrbitControls.js,
將其新增到初始化的函式中(initialize)
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
並將下面這行程式碼放到’onaudioprocess’函式中
visualizer.controls.update();
…
完整程式碼
結語
這篇文章提供了一個用WebAudio介面和three.js將音訊資訊視覺化的方法,目前看來javascriptNode,HTML5的WebAudio 介面還不夠穩定,這些程式碼可能會失效(但是我會跟進的),html5可以做很多瘋狂而又酷炫的事情,敲下這些程式碼的時候我還是蠻開心的,做出來的結果也非常滿意,繼續用html5震撼世界吧 233333.
引用
連結
文章來源