使用Web Audio创建有趣且沉浸式的音频体验





5.00/5 (1投票)
今天我想与大家分享我在构建 Babylon.js 开源游戏引擎的音频引擎时学到的知识。
如今,借助 Web Audio API 的强大功能,您可以直接在浏览器中创建身临其境的音频体验,无需任何插件。今天我想与大家分享我在构建我们 Babylon.js 开源游戏引擎的音频引擎时学到的知识。
阅读完本文,您将知道如何构建此类体验
用法:用鼠标或触摸在白色圆圈中心附近移动方块,即可使用 WebGL 和 Web Audio 在此 3D 体验中混合我的音乐。查看源代码。
Web Audio 简介
Web Audio 是 Web 上最先进的音频栈。
来自 Learn Web Audio API
如果您曾经尝试过除了使用 HTML5 audio 元素流式传输声音或音乐之外的其他操作,您就会知道它的局限性有多大。Web Audio 使您能够打破所有障碍,并像任何现代原生应用程序一样访问完整的流和音频管道。它通过一个由音频节点组成的音频路由图来实现。这使您可以精确控制时间、过滤器、增益、分析器、卷积器和 3D 空间化。
它现在得到了广泛支持(Microsoft Edge、Chrome、Opera、Firefox、Safari、iOS、Android、FirefoxOS 和 Windows Mobile 10)。在 Edge 中(但我想其他浏览器也一样),它在与主 JavaScript 线程分开的线程中渲染。这意味着几乎所有时候,它对您的应用程序或游戏几乎没有性能影响。所有浏览器至少支持 WAV 和 MP3 编解码器,其中一些还支持 OGG。Edge 甚至支持多声道 Dolby Digital Plus™ 音频格式!!因此,您需要注意这一点,以便构建一个可在任何地方运行并为所有浏览器提供潜在多个来源的 Web 应用程序。
音频路由图说明
注意:此图片是使用 Firefox DevTools 的出色 Web Audio 选项卡中显示的图构建的。我喜欢这个工具。如此之多,以至于我计划通过一个 Vorlon.js 插件来模仿它。我已开始着手处理,但仍处于非常早期的草稿阶段。
让我们看看这个图。每个节点都可以有一个输入,并连接到另一个节点的输入。在这种情况下,我们可以看到 MP3 文件充当AudioBufferSource 节点的源,连接到Panner 节点(提供空间化效果),连接到Gain 节点(音量),连接到Analyser 节点(以实时访问声音的频率),最后连接到AudioDestination 节点(您的扬声器)。您还可以看到您可以控制特定声音或多个声音的音量(增益)。例如,在这种情况下,我通过最终增益节点(在目标节点之前)有一个“全局音量”,以及一些轨道音量,这些音量位于分析器节点之前。
最后,Web Audio 提供了 2 种心理声学模型来模拟空间化
- equalpower(默认设置),用于支持笔记本电脑或落地式扬声器的经典扬声器。
- HRTF,它通过模拟双耳录音,在耳机上提供最佳效果。
注意:MS Edge 目前仅支持 equalpower。
以上就是基础知识。要开始编写代码,我建议您阅读这些很棒的资源
- W3C 规范:http://webaudio.github.io/web-audio-api/。我再次说明,我通过简单地阅读规范学到了很多东西。
- HTML5 Rocks 上的《Web Audio API 入门》、《使用 Web Audio API 开发游戏音频》和《混合位置音频和 WebGL》。内容绝对精彩。它们有时包含指向演示(使用旧的 Web Audio 规范)的已弃用链接,但它们在大多数情况下仍然非常相关。还要注意演示中使用的编解码器,您的浏览器可能不支持。
- 《为 Microsoft Edge 带来 Web Audio 以实现互操作性游戏和发烧友媒体》,这是最新也是最准确的资源之一。
例如,让我们设想一下我们想创建一个这样的音频图
我想播放两个同步的音乐,每个音乐都有自己的增益连接到自己的分析器。这意味着我们可以将每个音乐的频率显示在单独的画布上,并分别播放每个音乐的音量。最后,我们将有一个全局音量同时作用于两首音乐,以及第三个分析器,它将在累积在第三个画布上的两首音乐的频率上显示频率。
您可以在此处找到演示:analysersSample.html
只需使用您喜欢的浏览器及其 F12 工具查看源代码。
sound.js 抽象了加载声音、解码声音和播放声音的逻辑。analyser.js 抽象了使用 Web Audio 分析器并将频率显示在 2D 画布上的工作。最后,analysersSample.js 使用这两个文件创建了上述结果。
玩滑块以调整每个音轨的音量或全局音量,以查看发生了什么。例如,尝试理解为什么将全局音量设置为 0 后,您仍然可以看到右侧显示的两个分析器的数据?
要能够同步播放两个音轨,您只需通过 XHR2 请求加载它们,解码声音,一旦这两个异步操作完成,只需对每个音轨调用 Web Audio 的 play 函数。
Babylon.js 中的 Web Audio
基础
我在处理音频栈时,希望保持我们“简单而强大”的理念。即使 Web Audio 并不那么复杂,对我来说它仍然非常冗长,您很快就会执行无聊且重复的任务来构建您的音频图。因此,我的目标是将其封装起来,提供一个非常简单的抽象层,让人们无需成为声音工程师即可创建 2D 或 3D 声音。
这是我们选择用来创建声音的签名
var newSound = new BABYLON.Sound("nameofyoursound", "URLToTheFile", yourBabylonScene,
callbackFunctionWhenSoundReadyToBePlayed,
optionsViaJSONObject);
例如,以下是在 Babylon.js 中创建新声音的方法
var sound1 = new BABYLON.Sound("sound1", "./sound1.mp3", scene,
null, { loop: true, autoplay: true });
var sound2 = new BABYLON.Sound("sound2", "./sound2.wav", scene,
function () { sound2.play(); },
{ playbackRate: 2.0 });
var streamingSound = new BABYLON.Sound("streamingSound", "./sound3.mp3", scene,
null, { autoplay: true, streaming: true });
第一首声音将在从 Web 服务器加载并由 Web Audio 解码后自动循环播放。
第二首声音将通过回调函数播放一次,该函数将在解码并准备好播放后触发其 play()
函数。它还将以 2 倍的播放速率播放。
最后一首声音使用了“streaming: true
”选项。这意味着我们将使用HTML5 Audio 元素,而不是进行 XHR2 请求并使用 Web Audio AudioBufferSource
对象。如果您想流式传输音乐而不是等待其完全下载,这可能很有用。尽管如此,您仍然可以将 Web Audio 的所有魔力应用于此 HTML5 Audio 元素:分析器、3D 空间化等等。
您可以尝试在我们的 Playground 中玩这个逻辑,例如通过这个非常简单的示例:http://www.babylonjs-playground.com/index.html?22
引擎、音轨和内存占用
Web Audio 音频上下文仅在最后一刻创建。当 Babylon.js 音频引擎在 Babylon.js 主引擎构造函数中创建时,它只会检查 Web Audio 支持并设置一个标志。此外,每个声音都位于一个声音音轨内。默认情况下,声音将添加到附加到场景的主声音音轨中。
Web Audio 上下文和主音轨(包含其附加到全局音量的增益节点)仅在第一次调用 new BABYLON.Sound()
后才真正创建。
我们这样做是为了避免为不使用音频的场景创建一些音频资源,而这对于我们托管在 http://www.babylonjs.com/ 上的大多数场景来说是今天的情况。尽管如此,音频引擎随时可以调用。这也有助于我们将音频引擎与核心游戏引擎分离,如果我们想构建一个没有音频代码的 Babylon.js 核心版本,使其更轻量级。最后,我们目前与非常关注资源使用的各大工作室合作。
为了更好地理解这一点,我们将使用 Firefox 及其 Web Audio 选项卡。
- 导航到:http://www.babylonjs-playground.com/ 并通过 F12 打开 Web Audio 选项卡。您应该看到
尚未创建音频上下文。
- 在左侧的 Playground 编辑器中,在“return scene”之前添加此行代码,例如
var music = new BABYLON.Sound("Violons", "sounds/violons11.wav",
scene, null, { loop: true, autoplay: true });
- 您现在应该在 F12 中看到此音频图
音频上下文已在首次调用 new BABYLON.Sound()
后动态创建,主音量、主音轨音量以及与此声音相关的音量也是如此。
3D 音效
基础
Web Audio 在很大程度上受到 OpenAL(Open Audio Library)等 3D 库所用模型的影响。大多数 3D 复杂性将由 Web Audio 为您处理。它将处理声音的 3D 位置、方向和速度(以应用多普勒效应)。
作为开发人员,您需要要求 Web Audio 将声音定位在 3D 空间中,并使用 setPosition()
和 setOrientation()
函数设置和/或更新Web Audio 监听器(您的虚拟耳朵)的位置和方向。但是,如果您不是 3D 大师,这可能会有点复杂。这就是为什么我们在 Babylon.js 中为您处理。
到目前为止,我们一直在使用 2D 音效。要将声音转换为空间声音,您需要通过选项进行指定
var music = new BABYLON.Sound("music", "music.wav",
scene, null, { loop: true, autoplay: true, spatialSound: true });
空间声音的默认属性为
distanceModel
(衰减)默认为“linear”方程。其他选项是“inverse”或“exponential”。maxDistance
设置为100。这意味着一旦监听器距离声音超过 100 个单位,音量将为 0。您将听不到声音了panningModel
根据规范设置为“equalpower”。其他可用选项是“HRTF”。规范称其为:“使用与人类受试者测量脉冲响应卷积的更高质量空间化算法。此平移方法渲染立体声输出”。这是使用耳机时的最佳算法。
maxDistance
仅在使用“linear”衰减时使用。否则,您可以使用 rolloffFactor
和 refDistance
选项来调整其他模型的衰减。两者默认都设置为 1,但您可以根据需要更改它们。
例如:
var music = new BABYLON.Sound("music", "music.wav",
scene, null, {
loop: true, autoplay: true, spatialSound: true,
distanceModel: "exponential", rolloffFactor: 2
});
声音在 3D 世界中的默认位置是(0,0,0)。要更改它,请使用 setPosition()
函数
music.setPosition(new BABYLON.Vector3(100, 0, 0));
为了更好地理解,请看看我们 Playground 中的此示例
使用键盘和鼠标在场景中移动。每种声音都由一个紫色的球体表示。当您进入球体时,您将开始听到一种音乐。声音在球体的中心最大,离开球体时逐渐减小到 0。您还会注意到声音在左右扬声器之间以及前后之间进行了平衡(仅使用经典立体声扬声器较难注意到)。
将声音附加到网格
之前的演示很酷,但没有达到我“简单而强大”的标准。我想要更简单易用的东西。我最终采用了以下方法。
只需创建一个 BABYLON.Sound
,将其附加到现有网格,就完成了!只需两行代码!如果网格在移动,声音也会随之移动。您无需做任何事情,其他所有事情都由 Babylon.js 处理。
以下是使用代码
var music = new BABYLON.Sound("Violons", "sounds/violons11.wav",
scene, null, { loop: true, autoplay: true });
// Sound will now follow the box mesh position
music.attachToMesh(box);
调用声音上的 attachToMesh()
函数将自动将其转换为空间 3D 音效。使用上面的代码,您将使用默认的 Babylon.js 值:线性衰减,maxDistance
为 100,平移模型类型为“equalpower
”。
戴上您的耳机,在我们的 Playground 中启动此示例
不要移动相机。您应该会听到我的音乐在您头顶旋转,因为立方体在移动。想获得更好的耳机体验?
在支持 HRTF 的浏览器(如 Chrome 或 Firefox)中启动同一个演示,单击“调试层”按钮将其显示出来,然后向下滚动到音频部分。将当前的平移模型从“普通扬声器”(equalpower)切换到“耳机”(HRTF)。您应该能享受到稍好的体验。如果不行,请更换耳机……或您的耳朵。;-)
注意:您还可以使用箭头键和鼠标(像在 FPS 游戏中一样)在 3D 场景中移动,以检查 3D 音效定位的影响。
创建空间定向 3D 音效
默认情况下,空间音效是全向的。但如果您愿意,也可以拥有定向音效。
注意:定向音效仅适用于附加到网格的空间音效。
以下是使用代码
var music = new BABYLON.Sound("Violons", "violons11.wav", scene, null, { loop: true, autoplay: true });
music.setDirectionalCone(90, 180, 0);
music.setLocalDirectionToMesh(new BABYLON.Vector3(1, 0, 0));
music.attachToMesh(box);
setDirectionalCone
接受三个参数
coneInnerAngle
:内锥体的角度(度)coneOuterAngle
:外锥体的角度(度)coneOuterGain
:当您位于外锥体外部时声音的音量(0.0 到 1.0 之间)
锥体的外角必须大于或等于内角,否则会记录错误,定向音效将不起作用。
setLocalDirectionToMesh()
只是锥体相对于您附加到的网格的方向。默认情况下,它是(1,0,0)
。也就是说,在对象的右侧。
您可以在我们的 Playground 中玩这个示例来更好地理解输出
在 3D 场景中移动。如果您位于由灰色锥体定义的空间内,您应该会听到音乐,否则您将听不到,因为 coneOuterGain
设置为 0。
深入研究
玩我们著名的Mansion Web Audio演示
尝试在 Mansion 场景中找到交互式对象,它们几乎都与声音相关。为了构建此场景,我们的 3D 艺术家Michel Rousseau,没有使用一行代码!他完全使用我们新的 Actions Builder 工具和 3DS Max 完成了此操作。Actions Builder 目前仅在我们的 3DS Max 导出器中可用。我们很快将将其暴露在我们的沙盒工具中。如果您有兴趣构建类似的体验,请阅读这些文章
- 由我们 Babylon.js 的优秀实习生Julien Moreau-Mathis(他创建了该工具)撰写的《Actions Builder》
- 由Michel Rousseau撰写的《在 BabylonJS 中创建操作》。他在这里解释了如何以基本方式使用操作。他使用了与构建Mansion 演示相同的方法,因为 Actions Builder 也支持声音操作。
但是,要真正理解和学习我们在游戏引擎中如何使用 Web Audio 的最佳方法是阅读我们的源代码
- 由 Babylon.js 核心引擎作为静态创建的 BABYLON.AudioEngine。
- 包含多个声音的 BABYLON.SoundTrack。如果您创建了声音,至少会创建一个音轨,这是主音轨。
- 包含您将与之交互的主要逻辑的 BABYLON.Sound
- 可以附加到音频引擎本身(以通过全局音量查看输出)或特定音轨的 BABYLON.Analyser。
请也查看 BABYLON.Scene 和 _updateAudioParameters()
函数,以了解我们如何根据相机的当前位置和方向更新 Web Audio 监听器。
最后,这里有一些有用的资源
- Babylon.js 音频文档
- 通过 Flight Arcade 体验学习 Web Audio,您将学到我尚未在 Babylon.js 中实现的一些功能,例如卷积器。
构建使用多音轨和分析器的演示
为了向您展示我们 Babylon.js 音频栈可以快速完成什么,我构建了这个有趣的演示
它使用了 Babylon.js 的多项功能,例如资产加载器(将自动加载远程 WAV 文件并显示默认加载屏幕)、多声音音轨以及连接到声音音轨的多个分析器对象。
请通过您的 F12 工具查看 multitracks.js 的源代码,了解其工作原理。
此演示基于我过去创作的一首音乐,我对其进行了少量改编:The Geek Council。您可以通过玩滑块为音乐的第一部分进行自己的混音。您可以通过尽可能快地单击“史诗镲片”按钮来添加戏剧性效果。最后,单击“启动史诗结局序列”。它将等待当前循环结束,然后停止前 4 个音轨(鼓、史诗背景、垫乐和号角),以播放最终的史诗序列!:) 单击“重置”按钮可从头开始。
注意:事实证明,要获得出色的声音循环,没有任何瑕疵,您需要避免使用 MP3,正如这个论坛中所解释的:“最简单的解决方案就是不使用 .mp3。如果您改用 .ogg 或 .m4a,应该会发现声音可以无缝循环,前提是您从原始未压缩音频源生成这些文件,而不是从 mp3 文件生成!压缩到 mp3 的过程会在音频开头插入几毫秒的静音,这使得 mp3 不适合循环声音。我在某处读到,静默实际上应该是文件头信息,但它被错误地当作声音数据处理了!”因此,在此演示中我使用了 WAV 文件。它是跨所有浏览器都能正常工作的唯一干净的解决方案。
Music Lounge 演示怎么样?
《Music Lounge 演示》基本上重用了前面描述的所有内容。但您还缺少最后一块才能重现它,因为它基于一个我尚未解释的功能:customAttenuationFunction。
您可以选择提供自己的衰减函数给 Babylon.js,以替换 Web Audio 提供的默认函数(线性、指数和反向),如我们的文档中所述。
例如,如果您尝试这个Playground 演示,您会看到我使用了这个愚蠢的衰减函数
music.setAttenuationFunction(function (currentVolume, currentDistance,
maxDistance, refDistance, rolloffFactor) {
return currentVolume * currentDistance / maxDistance;
});
靠近对象时,音量几乎为 0。越远,声音越大。这完全违反直觉,但只是一个示例,让您了解一下。
在 Music Lounge 演示的情况下,这非常有用。确实,每首音轨的音量不取决于每个立方体与相机的距离,而是取决于立方体与白色圆圈中心的距离。如果您查看源代码,魔术发生在这里
attachedSound.setAttenuationFunction((currentVolume, currentDistance,
maxDistance, refDistance, rolloffFactor) => {
var distanceFromZero = BABYLON.Vector3.Zero()
.subtract(this._box.getBoundingInfo().boundingSphere.centerWorld)
.length();
if (distanceFromZero < maxDistance) {
return currentVolume * (1 - distanceFromZero / maxDistance);
}
else {
return 0;
}
});
我们正在计算当前立方体 (this._box) 与 (0,0,0) 坐标之间的距离,后者恰好是圆的中心。根据此距离,我们为与立方体关联的声音的音量设置了线性衰减函数。演示的其余部分使用附加到各种网格(立方体)的声音和一个连接到全局音量的分析器对象来为由粒子组成的外部圆圈设置动画。
我希望您喜欢这篇文章,就像我写它一样。我也希望它能为您带来创造酷炫 Web Audio / WebGL 应用程序或演示的新想法!
更多 Web 开发实践
本文是 Microsoft 技术布道者和工程师关于实际 JavaScript 学习、开源项目和互操作性最佳实践的 Web 开发系列的一部分,包括Microsoft Edge 浏览器和新的EdgeHTML 渲染引擎。
我们鼓励您在包括 Windows 10 默认浏览器 Microsoft Edge 在内的各种浏览器和设备上进行测试 – 使用 dev.microsoftedge.com 上的免费工具
- 扫描您的网站是否存在过时库、布局问题和可访问性问题
- 下载适用于 Mac、Linux 和 Windows 的免费虚拟机
- 查看包括 Microsoft Edge 路线图在内的各浏览器 Web 平台状态
- 在您自己的设备上远程测试 Microsoft Edge
我们工程师和布道者的更深入的学习
- GitHub 上的编程实验室:跨浏览器测试和最佳实践
- (来自我们的工程团队和 JS 社区)2015 Microsoft Edge Web Summit
- (来自 Rey Bango)哇,我可以在 Mac 和 Linux 上测试 Edge 和 IE!
- (来自 Christian Heilmann)在不破坏 Web 的情况下推进 JavaScript
- (来自 Jacob Rossi)让 Web 正常工作的 Edge 渲染引擎
- (来自 David Catuhe)使用 WebGL 和 Microsoft Edge 释放 3D 渲染
- (来自 Kevin Hill 和 Kiril Seksenov)托管 Web 应用和 Web 平台创新
我们的社区开源项目
- vorlon.JS(跨设备远程 JavaScript 测试)
- manifoldJS(部署跨平台托管 Web 应用)
- babylonJS(轻松实现 3D 图形)
更多免费工具和后端 Web 开发内容