转载:https://www.html5rocks.com/en/tutorials/webaudio/intro/
在HTML5 元素之前,需要使用Flash或其他插件来打破网络的沉默。虽然网络上的音频不再需要插件,但音频标签为实现复杂的游戏和交互式应用程序带来了重大限制。
Web Audio API是一种高级JavaScript API,用于处理和合成Web应用程序中的音频。此API的目标是包括现代游戏音频引擎中的功能以及现代桌面音频制作应用程序中的一些混合,处理和过滤任务。以下是使用这个功能强大的API的温和介绍。
一个AudioContext是用于管理和播放所有声音。要使用Web Audio API生成声音,请创建一个或多个声源并将它们连接到AudioContext
实例提供的声音目标。该连接不需要是直接的,并且可以通过任何数量的中间音频节点,其充当音频信号的处理模块。在Web Audio 规范中更详细地描述了该路由。
单个实例AudioContext
可以支持多个声音输入和复杂的音频图形,因此我们只需要为我们创建的每个音频应用程序中的一个。许多有趣的Web Audio API函数,如创建AudioNodes和解码音频文件数据都是其中的方法AudioContext
。
以下代码段创建了一个AudioContext
:
var context;
window.addEventListener('load', init, false);
function init() {
try {
// Fix up for prefixing
window.AudioContext = window.AudioContext||window.webkitAudioContext;
context = new AudioContext();
}
catch(e) {
alert('Web Audio API is not supported in this browser');
}
}
对于基于WebKit和Blink的浏览器,您当前需要使用webkit
前缀,即webkitAudioContext
。
Web Audio API使用AudioBuffer进行短到中等长度的声音。基本方法是使用XMLHttpRequest获取声音文件。
API支持以多种格式加载音频文件数据,例如WAV,MP3,AAC,OGG 等。针对不同的音频格式浏览器支持变化。
以下代码段演示了加载声音样本:
var dogBarkingBuffer = null;
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function loadDogSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
dogBarkingBuffer = buffer;
}, onError);
}
request.send();
}
音频文件数据是二进制(不是文本),因此我们将responseType
请求设置为'arraybuffer'
。有关更多信息 ArrayBuffers
,请参阅有关XHR2的这篇文章。
一旦接收到(未解码的)音频文件数据,就可以保留它以便稍后解码,或者可以使用AudioContext decodeAudioData()
方法立即解码它。此方法获取ArrayBuffer
存储在其中 的音频文件数据request.response
并异步解码(不阻止主JavaScript执行线程)。
当decodeAudioData()
完成时,它调用的回调函数,其提供经解码的PCM音频数据作为一个AudioBuffer
。
一个简单的音频图表
一旦AudioBuffers
加载了一个或多个,我们就可以播放声音了。让我们假设我们刚刚加载AudioBuffer
了一只狗吠叫的声音并且装载完成了。然后我们可以使用以下代码播放此缓冲区。
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.start(0); // play the source now
// note: on older systems, may have to use deprecated noteOn(time);
}
playSound()
每当有人按下某个键或用鼠标点击某些内容时,就可以调用此函数。
该start(time)
功能可以轻松为游戏和其他时间关键型应用程序安排精确的声音播放。但是,要使此调度正常工作,请确保预先加载声音缓冲区。(在较旧的系统上,您可能需要调用noteOn(time)
而不是start(time)
。)
需要注意的一点是,在iOS上,Apple目前将所有声音输出静音,直到在用户交互事件期间第一次播放声音为止 - 例如,playSound()
在触摸事件处理程序内部调用。除非你绕过这个问题,否则你可能会对iOS上的网络音频“不工作”感到困惑 - 为了避免这样的问题,只需在早期UI事件中播放声音(甚至可以通过连接到零增益的增益节点来静音) - 例如“触摸这里玩”。
当然,最好是创建一个更加通用的加载系统,该加载系统没有硬编码来加载这种特定的声音。有许多方法可以处理音频应用程序或游戏将使用的许多短到中等长度的声音 - 这是使用BufferLoader类的一种方式 。
以下是如何使用BufferLoader
该类的示例。让我们创造两个AudioBuffers
; 并且,一旦它们被加载,让我们同时播放它们。
window.onload = init;
var context;
var bufferLoader;
function init() {
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
bufferLoader = new BufferLoader(
context,
[
'../sounds/hyper-reality/br-jam-loop.wav',
'../sounds/hyper-reality/laughter.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
// Create two sources and play them both together.
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.start(0);
source2.start(0);
}
Web Audio API使开发人员可以精确地安排播放。为了证明这一点,让我们设置一个简单的节奏轨道。可能最广为人知的drumkit模式如下:
简单的摇滚鼓图案
其中每八个音符播放一个hihat,并且每季度在4/4时间内交替播放踢和小鼓。
假设我们已经加载kick
,snare
和hihat
缓冲区,代码要做到这一点很简单:
for (var bar = 0; bar < 2; bar++) {
var time = startTime + bar * 8 * eighthNoteTime;
// Play the bass (kick) drum on beats 1, 5
playSound(kick, time);
playSound(kick, time + 4 * eighthNoteTime);
// Play the snare drum on beats 3, 7
playSound(snare, time + 2 * eighthNoteTime);
playSound(snare, time + 6 * eighthNoteTime);
// Play the hi-hat every eighth note.
for (var i = 0; i < 8; ++i) {
playSound(hihat, time + i * eighthNoteTime);
}
}
在这里,我们只进行一次重复,而不是我们在乐谱中看到的无限循环。该函数playSound
是在指定时间播放缓冲区的方法,如下所示:
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start(time);
}
完整的源代码
您可能想要对声音执行的最基本操作之一是更改其音量。使用Web Audio API,我们可以通过GainNode将源路由到目的地,以便操作卷:
带增益节点的音频图
可以通过以下方式实现此连接设置:
// Create a gain node.
var gainNode = context.createGain();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);
设置图形后,您可以通过以下操作以编程方式更改音量gainNode.gain.value
:
// Reduce the volume.
gainNode.gain.value = 0.5;
以下是使用元素实现的音量控制的演示:
音量:
完整的源代码
现在,假设我们有一个稍微复杂的场景,我们在播放多个声音,但想要在它们之间交叉淡入淡出。这是类似DJ的应用程序中的常见情况,我们有两个转盘,希望能够从一个声源平移到另一个声源。
这可以使用以下音频图表完成:
具有通过增益节点连接的两个源的音频图
为了进行设置,我们只需创建两个GainNode,并通过节点连接每个源,使用类似这样的函数:
function createSource(buffer) {
var source = context.createBufferSource();
// Create a gain node.
var gainNode = context.createGain();
source.buffer = buffer;
// Turn on looping.
source.loop = true;
// Connect source to gain.
source.connect(gainNode);
// Connect gain to destination.
gainNode.connect(context.destination);
return {
source: source,
gainNode: gainNode
};
}
当您在样本之间平移时,天真的线性交叉淡化方法表现出体积下降。
线性交叉淡化
为了解决这个问题,我们使用相等的功率曲线,其中相应的增益曲线是非线性的,并且以更高的幅度相交。这样可以最大限度地减少音频区域之间的音量下降,从而在水平可能略有不同的区域之间产生更均匀的交叉淡入淡出。
等功率交叉淡化
以下演示使用控件在两个声源之间交叉淡入淡出:
鼓 器官
完整的源代码
另一种常见的交叉推子应用是用于音乐播放器应用。当歌曲发生变化时,我们希望淡出当前曲目,并淡化新曲目,以避免震动过渡。为此,请安排将来的交叉淡入淡出。虽然我们可以setTimeout
用来做这个调度,但这并不准确。使用Web Audio API,我们可以使用AudioParam接口来安排参数的未来值,例如a的增益值GainNode
。
因此,给定播放列表,我们可以通过在当前播放的音轨上调度增益减小来调整音轨之间的转换,并且在当前音轨完成播放之前稍微增加下一个音阶的增益:
function playHelper(bufferNow, bufferLater) {
var playNow = createSource(bufferNow);
var source = playNow.source;
var gainNode = playNow.gainNode;
var duration = bufferNow.duration;
var currTime = context.currentTime;
// Fade the playNow track in.
gainNode.gain.linearRampToValueAtTime(0, currTime);
gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
// Play the playNow track.
source.start(0);
// At the end of the track, fade it out.
gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
// Schedule a recursive track change with the tracks swapped.
var recurse = arguments.callee;
ctx.timer = setTimeout(function() {
recurse(bufferLater, bufferNow);
}, (duration - ctx.FADE_TIME) * 1000);
}
Web Audio API提供了一组方便的RampToValue
方法来逐步更改参数的值,例如linearRampToValueAtTime
和exponentialRampToValueAtTime
。
虽然可以从内置线性和指数线(如上所述)中选择转换时序功能,但您也可以使用该setValueCurveAtTime
函数通过值数组指定自己的值曲线。
以下演示使用上述方法显示两个轨道之间的类似播放列表的自动交叉渐变:
完整的源代码
带有音频的音频图 BiquadFilterNode
Web Audio API允许您将声音从一个音频节点传输到另一个音频节点,从而创建一个可能复杂的处理器链,为您的声音形式添加复杂的效果。
一种方法是在您的声源和目标之间放置BiquadFilterNode。这种类型的音频节点可以做各种低阶滤波器,可用于构建图形均衡器甚至更复杂的效果,主要与选择要强调的声音频谱的哪些部分以及要制服哪些部分有关。
支持的过滤器类型包括:
并且所有滤波器都包括用于指定一定量增益的参数 ,应用滤波器的频率以及品质因数。低通滤波器保持较低的频率范围,但丢弃高频。中断点由频率值确定,Q因子是无单位的,并确定图形的形状。增益仅影响某些滤波器,例如低架滤波器和峰值滤波器,而不影响此低通滤波器。
让我们设置一个简单的低通滤波器,只从声音样本中提取基数:
// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 'lowpass'; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.start(0);
以下演示使用了类似的技术,您可以通过复选框启用和禁用低通滤波器,以及使用滑块调整频率和质量值:
过滤: 频率: 质量:
完整的源代码
一般来说,需要调整频率控制以在对数刻度上工作,因为人类听觉本身的工作原理相同(即A4为440hz,A5为880hz)。有关更多详细信息,请参阅FilterSample.changeFrequency
上面源代码链接中的 函数。
最后,请注意示例代码允许您连接和断开过滤器,动态更改AudioContext图。我们可以通过调用断开AudioNodes与图表的连接node.disconnect(outputNumber)
。例如,要将图表从通过过滤器重新路由到直接连接,我们可以执行以下操作:
// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);
我们已经介绍了API的基础知识,包括加载和播放音频样本。我们已经构建了带有增益节点和滤波器的音频图表,以及预定的声音和音频参数调整,以实现一些常见的声音效果。此时,您已准备好构建一些甜蜜的Web音频应用程序!
如果您正在寻求灵感,许多开发人员已经使用Web Audio API 创建了 出色的工作。我最喜欢的一些包括: