如何在 Web 前端做 3D 音效處理

語言: CN / TW / HK

一、背景

在社交元宇宙、大逃殺等型別的遊戲場景下,使用者在通過簡單語音交流外,結合場景也需要一些立體聲效果來讓使用者感知遊戲角色周圍其他使用者的存在及其對應的距離和方位,提高語音互動的趣味性。

為了滿足上述需求 ZEGO Express Web SDK 從  v2.10.0(Native 為 v2.11.0)開始加入範圍語音功能模組,為遊戲提供語音服務。

當前範圍語音功能模組主要包括如下功能:

  • 範圍語音:房間內的收聽者對音訊的接收距離有範圍限制,若發聲者與自己的距離超過該範圍,則無法聽到聲音。

  • 3D 音效:聽者接收的聲音根據發聲者相對於聽者的距離和方位模擬現實中聲音的立體聲效果。

  • 小隊語音:玩家可以選擇加入小隊,並支援在房間內自由切換“全世界”模式和“僅小隊”模式。

其中對於 Web 3D 音效這部分功能的實現,我們是基於瀏覽器提供的 Web Audio API 對音訊進行處理。這裡小編也通過使用 ****Web Audio API ****做了一個簡單的環繞音的 demo 頁面。

demo 線上體驗地址: http://keen_wang.gitee.io/demo/music3d ,頁面如下圖,點選“開始播放”按鈕開始播放音樂,再點選“開閉空間化”進行開啟或關閉 3D 音效,開啟 3D 音效後就可以聽到空間環繞聲效果。(在體驗 3D 音效時需要使用左右聲道分開的耳機或音響裝置)

下文將介紹如何使用 ****Web Audio API ****來做這個環繞音 demo。

二、Web Audio API 簡介

Web Audio API 用於操作聲音,很多時候用於替代 <audio> 標籤來播放一段音訊,除此之外,還有音訊處理的功能,比如音量調節、音訊混合、音訊空間化等。

Web Audio API 使使用者可以在音訊上下文( AudioContext )中進行音訊操作,具有模組化路由的特點。

下面是最簡單的一個路由圖,表示音訊源通過效果處理後輸出到音訊目的地,圖中的 inputs、Effects、Destination 三個模組分別對應為音訊節點( AudioNode )的輸入源節點、處理節點、輸出節點。

下面我們將介紹 Web Audio API 的簡單使用步驟:

1、建立 AudioContext 音訊上下文例項

// 建立音訊上下文const AudioContext = window.AudioContext || window.webkitAudioContext;const audioCtx = new AudioContext();

複製程式碼

AudioContext 為音訊處理提供一個上下文環境,相當於一箇中央控制器,用於控制著音訊路由圖中的各個音訊節點。

2、在音訊上下文裡建立輸入源節點和處理節點。

// 建立輸入結點,解碼 audio 標籤的音訊源const audioEl = document.querySelector('audio');const sourceNode = audioCtx.createMediaElementSource(audioEl);// 建立用於控制音訊振幅的處理結點 GainNodeconst gainNode = audioCtx.createGain(); 

複製程式碼

3、將輸入源節點連到處理節點。

輸入源節點通過 connect 方法將音訊資料傳輸給處理節點。

sourceNode.connect(gainNode);

複製程式碼

4、將處理節點連線到選定的輸出節點進行效果輸出。

處理節點通過 connect 方法將處理完的音訊資料傳輸給輸出節點進行效果輸出。

這裡的輸出節點 audioCtx.destination 為當前使用的揚聲器。

gainNode.connect(audioCtx.destination);

複製程式碼

5、修改處理節點的屬性以修改輸出效果。

// 設定靜音處理gainNode.gain.setValueAtTime(0, audioCtx.currentTime);

複製程式碼

瞭解完 Web Audio API 的使用特點,接下來介紹如何進行音訊空間化處理。

三、 實現 3D 音效

音訊空間化的實現主要是通過 PannerNode 和  AudioListener 結合使用來處理聲音效果。這兩個類的例項物件進行設定空間方位資訊後動態處理音訊源並輸出到左右聲道。

  • AudioListener 物件代表三維空間中的聽者(使用者),通過 AudioContext.listener 屬性獲取對應的例項物件;

  • PannerNode 物件指的是三維空間中的聲音源,通過 new 的方式或者 AudioContext.createPanner() 建立得到。

下面將介紹如何設定 AudioListener ****和 PannerNode 的屬性來改變 3D 音效效果。

1、設定 AudioListener

AudioListener 物件表示聽者,這裡可以定義聽者在空間中的位置和他(她)們面向的方向, PannerNode 可以計算出聲音相對於收聽者位置的位置。

對於聽者位置資訊,AudioListener 提供了三個位置屬性: positionXpositionYpositionZ ,它分別代表聽者當前位置的 xyz 座標,這裡座標系使用的是右手笛卡爾座標系,x 軸和 z 軸在水平方向、y 軸在垂直方向。

// 為 listener 設定 positionconst listener = audioCtx.listener;listener.positionX = camera.position.x;listener.positionY = camera.position.y;listener.positionZ = camera.position.z; 

複製程式碼

(聽者朝向向量的圖示說明)

對於聽者的朝向可通過 AudioListener 的 forwardXforwardYforwardZ 這三個屬性設定聽者的正面朝向向量,預設值是 (0,0,-1) 。通過 AudioListener 的  upXupYupZ 這三個屬性設定聽者的頭頂朝向方向向量,預設值是 (0,1,0) ,即垂直朝上的方向。通過這兩個朝向向量的設定,即可確定聽者左右耳的位置來生成立體聲效果。

2、設定 PannerNode

PannerNode 是一個處理節點,提供了 3D 空間音訊能力,PannerNode 通過相對於 AudioContext 的 AudioListener 的位置和朝向資訊對聲音進行空間化處理。

PannerNode 有以下幾個常用屬性:

  • panningModel:音訊空間化演算法模型,預設值是 “equalpower”,即等冪平移演算法,建議設定更為只能的 “HRTF” 。

  • positionX/positionY/positionZ:聲源位置座標。

  • orientationX/orientationY/orientationZ:聲源朝向向量。

  • coneInnerAngle:錐形角度,單位為度,預設是 360。

  • rolloffFactor:聲音隨距離的衰減速度,預設值為 1。

  • distanceModel:聲音衰減演算法模型,預設值是 “inverse”,即相反距離模型。

3、環繞音 Demo

瞭解了這些 Web Audio API,就可以開始實現一個音訊空間化效果了。下面是一個播放環繞聲歌曲的 demo 程式碼,模擬空間中一個音源在聽者周圍環繞。通過在播放過程中動態修改 PannerNode 的定位資訊來生成環繞效果。

<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Web Audio</title></head>
<body> <audio loop autoplay crossorigin="anonymous" src="http://s3-us-west-2.amazonaws.com/s.cdpn.io/858/outfoxing.mp3"></audio> <button onclick="startPlay()">開始播放</button> <button onclick="spatialize()">開閉空間化</button> <span>音效狀態:</span><span id="status">關閉</span> <script> // 音源初始位置資訊 const audioPosition = [0, 0, 1] // 建立音訊上下文 const AudioContext = window.AudioContext || window.webkitAudioContext; const audioCtx = new AudioContext(); // 設定 AudioListener const listener = audioCtx.listener; listener.positionX.value = 0; listener.positionY.value = 0; listener.positionZ.value = 0; listener.forwardX.value = 0; listener.forwardY.value = 0; listener.forwardZ.value = -1;
// 建立輸入結點,解碼 audio 標籤的音訊源;建立處理結點,處理音訊 const audioEl = document.querySelector('audio'); const sourceNode = audioCtx.createMediaElementSource(audioEl); // 建立和設定 PannerNode const pannerNode = new PannerNode(audioCtx, { panningModel: "HRTF", // 音訊空間化演算法模型 distanceModel: "linear", // 遠離時的音量衰減演算法 rolloffFactor: 1, // 衰減速度 coneInnerAngle: 360, // 聲音 360 度擴散 positionX: audioPosition[0], positionY: audioPosition[1], positionZ: audioPosition[2], maxDistance: 10000, }); // 將輸入節點直接連線到輸出節點 sourceNode.connect(audioCtx.destination);
// 設定音源自動分別沿 xyz 三個軸來回移動效果,形成環繞效果 function autoMove(axis, interval, step = 100, maxDistance = 1000) { let isAdd = true const positionAxisMap = ["positionX", "positionY", "positionZ"] setInterval(() => { if (isAdd && audioPosition[axis] >= maxDistance) { isAdd = false; } else if (!isAdd && audioPosition[axis] <= -maxDistance) { isAdd = true; } if (isAdd) { audioPosition[axis] += step; } else { audioPosition[axis] -= step; } pannerNode[positionAxisMap[axis]].value = audioPosition[axis] console.log('audioPosition', audioPosition); }, interval)
} // 沿 x 軸在 -1000 到 1000 之間來回移動 autoMove(0, 100, 100, 1000) // 沿 z 軸在 -1000 到 1000 之間來回移動 autoMove(2, 200, 100, 1000) // 沿 y 軸在 -100 到 100 之間來回移動 autoMove(1, 400, 10, 100)
// 開始播放音樂 function startPlay() { audioCtx.resume(); // 設定靜音播放。 audioEl.play(); }
// 開關 3D 音效 let isSpatialized = false function spatialize() { isSpatialized = !isSpatialized document.querySelector("#status").innerText = isSpatialized ? "開啟" : "關閉" if (isSpatialized) { sourceNode.disconnect(); sourceNode.connect(pannerNode); // 將處理節點連線到 destination 輸出節點進行效果輸出。 pannerNode.connect(audioCtx.destination); } else { sourceNode.disconnect(); sourceNode.connect(audioCtx.destination); } } </script></body>
</html>

複製程式碼

四、結語

本文主要講解了對於 Web Audio API 的基本使用及使用 AudioListener 和 PannerNode 實現環繞聲效果。

Web Audio API 除了進行 3D 音效外還有很多強大的音訊處理能力,可以檢視 MDN 上的文件瞭解 Web Audio API 更多能力,連結: http://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API#定義音效

如果想要了解更多關於 ZEGO Express SDK 範圍語音功能模組,可以檢視 ZEGO 官網介紹文件,連結: http://doc-zh.zego.im/article/12045

也可以開啟我們的範圍語音 Demo 進行體驗,點選連結: http://zegoim.github.io/express-demo-web/src/Examples/Others/RangeAudio/index.html 進行體驗!