Web 音频 API 入门教程

发布于 2023-05-13 11:11:17 字数 12205 浏览 140 评论 0

在HTML5之前 <audio> 元素,Flash或其他插件需要打破网络的沉默。虽然网络上的音频不再需要插件,但音频标签给实现复杂的游戏和交互式应用程序带来了重大限制。

Web Audio API 是一个高级 JavaScript API,用于在 Web 应用程序中处理和合成音频。此 API 的目标是包括新式游戏音频引擎中的功能,以及新式桌面音频制作应用程序中的一些混合、处理和筛选任务。以下是使用这个强大 API 的温和介绍。

音频上下文入门

音频上下文 用于管理和播放所有声音。要使用 Web 音频 API 生成声音,请创建一个或多个声源,并将它们连接到 AudioContext 实例。此连接不需要是直接的,并且可以通过任意数量的 中间音频节点,这些中间节点 充当音频信号的处理模块。Web 此路由 中更详细地描述了 音频规范

的单个实例 AudioContext 可以支持多个声音输入和复杂的音频图形,因此我们创建的每个音频应用程序只需要其中一个。许多有趣的 Web 音频 API 函数(例如创建音频节点和解码音频文件数据)都是 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 等 others。浏览器对不同音频格式的支持 各不相同

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

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 上的 Web Audio “无法工作”时遇到困难 - 为了避免这样的问题,只需在早期的 UI 事件中播放声音(甚至可以通过连接到增益为零的增益节点来静音) - 例如“触摸此处播放”。

抽象化 Web 音频 API

当然,最好创建一个更通用的加载系统,该系统不是硬编码来加载此特定声音的。有许多方法可以处理音频应用程序或游戏将使用的许多中短长度声音 - 下面是使用 BufferLoader 类 的一种方法。

以下是如何使用 BufferLoader .class。让我们创建两个 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 音频 API 使开发人员能够精确地安排播放。为了演示这一点,让我们设置一个简单的节奏轨道。可能最广为人知的鼓组模式如下:

简单的摇滚鼓模式

其中每八个音符演奏一次hihat,每季度交替演奏踢腿和军鼓,时间为4/4时间。

假设我们已经加载了 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.start(time);
}

完整源代码

更改声音的音量

您可能想要对声音执行的最基本操作之一是更改其音量。使用 Web 音频 API,我们可以通过 GainNode 将源路由到其目的地 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 的应用程序中的常见情况,我们有两个转盘并希望能够从一个声源平移到另一个声源。

这可以通过以下音频图来完成:

通过增益节点连接两个源的音频图

为了设置这一点,我们只需创建两个 GainNodes ,并通过节点连接每个源,使用类似于下面的函数:

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 音频 API,我们可以使用 AudioParam 接口来调度参数的未来值,例如 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 音频 API 提供了一组方便的 RampToValue 逐步更改参数值的方法,例如 linearRampToValueAtTimeexponentialRampToValueAtTime.

虽然可以从内置的线性和指数函数(如上所述)中选择转换时序函数,但您也可以使用 setValueCurveAtTime 功能。

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

带有 BiquadFilterNode

Web 音频 API 允许您将声音从一个音频节点传送到另一个音频节点,从而创建一个可能复杂的处理器链,以向声音形式添加复杂的效果。

一种方法是将 BiquadFilterNodes 放置在声源和目的地之间。这种类型的音频节点可以做各种低阶滤波器,可用于构建图形均衡器甚至更复杂的效果器,主要是选择声音频谱的哪些部分要强调,哪些部分要抑制。

支持的筛选器类型包括:

  • 低通滤波器
  • 高通滤波器
  • 带通滤波器
  • 低架过滤器
  • 高架过滤器
  • 峰值滤波器
  • 陷波滤波器
  • 全通过滤器

所有滤波器都包含参数,用于指定一定数量的 增益 、应用滤波器的频率和品质因数。低通滤波器保留较低的频率范围,但丢弃高频。分断点由频率值决定 ,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 图。我们可以通过调用来断开音频节点与图形的连接 node.disconnect(outputNumber).例如,要将图形从通过过滤器重新路由到直接连接,我们可以执行以下操作:

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

进一步聆听

我们已经介绍了 API 的基础知识,包括加载和播放音频样本。我们构建了带有增益节点和滤波器的音频图,以及计划的声音和音频参数调整,以实现一些常见的声音效果。此时,您已准备好构建一些甜蜜的网络音频应用程序!

如果您正在寻找灵感,许多开发人员已经 great work 使用 Web 音频 API 创作了出色的作品。我最喜欢的一些包括:

  • AudioJedit 一个使用 SoundCloud 永久链接的浏览器内声音拼接工具。
  • ToneCraft 一种声音序列器,通过堆叠 3D 块来创建声音。
  • Plink 一款使用 Web Audio 和 Web Sockets 的协作音乐制作游戏。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

悲喜皆因你

暂无简介

0 文章
0 评论
837 人气
更多

推荐作者

马化腾

文章 0 评论 0

thousandcents

文章 0 评论 0

辰『辰』

文章 0 评论 0

ailin001

文章 0 评论 0

冷情妓

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文