如何在 Web 前端做 3D 音效处理
一、背景
在社交元宇宙、大逃杀等类型的游戏场景下,用户在通过简单语音交流外,结合场景也需要一些立体声效果来让用户感知游戏角色周围其他用户的存在及其对应的距离和方位,提高语音互动的趣味性。
为了满足上述需求 ZEGO Express Web SDK 从 v2.10.0(Native 为 v2.11.0)开始加入范围语音功能模块,为游戏提供语音服务。
当前范围语音功能模块主要包括如下功能:
- 范围语音:房间内的收听者对音频的接收距离有范围限制,若发声者与自己的距离超过该范围,则无法听到声音。
- 3D 音效:听者接收的声音根据发声者相对于听者的距离和方位模拟现实中声音的立体声效果。
- 小队语音:玩家可以选择加入小队,并支持在房间内自由切换“全世界”模式和“仅小队”模式。
其中对于 Web 3D 音效这部分功能的实现,我们是基于浏览器提供的 Web Audio API 对音频进行处理。这里小编也通过使用 Web Audio API 做了一个简单的环绕音的 demo 页面。
demo 在线体验地址:https://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);
// 创建用于控制音频振幅的处理结点 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;

(听者朝向向量的图示说明)
对于听者的朝向可通过 AudioListener 的forwardX、forwardY 、forwardZ 这三个属性设置听者的正面朝向向量,默认值是 (0,0,-1) 。通过 AudioListener 的 upX、upY、upZ 这三个属性设置听者的头顶朝向方向向量,默认值是 (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="https://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 更多能力,链接:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Audio_API#%E5%AE%9A%E4%B9%89%E9%9F%B3%E6%95%88 。
如果想要了解更多关于 ZEGO Express SDK 范围语音功能模块,可以查看 ZEGO 官网介绍文档,链接: https://doc-zh.zego.im/article/12045 。
也可以打开我们的范围语音 Demo 进行体验:https://zegoim.github.io/express-demo-web/src/Examples/Others/RangeAudio/index.html
如何在 Web 前端做 3D 音效处理的更多相关文章
- 如何在Web前端实现CAD图文字全文搜索功能之技术分享
现状 在CAD看图过程中我们经常会需要用到查找文字的功能,在AutoCAD软件查找一个文字时,可以通过打开左下角输入命令find,输入查找的文字,然后设置查找范围,就可以搜索到需要查询的文字.但在We ...
- “设计型web前端与开发型web前端”有哪些区别?
学web前端,你弄懂开发型web前端和设计型web前端的区别了吗?今天给大家梳理一下设计型web前端做什么?都要学习什么? 想必大家也会遇到这种情况,要做一个项目,产品经理说产品原型图已经画好了,让我 ...
- 学习WEB前端是应该自学还是参加培训机构?
先说观点,我强烈建议每个人都要自学,不要参加培训班. 我干web前端工程师这个职位已经有6年多的时间,之前在蚂蚁金服做过2年,后来离开是因为加班实在熬不住才走的,像这些已经上市的互联网公司几乎没有不加 ...
- 学web前端开发有前途吗
web前端开发现在如此火爆,可以说是引领了IT培训行业的一个潮流,那么web前端开发都要学些什么知识呢?为什么这么火有前途吗?现在行业很需要这种人才吗?还是大家盲目跟风,随大流,下面小编对web前端做 ...
- Web前端 Web前端和Web后端的区分
一.绪论 1. 前台:呈现给用户的视觉和基本的操作. 后台:用户浏览网页时,我们看不见的后台数据跑动.后台包括前端.后端. 前端:对应我们写的html.css.javascript 等网页语言作用在前 ...
- Web前端和Web后端的区分
版权声明:本文为CSDN博主「十豆三展」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/zz1399590022 ...
- [Web 前端] 如何在React中做Ajax 请求?
cp from : https://segmentfault.com/a/1190000007564792 如何在React中做Ajax 请求? 首先:React本身没有独有的获取数据的方式.实际上, ...
- Web前端可以转行做游戏吗?
作者:ManfredHu 链接:http://www.manfredhu.com/2018/03/15/31-laya-game-tips/index.html 声明:版权所有,转载请保留本段信息,谢 ...
- 什么是Web前端,Web前端是做什么的?
什么是Web前端 Web前端,顾名思义是来做Web的前端的.而Web前端开发应该就是来开发基于Web前端的相关应用的或者说是来开发前端的.那么,前端又是什么呢?我们这里所说的前端泛指Web前端,也就是 ...
- 绝对精品推荐做前端的看下:Web前端开发体会十日谈
20151208感悟: 前端人的角度来看的话,感觉像是阅读一个大牛前端的全部武功的一个秘籍说明,里面的思想高价值蛋白真是太多太多,推荐看. Web前端开发体会十日谈 一直想写这篇“十日谈”,聊聊我对W ...
随机推荐
- MakeSense标注指南
1.网址 https://www.makesense.ai/ 2.操作流程 2.1 导入 点击get started 点击drop images,上传图片 选择obeject detection 新建 ...
- 解决方案 | 外接键盘win+d失效,绿联键盘win+d,win+e失效
按下fn + 右边的win键 即可解决.如下图所示.
- node sass
registry=https://registry.npmmirror.com/ sass_binary_site=https://cdn.npmmirror.com/mirrors/node-sas ...
- UE4 蓝图查找Actor和Actor标签
在用UE4 开发数字孪生应用的过程中,有很多业务会涉及到actor的查找,和actor标签的使用. 比如下面的场景中,找出所有的物联网设备进行标注. 通过类查找actor 通过类查找actor,可以查 ...
- 暑假Java自学每日进度总结1
今日所学: 一.常用的cmd命令: 1>盘符: 2>dir(显示当前文件所有目录) 3>cd 目录(打开该目录) 4>cd..(回到上一目录) 5>cd(回到当前盘符初始 ...
- webpack4.15.1 学习笔记(七) — 懒加载(Lazy Loading)
懒加载或者按需加载,是一种很好的优化网页或应用的方式.实际上是先把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或引用另外一些新的代码块.这样加快了应用的初始加载速度,减轻了它 ...
- 对比python学julia(第三章:游戏编程)--(第三节)疯狂摩托(3)
3.3. 编程实现 2. 控制摩托车和箱子 在这个步骤中,将编程控制摩托车和箱子角色的运动,让摩托车在沙漠公路上能够加速或减速行驶,在碰到箱子时能够停止,以及显示麾托车的行驶速度和里程等. ( ...
- 【ELK】Kibana-7.13.1版本 启动报错 Centos6
报错信息: [root@centos6-1 gcc-4.8.2]# /opt/kibana-7.13.1-linux-x86_64/bin/kibana /opt/kibana-7.13.1-linu ...
- 【Mybatis-Plus】03 SpringBoot整合
创建SpringBoot工程: 选择辅助三件套: 再导入MP相关依赖坐标: <!-- jdbc --> <dependency> <groupId>mysql< ...
- MindSpore 建立神经网络
代码原地址: https://www.mindspore.cn/tutorial/zh-CN/r1.2/model.html 建立神经网络: import mindspore.nn as nn cla ...