Chrome 49 版網路音訊更新

Chris Wilson
Chris Wilson

Chrome 一直默默地改善對 Web Audio API 的支援。在 Chrome 49 中 (2016 年 2 月為 Beta 版,預計於 2016 年 3 月推出穩定版),我們更新了幾項功能以追蹤規格,並新增一個新節點。

decodeAudioData() 現在會傳回承諾

AudioContext 上的 decodeAudioData() 方法現在會傳回 Promise,啟用以 Promise 為基礎的非同步模式處理。decodeAudioData() 方法一向會將成功和錯誤回呼函式做為參數:

context.decodeAudioData( arraybufferData, onSuccess, onError);

不過,現在您可以改用標準 Promise 方法,處理解碼音訊資料的非同步性質:

context.decodeAudioData( arraybufferData ).then(
        (buffer) => { /* store the buffer */ },
        (reason) => { console.log("decode failed! " + reason) });

雖然在單一範例中,這看起來比較冗長,但 Promise 可讓非同步程式設計更容易且更一致。為了確保相容性,我們仍會依照規格支援 Success 和 Error 回呼函式。

OfflineAudioContext 現在支援 suspend() 和 resume()

乍看之下,在 OfflineAudioContext 上使用 suspend() 似乎很奇怪。畢竟,suspend() 是新增至 AudioContext 以便將音訊硬體設為待機模式,但在您將內容算繪至緩衝區的情況下,這似乎沒有意義 (當然,OfflineAudioContext 就是用於此用途)。不過,這項功能的重點在於一次只能建構「分數」的一部分,以盡量減少記憶體用量。您可以在渲染過程中暫停,並建立更多節點。

舉例來說,貝多芬的月光奏鳴曲含有約 6,500 個音符。每個「音符」可能會解構為至少一對音訊圖表節點 (例如 AudioBuffer 和 Gain 節點)。如果您想使用 OfflineAudioContext 將整個七分半鐘的內容算繪至緩衝區,可能不想一次建立所有節點。您可以改為以時間區塊建立這些檔案:

var context = new OfflineAudioContext(2, length, sampleRate);
scheduleNextBlock();
context.startRendering().then( (buffer) => { /* store the buffer */ } );

function scheduleNextBlock() {
    // create any notes for the next blockSize number of seconds here
    // ...

    // make sure to tell the context to suspend again after this block;
    context.suspend(context.currentTime + blockSize).then( scheduleNextBlock );

    context.resume();
}

這樣一來,您就能盡量減少在轉譯開始時需要預先建立的節點數量,並降低記憶體需求。

IIRFilterNode

規格已新增節點,方便音響迷建立自己的精確指定 無限衝激響應IIRFilterNode。這個篩選器可補足 BiquadFilterNode,但可完整指定篩選器回應參數 (而非 BiquadFilterNode 的易用 AudioParams,用於類型、頻率、Q 等)。IIRFilterNode 可精確指定先前無法建立的篩選器,例如單一順序篩選器;不過,使用 IIRFilterNode 需要對 IIR 篩選器的運作方式有一定程度的深入瞭解,而且也無法像 BiquadFilterNodes 一樣排程。

先前的異動

我也想提及先前實施的幾項改善:在 Chrome 48 中,BiquadFilter 節點自動化動作開始以音訊速率執行。為此,API 並未進行任何變更,但這表示您的濾波器掃描聲音會聽起來更流暢。在 Chrome 48 中,我們也透過傳回所連結的節點,將鏈結新增至 AudioNode.connect() 方法。這樣一來,您就能更輕鬆地建立節點鏈結,如這個範例所示:

sourceNode.connect(gainNode).connect(filterNode).connect(context.destination);

以上就是目前的資訊,祝你一切順利!