1. 程式人生 > >使用Web Audio與three.js實現音樂視覺化

使用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 與HTML5 Web Audio 介面實現音訊視覺化
讓我們首先把過程分成單個的任務:

  • 建立一個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.

引用

連結

文章來源