使用 Web Audio API
让我们看看如何开始使用Web Audio API。我们将简要介绍一些概念,然后研究一个简单的收音机示例,该示例允许我们加载音频轨道、播放和暂停它,以及更改其音量和立体声声场。
Web Audio API 不会替换<audio>
媒体元素,而是对其进行补充,就像<canvas>
与<img>
元素共存一样。您的用例将决定您使用哪些工具来实现音频。如果您想控制音频轨道的播放,则<audio>
媒体元素提供比 Web Audio API 更好、更快的解决方案。如果您想执行更复杂的音频处理以及播放,则 Web Audio API 提供了更大的功能和控制力。
Web Audio API 的一项强大功能是它没有严格的“声音调用限制”。例如,一次没有 32 或 64 个声音调用的上限。某些处理器可能能够播放超过 1000 个同时发出的声音而不会出现卡顿。
示例代码
我们的收音机如下所示
请注意带有播放按钮的复古磁带录音机,以及音量和声场滑块,允许您更改音量和立体声声场。我们可以使它变得更加复杂,但这对于这个阶段的简单学习来说是理想的。
音频图
Web Audio API 中的所有内容都基于音频图的概念,音频图由节点组成。
Web Audio API 在音频上下文中处理音频操作,并且旨在允许模块化路由。基本音频操作使用音频节点执行,这些节点链接在一起形成音频路由图。您有输入节点,它是您正在操作的声音的来源,修改节点根据需要更改这些声音,以及输出节点(目标),允许您保存或听到这些声音。
即使在单个上下文中,也支持具有不同通道布局的多个音频源。由于这种模块化设计,您可以创建具有动态效果的复杂音频功能。
音频上下文
为了能够使用 Web Audio API 做任何事情,我们需要创建音频上下文的实例。然后,这使我们能够访问 API 的所有功能。
const audioContext = new AudioContext();
当我们这样做时会发生什么?会自动为我们创建一个BaseAudioContext
并将其扩展到联机音频上下文。我们需要这样做,因为我们希望播放实时声音。
注意:如果您只想处理音频数据,例如缓冲和流式传输但不想播放它,您可能需要考虑创建OfflineAudioContext
。
加载声音
现在,我们创建的音频上下文需要一些声音来播放。使用 API 有几种方法可以做到这一点。让我们从一种简单的方法开始——因为我们有一个收音机,所以我们很可能想要播放完整的歌曲曲目。此外,为了实现无障碍访问,最好在 DOM 中公开该曲目。我们将使用<audio>
元素在页面上公开歌曲。
<audio src="myCoolTrack.mp3"></audio>
注意:如果您要加载的声音文件位于不同的域中,则需要使用crossorigin
属性;有关更多信息,请参阅跨源资源共享 (CORS)。
要使用 Web Audio API 提供的所有好东西,我们需要从该元素中获取源并将其管道到我们创建的上下文中。幸运的是,有一种方法可以让我们做到这一点——AudioContext.createMediaElementSource
// get the audio element
const audioElement = document.querySelector("audio");
// pass it into the audio context
const track = audioContext.createMediaElementSource(audioElement);
注意:上面的<audio>
元素在 DOM 中由类型为HTMLMediaElement
的对象表示,该对象带有自己的一组功能。所有这些都保持不变;我们只是允许声音可用于 Web Audio API。
控制声音
在 Web 上播放声音时,允许用户控制声音非常重要。根据用例的不同,有无数种选择,但我们将提供播放/暂停声音、更改音轨音量和将其从左到右平移的功能。
从 JavaScript 代码以编程方式控制声音受浏览器的自动播放支持策略的涵盖,因此可能会被阻止,除非用户授予权限(或允许列表)。自动播放策略通常要求在脚本触发音频播放之前,要么获得明确的许可,要么用户与页面进行交互。
这些特殊要求的存在本质上是因为意外的声音可能会令人恼火和干扰,并且可能导致无障碍问题。您可以在我们的文章媒体和 Web Audio API 的自动播放指南中了解更多相关信息。
由于我们的脚本是在响应用户输入事件(例如,单击播放按钮)时播放音频的,因此我们状态良好,并且不应受到自动播放阻止的影响。因此,让我们首先了解一下我们的播放和暂停功能。我们有一个播放按钮,当曲目正在播放时,它会更改为暂停按钮
<button data-playing="false" role="switch" aria-checked="false">
<span>Play/Pause</span>
</button>
在播放曲目之前,我们需要将音频图从音频源/输入节点连接到目标。
我们已经通过将音频元素传递到 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);
这将使我们的音频图如下所示
增益的默认值为 1;这使当前音量保持不变。增益可以设置为大约 -3.4028235E38 的最小值和大约 3.4028235E38 的最大值(JavaScript 中的浮点数范围)。在这里,我们将允许收音机将增益提高到 2(原始音量的两倍)并降低到 0(这将有效地使我们的声音静音)。
让我们让用户控制这一点——我们将使用范围输入
<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
上的值。这使得它们能够更加灵活,例如允许在一段时间内将一组特定的值传递给参数以在其之间更改。
太好了,现在用户可以更新音轨的音量了!如果您想添加静音功能,则增益节点是使用的完美节点。
向我们的应用程序添加立体声声场
让我们添加另一个修改节点来练习我们刚刚学到的内容。
有一个StereoPannerNode
节点,如果用户具有立体声功能,它可以改变左右扬声器之间声音的平衡。
注意:StereoPannerNode
适用于简单的场景,你只需要从左到右进行立体声声像定位。还有一个PannerNode
,它可以对3D空间或声音的空间化进行大量控制,从而创建更复杂的效果。例如,这在游戏中和3D应用程序中用于创建鸟儿从头顶飞过的声音,或声音从用户背后传来的效果。
为了可视化,我们将使我们的音频图看起来像这样
这次让我们使用构造函数方法来创建节点。当我们以这种方式进行时,必须传入上下文以及该特定节点可能接受的任何选项。
const pannerOptions = { pan: 0 };
const panner = new StereoPannerNode(audioContext, pannerOptions);
注意:目前并非所有浏览器都支持使用构造函数方法创建节点。较旧的工厂方法得到更广泛的支持。
这里我们的值范围从 -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);
剩下的唯一事情就是试用一下这个应用程序:在 Codepen 上查看最终演示。
总结
太棒了!我们有一个播放“磁带”的收音机,我们可以调整音量和立体声声像定位,从而获得一个相当基本的音频图。
这构成了你在网站或 Web 应用程序中添加音频所需的一些基础知识。Web Audio API 还有更多功能,但是一旦你掌握了节点的概念以及如何构建音频图,我们就可以继续学习更复杂的功能。
更多示例
还有其他示例可供学习有关 Web Audio API 的更多信息。
该Voice-change-O-matic是一个有趣的语音处理和声音可视化 Web 应用程序,它允许你选择不同的效果和可视化效果。该应用程序相当基础,但它演示了同时使用多个 Web Audio API 功能。(运行 Voice-change-O-matic 在线演示)。
另一个专门用于演示 Web Audio API 的应用程序是Violent Theremin,这是一个简单的 Web 应用程序,它允许你通过移动鼠标指针来改变音高和音量。它还提供了一个迷幻的灯光秀(查看 Violent Theremin 源代码)。
还可以查看我们的webaudio-examples 代码库以获取更多示例。