首页 > 其他分享 >如何在 Web 前端做 3D 音效处理

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

时间:2023-11-01 18:02:07浏览次数:35  
标签:API Web const 音频 listener 音效 节点 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 前端做 3D 音效处理_3D

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

编辑下面我们将介绍 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 的forwardXforwardY 、forwardZ 这三个属性设置听者的正面朝向向量,默认值是 (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="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


标签:API,Web,const,音频,listener,音效,节点,3D
From: https://blog.51cto.com/u_14794264/8130629

相关文章

  • javaweb--Mapper代理开发
     1、使Mapper接口和SQL映射文件放置在同一文件夹下,可以在resources文件夹下新建一个和SQL映射文件相同的文件夹(例如:com/avb/Mapper)不能用.分割文件夹,用/ 2、把SQL映射表里的namespace属性为Mapper接口的全限定名 3、在Mapper中定义方法,方法名是SQL映射文件中sql语句的id,并......
  • Web Woeker和Shared Worker的使用以及案例
    目录1、前言2、介绍WebWorker3、使用须知及兼容性3.1、使用须知3.2、兼容性4、使用WebWorker4.1、创建WebWorker4.2、与主线程通信4.3、终止WebWorker4.4、监听错误信息5、使用SharedWorker4.5、调试SharedWorker6、使用中的一些坑6.1、WebWoeker中引入了其余文件6.......
  • 创建一个Web服务器并保持其运行,可以使用Python的Flask库。以下是一个基本的示例: ```p
    创建一个Web服务器并保持其运行,可以使用Python的Flask库。以下是一个基本的示例:```pythonfromflaskimportFlask,requestimportosapp=Flask(__name__)@app.route('/webhook',methods=['POST'])defwebhook():  data=request.get_json()  #在这里添加你的......
  • 使用​​Flask​​库来创建一个Webhook服务器,该服务器可以接收HTTP请求
    在Python中,你可以使用Flask库来创建一个Webhook服务器,该服务器可以接收HTTP请求,处理请求体,并发送响应。以下是一个简单的示例:fromflaskimportFlask,request,jsonifyapp=Flask(__name__)@app.route('/webhook',methods=['POST'])defwebhook():#获取请求体中的数......
  • 图扑 HT for Web 手机端运维管理系统
    随着信息技术的快速发展,网络技术的应用涉及到人们生活的方方面面。其中,手机运维管理系统可提供数字化、智能化的方式,帮助企业和组织管理监控企业的IT环境,提高运维效率、降低维护成本、增强安全性、提升服务质量,并支持企业实现数字化转型,满足客户需求和市场竞争力至关重要。本文......
  • 图扑 HT for Web 手机端运维管理系统
    随着信息技术的快速发展,网络技术的应用涉及到人们生活的方方面面。其中,手机运维管理系统可提供数字化、智能化的方式,帮助企业和组织管理监控企业的IT环境,提高运维效率、降低维护成本、增强安全性、提升服务质量,并支持企业实现数字化转型,满足客户需求和市场竞争力至关重要。本文将......
  • 「2023·最新盘点」十大热门WebStorm主题
    WebStorm 是jetbrains公司旗下一款JavaScript开发工具。被广大中国JS开发者誉为"Web前端开发神器""最强大的HTML5编辑器""最智能的JavaSscriptIDE"等。与IntelliJIDEA同源,继承了IntelliJIDEA强大的JS部分的功能。在IDE中,开发者的感觉舒服很重要。WebStorm定制程度高,您可以按照......
  • javaweb--MyBatis
    持久层框架,用于简化JDBC开发负责将数据保存到数据库的那一层代码JavaEE三层架构:表现层、业务层、持久层免除了几乎所有JDBC代码及设置参数和获取结果集的工作。1、导入查询user表中所有的数据createDATABASEmybatis;usemybatis;droptableifexiststb_user;createtable......
  • [17章+电子书]C#速成指南-从入门到进阶,实战WPF与Unity3D开发
    点击下载:[17章+电子书]C#速成指南-从入门到进阶,实战WPF与Unity3D开发  提取码:a3s5 《C#速成指南--从入门到进阶,实战WPF与Unity3D开发》完整讲解了C#语言的核心知识和高阶编程技巧,并结合WPF客户管理系统和Unity3D切水果游戏两大实战项目,帮你实现技术的精通,完成从Zero到Hero的蜕变......
  • tinygo webassembly 试用
    主要是简单测试下tinygo的使用,同时基于vite进行web的集成构建wasm生成注意只测试标注类型支持比较多,其他的就没添加,其他类型的需要自己处理,这点上wasm-pack处理的比较好main.gopackagemain //go:wasm-module//exportaddfuncadd(x,yuint32)uint......