VR Audio
VR音频的输出硬件主要是耳机,根据音频源与场景之间的关系,可将VR音频分为两类:静态音频和空间化音频(audio spatialization)。
静态音频
这类音频作用于整个VR场景,可简单的理解成背景音乐,音频输出是静态的,比如微风雨滴声、闹市声等充斥整个场景的背景音效。
对于环境音效的开发,我们可以简单的使用<audio>标签进行循环播放。
空间化音频
音频作用在空间的实体上,具有发声体和听者的位置关系,音频输出会根据发声体与用户的距离、方向动态变化,它模拟了现实中声音的传播方式,具有空间感。
实现原理:在虚拟场景中,通过调节音频的振幅来描述发声体与听者之间的距离,再通过调节左右通道(audio channel)之间的差异,控制左右耳机喇叭输出,来描述发声体相对听者的方位。
- 从发声体与用户两点间的距离来看,如距离越远,音频音量(振幅)应越小;
- 从发声体与用户的方向来看,如发声体位于听者左侧,则音频输出的左声道应比右声道音量大。
形如音频空间化此类稍复杂的音频的处理,可通过Web Audio API来实现。
Web Audio API 简介
Web Audio API提供了一个功能强大的音频处理系统,允许我们在浏览器中通过js来实时控制处理音频,比如音频可视化、音频混合等。
Web Audio处理流程Web Audio处理流程可以比喻成一个加工厂对声源的加工,这个加工厂由多个加工模块AudioNode
连接而成,音频源经过一系列的处理加工后,被输送至扬声器。
AudioContext
类似于canvas
的context
上下文环境,它代表了一个audio加工厂控制中心,负责各个audioNode的创建和组合,通过new AudioContext()
的方式创建。
AudioNode
AudioNode音频节点,则是加工厂的加工模块, 按照功能可分为三类:输入结点、处理结点、输出结点。每个结点都拥有connect
方法连接下一个节点,将音频输出到下一个模块。
- 输入结点主要负责加载解码音频源,比如获取二进制音频源的
BufferSourceNode
、获取<audio>音频源的MediaElementSourceNode
等; - 处理结点主要对音频数据进行计算处理,比如处理音频振幅的
GainNode
等; - 输出结点则将音频输出至扬声器或耳机,
AudioContext.destination
便是默认的输出节点。
一个简单的音频处理流程只需要分为四步:
- 创建音频上下文
- 创建并初始化输入结点、处理结点
- 将输入结点、处理结点、输出结点进行有连接
- 动态修改结点属性以输出不同音效
参考以下代码:
const myAudio = document.querySelector('audio');
const audioCtx = new AudioContext(); // 创建音频上下文
// 创建输入结点,解码audio标签的音频源;创建处理结点,处理音频
const source = audioCtx.createMediaElementSource(myAudio);
const gainNode = audioCtx.createGain(); // 创建GainNode结点控制音频振幅
// 将输入结点、处理结点、输出结点两两相连
source.connect(gainNode); // 将输入结点连接到gainNode处理结点
gainNode.connect(audioCtx.destination); // 将gainNode连接到destination输出节点
// 通过动态改变结点属性产生不同音效
source.start(0); // 播放音频
gainNode.gain.value = val; // 设置音量
理解了Web Audio的开发流程,接下来看看如何在WebVR中实现Audio Spatialization,这里VR场景使用three.js进行开发。
实现空间化音频
Audio Spatialization的实现主要通过AudioListener
和PannerNode
结点配合,这两个对象可以根据空间方位信息动态处理音频源,并输出左右声道。
AudioListener
对象代表三维空间中的听者(用户),通过AudioContext.listener
属性获取;