Web Audio API 使用入门

Boris Smus
Boris Smus

在 HTML5 <audio> 元素之前,必须使用 Flash 或其他插件 打破网络的沉默虽然网络上的音频 需要使用插件,那么音频标记会显著限制 实现复杂的游戏和互动式应用

Web Audio API 是一种高级 JavaScript API,用于处理和 在 Web 应用中合成音频。此 API 旨在 包括现代游戏音频引擎中的功能,以及 混合、处理和过滤任务的功能, 音频制作应用。接下来将简要介绍 使用以下强大的 API。

AudioContext 使用入门

AudioContext 用于管理和播放所有声音。为了生成 使用 Web Audio API 制作声音,创建一个或多个声源 然后将它们连接到 AudioContext 提供的声音目标位置 实例。此连接不必是直接连接,并且可以经过 充当处理进程的中间 AudioNodes 用于音频信号的模块此路由部分已在 详情请参阅网络音频规范

单个 AudioContext 实例可以支持多个声音输入 以及复杂的音频图表,因此每个图表只需要 音频应用

以下代码段会创建一个 AudioContext

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

对于基于 WebKit 的旧版浏览器,请使用 webkit 前缀,例如 webkitAudioContext

许多有趣的 Web Audio API 功能,如创建 AudioNode 和解码音频文件数据是 AudioContext 的方法。

正在加载声音

Web Audio API 使用 AudioBuffer 处理短到中等长度音频 声音。基本方法是使用 XMLHttpRequest 发出 来提取声音文件

该 API 支持加载多种格式的音频文件数据,例如 WAV、MP3、AAC、OGG 和其他格式。浏览器支持 音频格式各有不同

以下代码段演示了如何加载声音示例:

var dogBarkingBuffer = null;
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() 完成后,它会调用回调函数, 以 AudioBuffer 的形式提供已解码的 PCM 音频数据。

正在响铃

<ph type="x-smartling-placeholder">
</ph> 简单的音频图表
简单的音频图表

加载一个或多个 AudioBuffers 后,就可以播放了 声音。假设我们刚刚加载了一个带有声音的 AudioBuffer。 以及加载已完成。然后,我们就可以 替换为以下代码。

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.noteOn(0);                          // play the source now
}

每当有人按下某个键或某个键时,都可调用以下 playSound() 函数 点击某个对象。

noteOn(time) 函数可让您轻松调度精确的声音 。不过,要 此时间表运行正常,请确保您的声音缓冲区 。

抽象化 Web Audio API

当然,创建更通用的加载系统 它并未经过硬编码有很多 处理许多短长度到中等长度的声音 音频应用或游戏会使用的 - 以下是一种使用 BufferLoader(不属于 Web 标准的方法)。

以下示例展示了如何使用 BufferLoader 类。 创建两个 AudioBuffers;加载完成后 就可以同时播放这两首曲目

window.onload = init;
var context;
var bufferLoader;

function init() {
    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.noteOn(0);
    source2.noteOn(0);
}

时间应对:有节奏地播放声音

借助 Web Audio API,开发者可以精确地安排播放时间。接收者 我们来设置一个简单的节奏轨道可能 最广为人知的架子鼓模式如下:

<ph type="x-smartling-placeholder">
</ph> 简单的摇滚鼓模式
简单的摇滚鼓

其中,每八个音演奏一个踩镲,鼓和小军鼓 每个季度交替进行

假设我们已加载 kicksnarehihat 缓冲区, 代码非常简单:

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.noteOn(time);
}

更改音量

您可能想要对声音执行的一项最基本操作是 调节音量。使用 Web Audio API,我们可以将源路由到 其目的地通过 AudioGainNode 控制其目标, 音量:

<ph type="x-smartling-placeholder">
</ph> 包含增益节点的音频图表
包含增益节点的音频图表

您可以按如下方式完成此连接设置:

// Create a gain node.
var gainNode = context.createGainNode();
// 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 的应用中非常常见 希望能够从一个声源平移到另一个声源。

这可以使用以下音频图表实现:

<ph type="x-smartling-placeholder">
</ph> 包含两个通过增益节点连接的来源的音频图表
包含两个通过增益节点连接的来源的音频图表

要进行此设置,我们只需创建两个 AudioGainNodes, 通过节点传递每个来源,代码如下所示:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    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
    };
}

等功率淡入淡出

简单的线性淡入淡出方法显示了平移时的音量下降 之间的差异。

<ph type="x-smartling-placeholder">
</ph> 线性淡入淡出
线性淡入淡出

为了解决这个问题,我们使用了等功率曲线,其中 对应的增益曲线是非线性的,并且在较高的位置相交 振幅。这样可以最大限度地减小音频区域之间的音量骤降, 呈现出更均匀的淡入淡出 也有所不同

<ph type="x-smartling-placeholder">
</ph> 等功率淡入淡出。
等功率淡入淡出

播放列表淡入淡出

另一个常见的淡入淡出应用是用于音乐播放器应用。 当歌曲发生变化时,我们希望将当前曲目淡出, 以免出现突兀的过渡为此,请安排 交叉淡入未来。虽然我们可以使用 setTimeout 来实现此目的, 但这并不精确。借助 Web Audio API 可以使用 AudioParam 接口安排 参数,例如 AudioGainNode 的增益值。

因此,对于给定的播放列表,我们可以通过安排 而当前播放曲目的增益在 在当前曲目播放完毕之前两个片段:

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.noteOn(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 方法 您可以逐渐更改参数值,例如 linearRampToValueAtTimeexponentialRampToValueAtTime

虽然过渡时间函数可以从内置的线性函数中选择, 和指数型维度(如上所示),您还可以自行指定 通过使用 setValueCurveAtTime 函数的值数组实现曲线。

对声音应用简单的过滤效果

<ph type="x-smartling-placeholder">
</ph> 包含 BiquadFilterNode 的音频图表
包含 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 = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

一般而言,频率控制需要进行调整, 对数刻度,因为人类听力本身的工作原理是相同的原理 (即 A4 表示 440hz,A5 表示 880hz)。有关详情,请参阅 FilterSample.changeFrequency 函数。

最后,请注意示例代码让你可以 过滤器,动态更改 AudioContext 图表。我们可以断开连接 通过调用 node.disconnect(outputNumber) 从图中获取 AudioNode。 例如,要重新设置图表路线,使其不再通过过滤器, 直接连接,我们可以执行以下操作:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

深入收听

我们已经介绍了该 API 的基础知识,包括加载和播放音频 示例。我们构建了带有增益节点和滤波器的音频图表, 调整了预定声音和音频参数,以启用一些常见的声音 效果。现在,您可以开始构建 音频应用!

如果您正在寻求灵感,许多开发者已经创建 太棒了。我喜爱的一些 包括:

  • AudioJedit:一种在浏览器中使用 SoundCloud 固定链接。
  • ToneCraft:一种声音定序器,其声音由 将 3D 块堆叠在一起
  • Plink,一款使用网络音频和网页的协作式音乐制作游戏 套接字。