Web Audio API:为什么要编写代码?

已发表: 2022-03-11

Web 音频 API 的第一个草案于 2011 年出现在 W3C 中。尽管网页中的音频已经得到了很长时间的支持,但直到最近才出现从 Web 浏览器生成音频的正确方法。 我个人将这归功于谷歌浏览器,因为对于谷歌的兴趣,浏览器开始成为计算机中最重要的部分。 您可能还记得,在 Google Chrome 出现之前,Web 浏览器的领域并没有开始发生太大变化。 如果您此时在网页中使用声音,那将是一个糟糕的设计决定。 但自从网络实验的想法出现后,网络音频又开始变得有意义了。 现在的网络浏览器是另一种艺术表达的工具,网络浏览器中的视频和音频在其中起着至关重要的作用。

Web Audio API:为什么要编写代码?

Web Audio API:为什么要编写代码?
鸣叫

Web Audio API 可能很难用于某些目的,因为它仍在开发中,但已经存在许多 JavaScript 库以使事情变得更容易。 在本例中,我将向您展示如何使用名为 Tone.js 的库开始使用 Web Audio API。 有了这个,您将能够仅通过学习基础知识来满足您的大部分浏览器声音需求。

你好网络音频 API

入门

我们将在不使用库的情况下开始。 我们的第一个实验将涉及制作三个正弦波。 由于这将是一个简单的示例,我们将只创建一个名为 hello.html 的文件,这是一个带有少量标记的纯 HTML 文件。

 <!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>

Web Audio API 的核心是音频上下文。 音频上下文是一个包含与网络音频相关的所有内容的对象。 在单个项目中拥有多个音频上下文并不是一种好的做法。 我们将首先按照 Mozilla 的 Web Audio API 文档给出的建议实例化一个音频上下文。

 var audioCtx = new (window.AudioContext || window.webkitAudioContext);

制作振荡器

实例化音频上下文后,您已经有了一个音频组件:audioCtx.destination。 这就像你的扬声器。 要发出声音,您必须将其连接到 audioCtx.destination。 现在要产生一些声音,让我们创建一个振荡器:

 var sine = audioCtx.createOscillator();

很棒,但还不够。 它还需要启动并连接到我们的 audioCtx.destination:

 sine.start(); sine.connect(audioCtx.destination);

通过这四行代码,您将拥有一个播放正弦声音的非常烦人的网页,但现在您了解了模块如何相互连接。 在下面的脚本中,将有三个连接到输出的正弦音调,每个都有不同的音调。 代码非常不言自明:

 //create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);

振荡器不仅限于正弦波,还可以是三角形、锯齿形、方形和自定义形状,如 MDN 中所述。

Web音频的补丁逻辑

接下来,我们将在我们的 Web Audio 组件管弦乐队中添加一个增益模块。 这个模块允许我们改变声音的幅度。 它类似于音量旋钮。 我们已经使用 connect 函数将振荡器连接到音频输出。 我们可以使用相同的连接函数来连接任何音频组件。 如果您使用的是 Firefox,并查看网络音频控制台,您将看到以下内容:

如果我们想改变音量,我们的补丁应该是这样的:

这意味着振荡器不再连接到音频目标,而是连接到增益模块,并且该增益模块连接到目标。 总是想象你用吉他踏板和电缆来做这件事是件好事。 代码将如下所示:

 var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;

您可以在 https://github.com/autotel/simple-webaudioapi/ 找到解决方案。

GainNode 是最基本的效果单元,但也有延迟、卷积器、双二次滤波器、立体声声相器、波形整形器等等。 您可以从 Tone.js 等库中获取新效果。

将这些声音补丁之一存储在它们自己的对象中将允许您根据需要重用它们,并使用更少的代码创建更复杂的编排。 这可能是未来帖子的主题。

使用 Tone.js 让事情变得更简单

现在我们已经简要了解了 vanilla Web Audio 模块的工作原理,让我们来看看很棒的 Web Audio 框架:Tone.js。 有了这个(以及用于用户界面组件的 NexusUI),我们可以非常轻松地构建更有趣的合成器和声音。 为了尝试一下,让我们制作一个采样器并对其应用一些用户交互效果,然后我们将为这个示例添加一些简单的控件。

Tone.js 采样器

我们可以从创建一个简单的项目结构开始:

 simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html

我们的 JavaScript 库将驻留在js目录中。 出于本演示的目的,我们可以使用 NoiseCollector 的 hit4.wav 文件,该文件可以从 Freesound.org 下载。

Tone.js 通过 Player 对象提供其功能。 该对象的基本功能是加载样本,并循环播放或播放一次。 我们的第一步是在 sampler.html 文件中的“sampler”变量中创建一个播放器对象:

 <!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>

注意播放器构造函数的第一个参数是WAV文件的名称,第二个是回调函数。 WAV 不是唯一受支持的文件类型,兼容性更多地取决于网络浏览器而不是库。 当播放器完成将样本加载到其缓冲区中时,回调函数将运行。

我们还必须将采样器连接到输出。 Tone.js 的做法是:

 sampler.toMaster();

... 其中 sampler 是一个 Tone.Player 对象,在第 10 行之后。toMaster 函数是 connect(Tone.Master) 的简写。

如果您在开发人员控制台打开的情况下打开 Web 浏览器,您应该会看到“已加载样本”消息,表明播放器已正确创建。 在这一点上,您可能想听到样本。 为此,我们需要在网页上添加一个按钮,并对其进行编程以在按下后播放样本。 我们将在正文中使用 NexusUI 按钮:

 <canvas nx="button"></canvas>

您现在应该会在文档中看到一个圆形按钮。 为了对其进行编程以播放我们的示例,我们添加了一个 NexusUI 侦听器,如下所示:

 button1.on('*',function(data) { console.log("button pressed!"); })

NexusUI 的突出之处在于它为每个 NexusUI 元素创建了一个全局变量。 您可以将 NexusUI 设置为不这样做,而是通过将 nx.globalWidgets 设置为 false 将这些变量仅存在于 nx.widgets[] 中。 在这里,我们将只创建几个元素,所以我们将坚持这种行为。

和 jQuery 一样,我们可以放置这些 .on 事件,第一个参数是事件名称。 在这里,我们只是为按钮所做的任何事情分配一个功能。 这不管写成“*”。 您可以了解有关 NexusUI API 中每个元素的事件的更多信息。 要在按下按钮时播放样本而不是记录消息,我们应该运行采样器的启动函数。

 nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }

另请注意,侦听器进入 onload 回调。 NexusUI 元素是在画布中绘制的,在 nx 调用 onload 函数之前,您无法引用它们。 就像在 jQuery 中处理 DOM 元素一样。

该事件在鼠标按下和释放时触发。 如果您希望它仅在按下时触发,则必须评估 event.press 是否等于 1。

有了这个,您应该有一个按钮,可以在每次按下时播放样本。 如果您将 sampler.retrigger 设置为 true,它将允许您播放样本,无论它是否正在播放。 否则,您必须等到示例完成才能重新触发它。

应用效果

使用 Tone.js,我们可以轻松创建延迟:

 var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();

第一个参数是延迟时间,可以用乐谱编写,如下所示。 第二个是湿电平,这意味着原始声音和对其有影响的声音之间的混合。 对于延迟,您通常不希望 100% 湿润,因为延迟相对于原始声音来说很有趣,而单独的湿润并不是很吸引人,因为两者结合在一起。

下一步是从 master 上拔下我们的采样器并将其插入延迟。 调整采样器连接到主控的线路:

 sampler.connect(delay);

现在再试一次按钮,看看有什么不同。

接下来,我们将在文档正文中添加两个刻度盘:

 <canvas nx="dial"></canvas> <canvas nx="dial"></canvas>

我们使用 NexusUIlistener 将表盘的值应用于延迟效果:

 dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })

您可以在每个事件上调整的参数可以在 Tone.js 文档中找到。 为了延迟,它就在这里。 现在您已准备好尝试该示例并使用 NexusUI 拨盘调整延迟参数。 这个过程可以通过每个 NexusUI 元素轻松完成,不仅限于效果。 例如,还可以尝试添加另一个表盘,并添加其侦听器,如下所示:

 dial3.on('*',function(data) { sampler.playbackRate=data.value; })

您可以在 github.com/autotel/simpleSampler 找到这些文件

结论

当我浏览这些 API 时,我开始对开始出现在我脑海中的所有可能性和想法感到不知所措。 这种音频实现与传统数字音频实现的最大区别不在于音频本身,而在于上下文。 这里没有新的合成方法。 相反,创新在于音频和音乐制作现在正在满足网络技术。

我个人参与了电子音乐制作,这个领域一直存在这种矛盾,即在实际演奏音乐和按播放录制的曲目之间存在歧义。 如果您想真正制作现场电子音乐,您必须能够创建自己的表演工具或“音乐制作机器人”进行现场即兴创作。 但如果电子音乐的演奏变成简单地在预先准备好的音乐制作算法中调整参数,那么观众也可以参与到这个过程中。 我一直在做一些关于将网络和音频整合到众包音乐中的小实验,也许很快我们就会参加那些音乐来自观众通过智能手机的聚会。 毕竟,这与我们在洞穴时代可能享受的节奏即兴演奏并没有什么不同。


进一步阅读 Toptal 工程博客:

  • WebAssembly/Rust 教程:音高完美的音频处理
  • MIDI 教程:创建由 MIDI 硬件控制的基于浏览器的音频应用程序