一、背景

在社交元宇宙、大逃杀等类型的游戏场景下,用户在通过简单语音交流外,结合场景也需要一些立体声效果来让用户感知游戏角色周围其他用户的存在及其对应的距离和方位,提高语音互动的趣味性。

为了满足上述需求 ZEGO Express Web SDK 从  v2.10.0(Native 为 v2.11.0)开始加入范围语音功能模块,为游戏提供语音服务。

当前范围语音功能模块主要包括如下功能:

  • 范围语音:房间内的收听者对音频的接收距离有范围限制,若发声者与自己的距离超过该范围,则无法听到声音。

  • 3D 音效:听者接收的声音根据发声者相对于听者的距离和方位模拟现实中声音的立体声效果。

  • 小队语音:玩家可以选择加入小队,并支持在房间内自由切换“全世界”模式和“仅小队”模式。

其中对于 Web 3D 音效这部分功能的实现,我们是基于浏览器提供的 Web Audio API 对音频进行处理。这里小编也通过使用 ****Web Audio API ****做了一个简单的环绕音的 demo 页面。

下面我们将介绍 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);
// 创建用于控制音频振幅的处理结点 GainNode
const 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 提供了三个位置属性:positionX 、positionY 、positionZ ,它分别代表听者当前位置的 xyz 坐标,这里坐标系使用的是右手笛卡尔坐标系,x 轴和 z 轴在水平方向、y 轴在垂直方向。

// 为 listener 设置 position
const listener = audioCtx.listener;
listener.positionX = camera.position.x;
listener.positionY = camera.position.y;
listener.positionZ = camera.position.z;