Web Audio 簡易教程


Web Audio提供了一個強大的音頻處理系統,在我們現有的業務場景中,很少有使用到Web Audio,很多時候用到也僅限於播放一段音頻。

除此之外,還能實現豐富的功能,比如:可視化、音色合成器、動態混音、聲音特效、3D空間音頻、均衡器、環境混響等,可以應用在音樂播放器、電子音樂軟件、游戲音效、音樂游戲、直播互動等領域。

這篇文章是我在學習Web Audio的過程中寫的一些總結和Demo,簡單介紹一些API基礎用法。

文章中所有示例:https://web-audio.johnsonlee.site/[1]

1. AudioContext

AudioContext為音頻處理提供一個上下文環境,相當於一個中央控制器,控制着音頻路由圖中的各個音頻模塊。

在開始音頻處理之前,都需要創建一個AudioContext實例,並且可以全局共享同一個。所有(相關)的音頻節點都需要包含在同一個AudioContext中,每個音頻節點,只能關聯一個AudioContext。

1.1 音頻節點

音頻節點即AudioNode,它是一個基類,作為一個音頻路由圖[2]中的基本單位,它的工作依賴於AudioContext

音頻節點擁有自己的輸入/輸出,可以通過connect方法將一個節點的輸出連接至另一個節點的輸入。比如我們可以將一個音頻節點連接至audioContext.destination節點來進行音頻播放。

audioBufferSourceNode.connect(audioContext.destination)
 

上面的audioContext.destination是音頻節點中的一種,音頻節點可以分為三類:

  • Source Node:能產生音頻的節點,只有輸出,沒有輸入。
  • Process Node:對音頻進行處理的節點,有輸入(可能有多個)和輸出。
  • Destination Node:通常為音頻播放設備,如揚聲器。

 

 

有的音頻處理節點會有多個輸出,比如ChannelSplitterNode,可以將音頻拆分為多個聲道,對應的,也有一個合並聲道的節點ChannelMergerNode,有多個輸入和一個輸出。

1.2 路由圖

Web Audio 提供的是模塊化的API,在AudioContext中,各個音頻節點的連接,構建了一個路由圖,audioContext控制着整個路由圖的運轉。比如下面一個簡單的音頻播放示例

const ac = new AudioContext()

const $audio = document.querySelector('#audio');
const sourceNode = ac.createMediaElementSource($audio); // 從audio標簽創建一個音頻源節點

const gainNode = ac.createGain(); // 創建一個增益節點
gainNode.gain.value = 0; // 將增益設置為0(相當於音量設置為0)

$audio.addEventListener('play', () => {
  gainNode.gain.exponentialRampToValueAtTime(1, 1); // 在1秒的時間內指數增長到1,實現一個播放漸入效果
});

sourceNode.connect(gainNode); // 音頻源節點連接到增益節點
gainNode.connect(ac.destination); // 增益節點連接到destination進行播放
 

2. 音頻源

web中音頻源包括:

  • audio節點
  • 網絡加載的音頻文件
  • 實時音頻流(webRTC、麥克風)
  • 能產生音頻信號的音頻節點(如:OscillatorNode)

2.1 從<audio>標簽創建音頻源

網絡加載的音頻文件,需要將其轉換成音頻源節點,才能連接到路由圖中,比如我們經常使用的<audio>標簽,它是不能直接連接到其它音頻節點的

const ac = new AudioContext();
const $audio = document.querySelector('#audio');
// create a source node from an audio 
const sourceNode = ac.createMediaElementSource($audio);
sourceNode.connect(/* other audio node */);
 

2.2 使用http請求加載音頻數據

我們也可以使用http請求,將二進制的音頻數據下載到本地后,再創建一個音頻源節點

const ac = new AudioContext();
const source = ac.createBufferSource();
source.connect(ac.destination);
fetch('xxx.mp3').then(res => res.arrayBuffer()).then(
  async buffer => {
    source.buffer = await ac.decodeAudioData(buffer);
    source.start();
  }
);
 

上面代碼中,我們將解碼后的數據(AudioBuffer[3])加載到了source.buffer屬性中,實際上,這里除了從網絡/本地加載音頻文件外,我們也可以自己創建一個buffer丟給source.buffer來制造一些聲音,比如下面的這個示例,使用隨機函數生成一個白噪聲音頻

https://codepen.io/JohnsonLeee/pen/bGqLXmZ?editors=0010[4]

AudioBuffer的設計是為了存儲短時間的音頻片段,不建議將特別長的音頻通過BufferSource方式進行播放,更推薦使用上一種方案。

2.3 使用內置振盪器產生聲音

OscillatorNode是一個振盪器節點,它可以產生一個周期信號,可以指定type為squaresinesawtoothtrianglecustom, 默認為sine。其次需要設置frequency屬性來設定頻率,它是AudioParam類型,不能直接賦值。

const ac = new AudioContext();
const oscillator = ac.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = 440; // 不能直接給frequency負值,可以設置其value
// oscillator.frequency.exponentialRampToValueAtTime(440, 1) // 也可以通過它提供的方法來設置
oscillator.connect(ac.destination);
oscillator.start();
 

https://codepen.io/JohnsonLeee/pen/MWpwvPZ[5]

注意到type還有一個值custom,我們可以自定義波形(決定聲音的音色),Web Audio提供了一個接口PeriodicWave來創建一個周期波形。需要注意的是不能直接設置type為custom,而是要通過setPeriodicWave方法來設置自定義波形。

const real = [0, 0, 1, 0, 1]; // cosine terms
const imag = [0, 0, 0, 0, 0]; // sine terms
const periodicWave = ac.createPeriodicWave(real, imag);
oscillator.setPeriodicWave(periodicWave);
 

2.4 音頻流

音頻流也可以用來創建一個音頻源節點,常見的音頻流由麥克風、WebRTC產生,通過audioContext.createMediaStreamSource()方法從音頻流創建一個音頻源節點。

將將其連接到前面的示波器組件,便可以做一個簡單的錄音可視化效果,查看示例[6]

3. 音頻處理

音頻分析處理相關的API,都可以歸類音頻處理節點,相關的API有很多,這里分別介紹幾個常用API。

3.1 Analyse

AnalyserNode可以對音頻信號進行實時的快速傅立葉變換(FFT)得到音頻的時域/頻域數據,拿到時域/頻域的數據后,可以實現時域分析、頻域分析、音頻可視化動效等。

通過audioContext.createAnalyser()方法來創建一個AnalyserNode,創建之后可以設置一個fftSize屬性,該屬性指定要進行FFT的采樣數據的窗口大小,它的值必須是2的非零整數倍,默認為2048。

const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const dataArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteTimeDomainData(dataArray);
// analyser.getByteFrequencyData(dataArray);
 

analyser.frequencyBinCount是時域/頻域數據的長度,它是fftSize的一半(因為實時音頻信號的傅立葉變換具有對稱性,只需要獲取一半數據即可),通過analyser.getByteTimeDomainData(dataArray)方法將數據填充到8位無符號整型數組中。

之后,我們就可以使用dataArray進行對應的操作。可以使用上面的示波器組件來繪制一首音樂的波形:

https://codepen.io/JohnsonLeee/pen/poeaWQr[7]

將上面的繪制頻譜圖代碼稍作修改,就可以做一個簡單的音樂播放動效。

https://codepen.io/JohnsonLeee/pen/mdWWgyP[8]

3.2 BiquadFilter

BiquadFilterNode是個雙二階數字濾波器[9]。濾波器的作用是對特定頻率的輸入信號進行增強/減弱,通常用來控制音調、做均衡器。可以通過audioContext.createBiquadFilter()方法創建一個雙二階濾波器,它有4個關鍵的參數:typefrequencyQgain

const filter = ac.createBiquadFilter();
filter.type = 'peaking'; // 設置類型為峰值濾波器
filter.Q.value = 1; // 峰值濾波器的帶寬
filter.frequency.value = 1000; // 中心頻率
filter.gain.value = 5; // 增益,單位db
// ...
source.connect(filter);
filter.connect(ac);
 

不同類型的濾波器在用法和效果上都有一定的差異,這里以低架濾波器、峰值濾波器、高架濾波器為例寫了一個均衡器Demo[10]

下表列出了所有的濾波器類型及對應的參數說明:

類型 描述 frequency Q gain
lowpass 低通濾波器,只允許低於設定的值的頻率通過 截止頻率 值越小減弱越強 沒用
highpass 高通濾波器,只允許高於設定的值的頻率通過 截止頻率 值越小減弱越強 沒用
bandpass 帶通濾波器,允許指定范圍內的頻率通過 中心頻率 控制帶寬,值越小帶寬越大 沒用
lowshelf 低架濾波器,對低於設定值的頻率進行增強/減弱,高於的部分不變 上限頻率 沒用 正值增強,負值減弱,單位db
highshelf 高架濾波器,對高於設定值的頻率進行增強/減弱,低於的部分不變 下限頻率 沒用 正值增強,負值減弱,單位db
peaking 峰值濾波器,在設定范圍內的頻率會被增強/減弱,其它部分不變 中心頻率 控制帶寬,值越小帶寬越大 正值增強,負值減弱,單位db
notch 陷波濾波器,也稱作帶阻濾波器,它的作用和帶通濾波器相反 中心頻率 控制帶寬,值越小帶寬越大 沒用
allpass 全通濾波器,不會對輸入信號進行增強/減弱,但是可以改變輸入信號的相位,可以用來做延遲 群延遲[11]最大頻率,即發生相變的中心頻率。 控制中頻過渡的銳度,值越大過渡銳度越大 沒用

3.3 Panner

PannerNode提供了3D空間音頻能力(前提是播放設備必須擁有2個或以上的聲道)。聲音源在人的不同方向,聽到的聲音感受都是不一樣的,甚至在聲音源移動的時候由於多普勒效應[12]會產生一種奇怪的聲音。

而通常使用電子設備播放音樂的時候僅僅是簡單的播放,PannerNode便為我們提供了模擬現實場景的能力,能夠開發出具有真實聽覺體驗的VR、游戲等場景。

PannerNode工作在右手笛卡爾坐標系中,由位置坐標、朝向向量坐標確定其位置,聲音呈錐形向空間擴散。可以通過設置coneInnerAngle屬性控制聲源擴散錐形的角度。

 

 

 

 

 

使用audioContext.createPanner()可以創建一個panner節點,它有幾個關鍵的屬性:

  • positionX/positionY/positionZ:聲源位置坐標
  • orientationX/orientationY/orientationZ:聲源朝向向量
  • coneInnerAngle:錐形角度
  • rolloffFactor:聲音隨距離的衰減速度
  • distanceModel:聲音衰減算法模型

音頻空間化還需要使用到一個AudioListener,它表示空間中收聽音頻的人。在一個音頻上下文中,僅有一個AudioListener,並且它不是AudioNode的子類,通過audioContext.listener獲取到。

 

 

AudioListener也擁有幾個類似的屬性:

  • forwardX/forwardY/forwardZ:收聽者面部朝向向量
  • upX/upY/upZ:收聽者頭部向量
  • positionX/positionY/positionZ:收聽者在空間中的坐標

了解了這些API,就可以開始寫一個音頻空間化效果了。

const panner = ac.createPanner();
panner.panningModel = 'HRTF'; // 音頻空間化算法模型
panner.distanceModel = 'inverse'; // 遠離時的音量衰減算法
panner.maxDistance = 500; // 最大距離
panner.refDistance = 50; // 開始衰減的參考距離
panner.rolloffFactor = 1; // 衰減速度
panner.coneInnerAngle = 360; // 聲音360度擴散

panner.orientationX.value = 0; // 聲源朝向x分量
panner.orientationY.value = 0;
panner.orientationZ.value = 1;

const listener = ac.listener;
listener.forwardX.value = 0; // 收聽者面部朝向向量
listener.forwardY.value = 0;
listener.forwardZ.value = -1; // 設置與panner朝向相反,即與panner面對面
listener.upX.value = 0; // 收聽着頭部朝向向量
listener.upY.value = 1;
listener.upZ.value = 0;

source.connect(panner);
panner.connect(ac.destination);
 

播放過程中動態的設置panner和listener的position,即可實現空間化的效果,查看Demo[13]

3.4 Convolver

ConvolverNode可以對音頻信號進行線性卷積運算,將特定的脈沖信號與音頻進行卷積運算后,可以獲得一些畸變效果,可以用來做一些環境混響效果。

ConvolverNode使用比較簡單,通過audioContext.createConvolver()創建一個卷積器節點,它有兩個屬性:

  • buffer:AudioBuffer類型,一個沖激信號(必須是.wav格式的文件)
  • normalize:決定是否對buffer的沖激響應進行等功率的歸一化縮放

normalize可能不是很好理解,有興趣的同學可以深入學習,這里簡單寫個示例代碼

const impulse = ac.decodeAudioData(arrayBuffer); // 解碼加載好的沖激信號
const convolver = ac.createConvolver()
convolver.buffer = impulse;
source.connect(convolver);
convolver.connect(ac.destination);
 

這個API在使用上很簡單,也不要求必須了解相關的理論基礎,只需要找到對應的沖激信號文件。當然,在進行卷積運算后輸出的音頻功率會有所變化,一般情況需要配合GainNode來調節增益。

查看Demo[14]

4. 兼容性

Web Audio目前兼容性還不夠完善,可以查看MDN兼容性表格[15],在復雜的應用場景中可能會遇到一些難以解決的問題。

5. 結束語

Web Audio覆蓋的內容很多,本文簡單介紹了一些特性。

因為還在學習中,本文會繼續更新完善。有興趣的同學可以一起交流。

 

參考資料

[1]

https://web-audio.johnsonlee.site/: https://web-audio.johnsonlee.site/

[2]

音頻路由圖: https://otrlvkfgf2.feishu.cn/docs/doccndPOo08N5Dmc53KJxjhcEsh#fcDQp2

[3]

AudioBuffer: https://developer.mozilla.org/zh-CN/docs/Web/API/AudioBuffer

[4]

https://codepen.io/JohnsonLeee/pen/bGqLXmZ?editors=0010: https://codepen.io/JohnsonLeee/pen/bGqLXmZ?editors=0010

[5]

https://codepen.io/JohnsonLeee/pen/MWpwvPZ: https://codepen.io/JohnsonLeee/pen/MWpwvPZ

[6]

查看示例: https://codepen.io/JohnsonLeee/pen/NWpXWbM

[7]

https://codepen.io/JohnsonLeee/pen/poeaWQr: https://codepen.io/JohnsonLeee/pen/poeaWQr

[8]

https://codepen.io/JohnsonLeee/pen/mdWWgyP: https://codepen.io/JohnsonLeee/pen/mdWWgyP

[9]

雙二階數字濾波器: https://zh.wikipedia.org/wiki/雙二階濾波器

[10]

均衡器Demo: https://web-audio.johnsonlee.site/biquad-filter

[11]

群延遲: https://en.wikipedia.org/wiki/Group_delay_and_phase_delay

[12]

多普勒效應: https://en.wikipedia.org/wiki/Doppler_effect

[13]

查看Demo: https://web-audio.johnsonlee.site/panner

[14]

查看Demo: https://web-audio.johnsonlee.site/convolver

[15]

MDN兼容性表格: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext#browser_compatibility

 轉自https://mp.weixin.qq.com/s/8DCFok78lzqgCrp_w5rEmw


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM