如何在 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 進行體驗!