Web Audio API让我们在浏览器中制造一些还不错的声音。这使你的站点,应用,游戏变得更加有趣吸引人。你还可以制造一些像鼓机或者合成器这样特别的音乐应用。在本文中,我们将学习通过Web Audio API做一些简单有趣的项目。
入门
让我们了解一些专业术语。在Web Audio API中,所有的音频操作都是在一个audio上下文中。每个基础的声音操作通过使用链接在一起的音频节点所展现的,从而形成音频路由图。在播放任何声音之前,你需要创建这个音频的上下文。这非常类似于创建canvas元素内部绘制的上下文。下面是我们如何创建一个音频上下文的方法:
var context = new (window.AudioContext || window.webkitAudioContext)();
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到
Safari需要一个webkit前缀支持AudioContext,所以你应该使用上面这行代替new AudioContext();
通常,Web Audio API的工作流程如下所示:
创建源->连接过滤器节点->连接目标
下面有三种类型的来源:
- 振荡器 – 数字计算声音
-
音频样本 – 从音频/视频文件中获取
-
音频流 – 从网络摄像头或者麦克风中的音频
从振荡器开始介绍
一个振荡器是重复的波形。它具有频率,峰值振幅。除了它的频率和振幅以外,振荡器最重要的特色之一是它的波形。四种最常见的振子波是正弦波,三角波,方波,锯齿波。
也可以自定义波形。不同的形状适用于不同的合成技术。他们提供不同的从光滑到刺耳的声音。
Web Audio API用 OscillatorNode来代表重复的波形。我们可以使用上述展示的波形。为此,我们分配value属性值如下:
OscillatorNode.type = 'sine'|'square'|'triangle'|'sawtooth';
你也可以创建一个自定义的波形。你可以使用setPeriodicWave()方法来创建一个波形。可以自动的设置一个定制类型。我们来听听不同的波形产生的不同的声音:
通常的波形运用傅里叶变换自定义波形。如果你想要学习更多自定义的波形(比如,怎样制作警笛)。你可以学习这个优秀的资源中学习。
运行振荡器
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到
我们来一起试试制造一些噪音。这是我们所需要做的:
- 我们需要创建一个Web Audio API上下文
-
在上下文中创建一个振荡器节点
-
选择波形类型
-
设置频率
-
将振荡器链连接目标
-
启动振荡器
让我们转化这些步骤转化为代码吧。
var context = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = context.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.value = 440;
oscillator.connect(context.destination);
oscillator.start();
记录我们如何定义一个音频上下文。Safari浏览器需要webkit前缀。所以我们得让它跨浏览器兼容。
然后我们创建一个振荡器并设置一个波形。默认值是正弦型,所以你可以跳过这一行,我只是想添加它使其更加清晰,更加容易更新。我们可以将频率值设置440HZ,即A4音符(也是默认值)。这些音符从C0到B8的频率都是在16.35到7902.13HZ的范围之间。在这篇文章中,我们将看一个播放一些不同的音符的例子
现在,当我们知道所有这些之后,我们也可以调节音量。为此,我们需要在上下文中创建一个gain节点,将其连在链中,还有连接gain节点连接到目标。
var gain = context.createGain();
oscillator.connect(gain);
gain.connect(context.destination);
var now = context.currentTime;
gain.gain.setValueAtTime(1, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.5);
oscillator.start(now);
oscillator.stop(now + 0.5);
现在你对使用振荡器有一些了解,这是一个很好的练习。这个Codepen链接有振荡器的设置代码。尝试来做一个简单的应用程序,在屏幕上下移动光标来改变音量,左右移动来改变频率。
Web Audio API的计时
创建一个音频软件很重要的事情之一是管理时间。对于这里需要的精度,使用一个JavaScript时钟不是最佳的实践。因为它不够精确。然而,Web Audio API附带了一个currentTime属性,这是需要不断增加的双硬件时间戳,可用于调度音频播放。当音频上下文建立之后,它是从0开始的。尝试运行一下console.log(context.currentTime)查看时间戳。
例如,如果你想要立马播放振荡器,应该立马运行oscillator.start(0)(你可以省略0,因为0是默认值)。然而,你可能想让它从现在开始一秒钟,播放两秒钟,然后停止。下面是如何实现的:
var now = context.currentTime;
oscillator.play(now + 1);
oscillator.stop(now + 3);
这里有两种方法在这提及的。
AudioParam.setValueAtTime(value, startTime)方法在精确时间计划更改变化的值。例如,你想要在一秒之内改变振荡器的频率值:
oscillator.frequency.setValueAtTime(261.6, context.currentTime + 1);
但是,如果想要立刻改变值,也可以使用.setValueAtTime(value, context.currentTime)。你可以通过修改值的属性设置AudioParam。但是,如果它们与自动化事件(使用AudioParam方法的事件)同时发生,则任何的值被忽略而不抛出异常。
AudioParam.exponentialRampToValueAtTime(value, endTime) 方法用来持续的值的变化。这段代码将在一秒钟之内以指数形式减少振荡器的音量。这是一个平稳停止声音的好办法。
gain.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 1);
我们不能使用0作为值,因为需要一个正值,所以我们可以用一个很小的值进行代替。
创建一个Sound类
一旦停止一个振荡器,就无法重新启动它。你没有做错任何事情,这是Web Audio API优化性能的功能。我们可以做的是创建一个声音类来,负责创建一个振荡器节点,播放和停止声音。这个方法我们可以多次调用声音。这里将使用ES6的语法:
class Sound {
constructor(context) {
this.context = context;
}
init() {
this.oscillator = this.context.createOscillator();
this.gainNode = this.context.createGain();
this.oscillator.connect(this.gainNode);
this.gainNode.connect(this.context.destination);
this.oscillator.type = 'sine';
}
play(value, time) {
this.init();
this.oscillator.frequency.value = value;
this.gainNode.gain.setValueAtTime(1, this.context.currentTime);
this.oscillator.start(time);
this.stop(time);
}
stop(time) {
this.gainNode.gain.exponentialRampToValueAtTime(0.001, time + 1);
this.oscillator.stop(time + 1);
}
}
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到
我们将上下文传递给构造函数,因此我们可以在用一个上下文中,创建该Sound类的所有实例。然后有一个init方法,可以创建振荡器和所有必要的滤波器节点,连接它们,等等。play方法接受值(将要播放的音符的频率)还有播放时间。但首先,它创建了一个振荡器,每当我们调用该play方法就会发生这种情况。stop方法在一秒内指数减少了音量,直到它完全停止振荡器 。因此,每当我们需要再次播放这个声音时,我们需要再次创建一个新的Sound类并调用play方法。现在我们来播放一些音符:
let context = new (window.AudioContext || window.webkitAudioContext)();
let note = new Sound(context);
let now = context.currentTime;
note.play(261.63, now);
note.play(293.66, now + 0.5);
note.play(329.63, now + 1);
note.play(349.23, now + 1.5);
note.play(392.00, now + 2);
note.play(440.00, now + 2.5);
note.play(493.88, now + 3);
note.play(523.25, now + 3.5);
将在相同的上下文中播放C D E F G A B C。如果你想要知道这些音符的频率,你可以在这里找到它们。
了解所有这些,使我们能够建立木琴一样的东西!创建了一个新的Sound实例并播放。你可以查看例子并尝试自己制作一个练习。
看看由Greg Hovanesyan(@gregh)创建的例子木琴,在CodePen上
我已经创建了一个小广场,包含所有需要的HTML和CSS,以及创建的Sound类。使用该data-frequency属性获取声音值。 试试这个。
使用录制的声音
现在,你已经用振荡器构建了一些东西,让我们来看看如何处理一个录制的声音。使用振荡器很难再现一些声音。在很多情况下,为了使用真实的声音必须要使用一些录音。可以是.mp3,.ogg,.wav格式等等。有关详细信息,请完整的表格 提供的更多的信息。我喜欢使用.mp3格式,因为轻巧,广泛的支持,还有非常好的音质。
你不能通过图像一样通过URL获取声音。我们必须运行一个XMLHttpRequest 来获取文件,解码数据,并且存入缓冲区。
Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到
class Buffer {
constructor(context, urls) {
this.context = context;
this.urls = urls;
this.buffer = [];
}
loadSound(url, index) {
let request = new XMLHttpRequest();
request.open('get', url, true);
request.responseType = 'arraybuffer';
let thisBuffer = this;
request.onload = function() {
thisBuffer.context.decodeAudioData(request.response, function(buffer) {
thisBuffer.buffer[index] = buffer;
updateProgress(thisBuffer.urls.length);
if(index == thisBuffer.urls.length-1) {
thisBuffer.loaded();
}
});
};
request.send();
};
loadAll() {
this.urls.forEach((url, index) => {
this.loadSound(url, index);
})
}
loaded() {
// what happens when all the files are loaded
}
getSoundByIndex(index) {
return this.buffer[index];
}
}
让我们来看看构造函数。我们在Sound类中接收上下文。接收到将要加载到的URLa列表,以及缓冲区的空数组。
我们有两种方法:loadSound和loadAll。loadAll循环遍历URL列表并且调用loadSound方法,传递索引也非常重要。无论首先加载是哪个请求,这样我们通过缓冲的声音放入数组的正确元素之中。这也让我们来看看最后一个加载的请求是哪个,这意味着完成缓冲区时会加载。
然后你可以调用loaded() 方法,这样可以做一些隐藏加载指示器的事情。最后,getSoundByIndex(index)方法通过索引从缓冲区得到声音进行回放。
decodeAudioData方法有一个较新的基于Promise的语法,但是它在Safari中不起作用:
context.decodeAudioData(audioData).then(function(decodedData) {
// use the decoded data here
});
然后我们必须为声音创建一个类。现在我们有完整的声音类处理用于录音中:
class Sound() {
constructor(context, buffer) {
this.context = context;
this.buffer = buffer;
}
init() {
this.gainNode = this.context.createGain();
this.source = this.context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect(this.gainNode);
this.gainNode.connect(this.context.destination);
}
play() {
this.setup();
this.source.start(this.context.currentTime);
}
stop() {
this.gainNode.gain.exponentialRampToValueAtTime(0.001, this.context.currentTime + 0.5);
this.source.stop(this.context.currentTime + 0.5);
}
}
构造函数接收上下文和缓冲区。我们通过调用createBufferSource()方法创建,而不是像之前用过的createOscillator 。缓冲区通过getSoundByIndex()方法获得音符(缓冲区数组的元素)。现在我们不是使用振荡器创建一个缓冲源,而是设置缓冲区,然后连接目标(或者gain节点还有其它滤波器)。
let buffer = new Buffer(context, sounds);
buffer.loadAll();
sound = new Sound(context, buffer.getSoundByIndex(id));
sound.play();
现在,我们必须创建一个缓冲区实例并调用loadAll方法,加载所有声音到缓冲区中去。我们还有getSoundById方法来获取需要的声音得方法,因此我们将声音给Sound类和调用play()方法。该id可以存储在你可以播放声音的按钮属性中。
这有一个使用缓冲区,录音,音符等的项目:
看看这个Pen The Bluesman – 你可以演奏蓝调 (Web Audio API) 是Greg Hovanesyan (@gregh)在 CodePen上制作的。
你可以使用这个例子作为参考,但是除了你自己的练习,这里还有一个我创建的小广场。这里有所有必要的HTML,CSS,还有一些我在真实的电吉上录的音符的URL链接。试试编写自己的代码吧!
介绍过滤器
Web Audio API允许你添加不同的筛选器节点在声源和目标之间。BiquadFilterNode是一个简单的低阶滤波器,可以控制频率这一部分,哪部分需要加强还是减弱。这让你建立一个均衡器应用和其他效果。这里有八种的双二阶滤波器:高通,低通,带通,低架,高架,高峰,缺口和全通。
高通是一个也可以传递更高频率的过滤器,但是会衰减信号的低频分量。低通通过较低频率,但衰减较高频率。它们也被称为“低切”和“高切”滤波器,因为它解释了信号发生了什么。
高架 和 低架 是用于控制声音的低音和高音的滤波器,它们用于强调或减少高于或低于给定频率的信号。
你将找到Q属性的BiquadFilterNode接口,这代表 Q Factor的双重接口。品质因数或Q Factor控制带宽,影响频率高低。Q Factor越低,带宽越宽,意味着影响的频率越多。Q Factor越高,带宽越窄。
你可以找到更多的滤波器的信息,这里。但我们已经可以构建参数均衡器。它是一个均衡器,可以完全控制频率,带宽和gain节点。
让我们创建一个参数均衡器。
我们来看看怎样运用在声音的失真上。如果你想要的制作一个听起来像电吉他的声音,这就是失真效应。我们使用 WaveShaperNode 接口来代表一个非线性失真器。我们需要做的是创建一条曲线来代表信号,扭曲并产生特定的声音。我们不必花一些时间创建曲线,我们已经做好了。我们也可以调整失真量:
后记
现在你已经了解如何建立Web Audio API,我建议你试试它制作一些自己的项目吧!
这里有一些处理Web音频的库:
-
Pizzicato.js – Pizzicato旨在通过Web Audio API简化您创建和操作声音的方式
-
webaudiox.js – webaudiox.js是一个助手,将让你轻松的使用WebAudio API
-
howler.js – 现代网络的Javascript音频库
-
WAD – 使用HTML5的Web Audio API进行动态合成声音。这很像为了你耳朵的jQuery
-
Tone.js – 用于在浏览器中制作交互式音乐的Web Audio框架
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程
本文来源于网络,其版权属原作者所有,如有侵权,请与小编联系,谢谢!
转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com
链接:https://www.javascriptc.com/2021.html
原文链接:https://www.zcfy.cc/article/introduction-to-web-audio-api