如何在 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 ...
随机推荐
- 分享一个国内可用的ChatGPT网站,免费无限制,支持AI绘画
背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具,近期的热度直接沸腾. 作为一个AI爱好者,翻遍了各大基于ChatGPT的网站,终于找到一个免费!免登陆!手机电脑通用!国内可直接对话的C ...
- 要想业务中台建得快,最好用Service Mesh来带
中国企业数字化转型进入深水区,业务中台及下一代微服务Service Mesh(服务网格)被越来越多的人关注,本文结合网易轻舟微服务Service Mesh实践,解析业务中台为什么需要Service M ...
- [oeasy]python0143_主控程序_main
主控程序 回忆上次内容 上次把 apple.py 拆分成了 输入 主函数 引用模块中变量的时候 要带上包(module)名 get_fruits.a get_fruits.b 最终 拆分代 ...
- 二分专题总结 -ZHAOSANG
上一周训练了二分专题 可能是我之前自学的时候基础没有打牢,做的时候还是吃力的. 现总结遇到的一些二分题型和思路 二分+模拟(题目最多的) [https://ac.nowcoder.com/acm/co ...
- Activity的创建
Activity的创建: 1.layout内写入相关代码 此处为显示的页面 2.Java内创建相关类写入代码 3.在清单内写入 快捷方法:直接完成上面步骤 layout: match_parent// ...
- 解决Sqoop导入导出MySQL数据错位问题
添加--columns "columns,columns,columns" \可以在hive导入mysql时防止数据错位:
- MySQL常用语句(经常容易忘记)
MySQL常用语句 一.连接MySQL 格式: mysql -h <主机地址> -u<用户名> -p<用户密码> --port=<端口号> 1.例1:连 ...
- 【Vue2】Filter 过滤器
过滤器案例: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- 【IDEA】创建Maven工程
当前工程,点new - project 选Maven,不需要点选什么骨架创建,骨架创建要下载大量依赖,生成时间太长, 空Maven的目的是让我们自己了解这个项目结构,需要什么依赖再加什么依赖 框线内的 ...
- 【SpringBoot】09 日志集成
原来日志还分抽象层和实现层... 抽象层被称为是日志门面,实现层被称为是日志实现 门面的有: - JCL[Jakarta Commons Logging] 远古门面 - SLF4J[Simple ...