Chrome 49 版網路音訊更新

克里斯威爾森
Chris Wilson

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

decodeAudioData() 現在會傳回 promise

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 讓非同步程式設計變得更簡單,也更具一致性。為達成相容性,系統仍支援「成功」和「錯誤」回呼函式 (根據規格要求)。

ManyAudioContext 現已支援暫停() 和 Resume()

乍看之下,在 OfflineAudioContext 中加入暫停() 似乎很奇怪。 畢竟,suspend() 已新增至 AudioContext,這樣可讓音訊硬體進入待機模式;當您轉譯至緩衝區時,這個模式看起來似乎無點不對 (OfflineAudioContext 的用途是這樣)。不過,這項功能的重點是一次只建構「分數」的一部分,盡量減少記憶體用量。在轉譯期間暫停時,您可以建立更多節點。

例如,貝多芬的 Moonlight Sonata 包含大約 6,500 則筆記。每個「note」可能至少解譯為兩個音訊圖節點節點 (例如 AudioBuffer 和 Get 節點)。如果您想使用 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

這項規格為語音愛好者新增了一個節點,方便他們建立專屬的精確 infinite-impulse-responseIIRFilterNode。這個篩選器可與 BiquadFilterNode 相輔相成,但允許篩選回應參數的完整規格 (而非 BiquadFilterNode 簡單易用的 AudioParams,適用於類型、頻率、Q 等),IIRFilterNode 可以指定之前無法建立的篩選器 (例如單順序篩選器);但使用 IIRFilterNode 需要深入瞭解 IIR 篩選器的運作方式,而且它們 (如 BiquadFilterNodes) 也無法排程。

先前的變更

此外,我想說明幾項先前改善的改善項目:在 Chrome 48 中,BiquadFilter 節點自動化功能已開始以音訊速率執行。API 完全不會變更,但這表示篩選器清除的指令會更順暢。在 Chrome 48 版中,我們也傳回了要連結的節點,藉此在 AudioNode.connect() 方法中新增鏈結。這樣可以更輕鬆地建立節點鏈,如以下範例所示:

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

以上就是今天要與各位分享的內容,請繼續保持!