这篇文章将让我们了解如何开始使用 Web Audio API。本文会先简要介绍一些概念,然后研究一个简单的便携式音响示例,这个示例允许我们加载一个音频轨道,播放和暂停它,并改变它的音量和立体声像。
Web Audio API 并不能取代 <audio>
媒体元素,而是对其进行补充,就像 <canvas>
与 <img>
元素并存一样。你的用例将决定你使用什么工具来实现音频。如果你想控制音频轨道的播放,<audio>
媒体元素提供了比 Web Audio API 更好、更快的解决方案,而如果你想进行更复杂的音频处理以及播放,Web Audio API 就提供了更丰富的功能和控制。
Web Audio API 的一个强大功能是它没有严格的 “声音调用限制”。例如,没有一次只能进行 32
或 64
次声音调用的上限。一些处理器可能能够同时播放超过 1000
个声音而不会出现卡顿。
示例代码
我们的便携式音响看起来是这样的:
请注意这个复古的卡带式录音机,上面有一个播放按钮,以及音量和平衡滑块,允许您调整音量和立体声像。我们可以让它变得更加复杂,但在这个阶段,这样就已经非常适合简单的学习了。
你可以在 Codepen 上查看最终的演示,或者在 GitHub 上查看源代码。
音频图(Audio graphs)
Web Audio API 中的所有内容都围绕着音频图的概念展开,而音频图又是由节点组成的。
Web Audio API 在音频上下文(audio context)中处理音频操作,其设计旨在实现模块化路由(modular routing)。基本的音频操作通过音频节点(audio nodes)执行,这些节点连接在一起形成音频路由图(audio routing graph)。其中有处理声音来源的输入节点,有可根据需要改变这些声音的修改节点,还有可让你保存或听到这些声音的输出节点(destinations)。
即使在单个上下文中,也支持具有不同通道布局的多个音频源。由于这种模块化设计,您可以创建具有动态效果的复杂音频功能。
音频上下文(Audio context)
为了能够使用 Web Audio API 进行各种操作,访问它的所有特性和功能,我们需要先创建一个音频上下文的实例。
const audioContext = new AudioContext();
那么当我们这样做的时候发生了什么呢?一个 BaseAudioContext
会自动被创建,并扩展为一个在线音频上下文,这么做的原因是因为我们想要播放实时的声音。
注意:
如果你只想要处理音频数据,比如缓冲和流式传输音频数据但不播放它,那么你可能希望研究一下如何创建一个OfflineAudioContext
。
加载声音
现在,我们创建的音频上下文需要一些声音来播放。API提供了几种方法来实现这一点。我们不妨从一个简单的方法开始,既然我们有一个便携式音响,那么我们很可能想要播放一整首歌曲。此外,为了可访问性,将曲目暴露在DOM
中也是一个很不错的选择,因此,我们将使用<audio>
元素在页面上显示歌曲。
<audio src="myCoolTrack.mp3"></audio>
注意:
如果你正在加载的音频文件位于不同的域上,那么你需要使用crossorigin
属性;有关更多信息,请参阅跨源资源共享(CORS)。
要使用Web Audio API提供的所有好东西,我们需要从该元素中获取源并将其传输到我们创建的上下文中。幸运的是,有一个方法允许我们这样做——AudioContext.createMediaElementSource
:
// 获取 audio 元素
const audioElement = document.querySelector("audio");
// 将 audio 元素传入音频上下文
const track = audioContext.createMediaElementSource(audioElement);
注意:
上面的<audio>
元素在DOM中由一个HTMLMediaElement
类型的对象表示,该对象自带一组功能。所有这些功能都保持不变,我们只是允许声音可用于Web Audio API而已。
控制声音
在网页上播放声音时,允许用户控制声音很重要。根据用例的不同,存在无数种选择,但我们将提供播放/暂停声音、改变音量以及将声音从左到右平移的功能。
通过 JavaScript
编程方式控制声音会受到浏览器自动播放支持策略的限制,因此,如果没有用户的许可,很可能会被阻止。自动播放策略通常要求在脚本触发音频播放之前,要么获得了明确的许可,要么需要用户与页面进行交互。
这些特殊要求之所以存在,主要是因为意外的声音可能会打扰用户或者令人感到厌烦,并可能导致可访问性问题。你可以通过《媒体和Web Audio API的自动播放指南》了解更多相关信息。
由于我们的脚本是在响应用户输入事件(例如点击播放按钮)时播放音频的,因此情况良好,应该不会遇到自动播放被阻止的问题。所以,我们首先看看播放和暂停功能。我们有一个播放按钮,当曲目正在播放时,它会变成暂停按钮:
<button data-playing="false" role="switch" aria-checked="false">
<span>Play/Pause</span>
</button>
在播放曲目之前,我们需要将音频图从音频源/输入节点连接到目标节点。
我们已经通过将 audio
元素传递给API来创建了一个输入节点。在大多数情况下,不需要创建输出节点,只需将其它节点连接到BaseAudioContext.destination
即可:
track.connect(audioContext.destination);
将这些节点可视化的一个好方法是绘制一个音频图,这样你就可以直观的看到它了。我们当前的音频图如下所示:
现在我们可以添加播放和暂停功能:
// Select our play button
const playButton = document.querySelector("button");
playButton.addEventListener(
"click",
() => {
// Check if context is in suspended state (autoplay policy)
if (audioContext.state === "suspended") {
audioContext.resume();
}
// Play or pause track depending on state
if (playButton.dataset.playing === "false") {
audioElement.play();
playButton.dataset.playing = "true";
} else if (playButton.dataset.playing === "true") {
audioElement.pause();
playButton.dataset.playing = "false";
}
},
false,
);
我们还需要考虑曲目播放完毕时应该做什么。我们的HTMLMediaElement
在播放完毕后会触发一个ended
事件,所以我们可以监听这个事件并运行相应的代码:
audioElement.addEventListener(
"ended",
() => {
playButton.dataset.playing = "false";
},
false,
);
修改声音
本节我们将深入研究一下如何通过一些基本的修改节点,来改变现有的声音。这正是Web Audio API真正开始发挥作用的地方。首先,我们来改变一下音量,这可以通过 GainNode
来实现,它代表了声波的大小。
通过Web Audio API,你可以通过两种方式创建节点,一个是上下文本身的工厂方法(如audioContext.createGain()
),另一个是节点的构造函数(如new GainNode()
)。我们这里将使用工厂方法:
const gainNode = audioContext.createGain();
现在我们必须更新前面的音频图,即输入连接到增益节点,然后增益节点连接到目标节点:
track.connect(gainNode).connect(audioContext.destination);
更新后的效果如下图所示:
增益(gain
)的默认值是 1
,即保持当前音量不变,其取值范围为-3.4028235E38 ~ 3.4028235E38
(JavaScript
中的浮点数范围)。在这里,我们将允许将增益调高到2
(原音量的两倍)并调低到0
(静音)。
我们通过一个range
标签,来赋予用户做这件事的控制权:
<input type="range" id="volume" min="0" max="2" value="1" step="0.01" />
接下里我们获取这个输入的值,并在输入节点的值被用户更改时更新增益值:
const volumeControl = document.querySelector("#volume");
volumeControl.addEventListener(
"input",
() => {
gainNode.gain.value = volumeControl.value;
},
false,
);
注意:
节点对象(如GainNode.gain
)的值不是一个简单值,而是AudioParam
类型的对象。这也是为什么我们必须设置GainNode.gain
的value
属性,而不是直接给gain
赋值的原因了。这种赋值方式更加灵活,例如,可以为参数传递一组特定的值,以便在一段特定的时间内进行更改。
很好,现在用户可以更新音轨的音量了!如果你想要添加静音功能,增益节点(GainNode
)也可以完美实现。
向应用中添加立体声平移功能
我们再添加一个修改节点来练习刚刚学到的东西。
如果用户具有立体声功能,可以通过 StereoPannerNode
节点改变左右扬声器之间声音的平衡。
注意:StereoPannerNode
适用于简单的从左到右的立体声平移情况。还有一个PannerNode
,它允许对3D空间或声音空间化进行大量的控制,以创建更复杂的效果,如在游戏和3D应用程序中用于创建头顶飞过的鸟或来自用户身后的声音等效果。
为了可视化,再次更新后的音频图如下图所示:
这次我们使用构造函数来创建节点,这种方式必须把上下文和该特定节点可能用到的各种选项作为参数传入:
const pannerOptions = { pan: 0 };
const panner = new StereoPannerNode(audioContext, pannerOptions);
注意:
目前,并不是所有浏览器都支持以构造函数的方式创建节点,但对较旧的工厂方法方式都有很好的支持。
这里我们再次使用range
标签来更改参数的值,范围从-1
(最左边)到1
(最右边):
<input type="range" id="panner" min="-1" max="1" value="0" step="0.01" />
我们用和前面相同方式来调整平移器的值:
const pannerControl = document.querySelector("#panner");
pannerControl.addEventListener(
"input",
() => {
panner.pan.value = pannerControl.value;
},
false,
);
我们再次调整音频图,将所有节点连接在一起:
track.connect(gainNode).connect(panner).connect(audioContext.destination);
总结
太棒了!到此我们就得到一个可以播放 “磁带” 的便携式音响和一个相当基础的音频图了,并且我们还可以调整音量和立体声像。
这个示例涵盖了将音频添加到网站或网络应用程序所需的一些基础知识。当然,Web Audio API的功能远不止于此,但一旦掌握了节点和音频图的概念,我们就可以继续研究更复杂的功能了。
评论0
暂时没有评论