Web Audio API的运用

这篇翻译不完整。请帮忙从英语翻译这篇文章

Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容。它允许你开发复杂的混音,音效,平移以及更多。在这篇文章里,我们将会通过几个例子来解释web Audio API的基本运用。

Web Audio API并不会取代<audio>音频元素,倒不如说它是<audio>的补充更好,就好比如<canvas>与<img>共存的关系。你使用来实现音频的方式取决于你的使用情况。如果你只是想控制一个简单的音轨的播放,<audio>或许是一个更好更快的选择。如果你想实现更多复杂的音频处理,以及播放,Web Audio API提供了更多的优势以及控制。

Web Audio API的一个强大之处在于,它没有任何严格的声音呼叫控制。比如说,在同一时间它没有呼叫32或64的声音的限制。如果你的处理器性能好的话,同一时间播放1000多的声音不卡顿也是有可能的。这充分显示真正的进步,要知道几年前中高频的声卡仅能处理小部分的负载。

例子

为了演示Web Audio API的应用,我们创造了许多例子,这些例子在之后会慢慢加上来。请自由的添加用例以及提出改善的建议吧!

首先,我们创作了 Voice-change-O-matic, 一个很有意思的变声器及声音可视化web应用程序,它允许你选择不同的效果以及可视化。毫无疑问它可以在音质效果提高,但它给更多的显示了web Audio API的不同功能的混合运用( 运行 the Voice-change-O-matic live).

A UI with a sound wave being shown, and options for choosing voice effects and visualizations.

基于对理解Web Audio的追求,我们创建了另一个例子 Violent Theremin, 一个简单的应用程序,允许你通过移动鼠标来改变它的音调音量。它也提供了一个迷幻的光秀(查看Violent Theremin 源代码).

A page full of rainbow colours, with two buttons labeled Clear screen and mute.

基础概念

注释: 很多的代码碎片来自于这个例子 Violent Theremin example.

Web Audio API包含在音频上下文的处理音频操作,以及已被设计允许模块化路由。基本音频操作可通过音频节点进行,这些节点连接在一起,组成一个音频的路由表。多个音源——带有不同类型的频道配置——甚至可以被一个上下文支持。这个模块设计提供了创造带有动态效果的复杂音频功能的灵活性。

音频节点通过输入与输出进行连接,形成一个链,从一个或多个源出发,通过一个或更多的节点,最终到输出终端(你也可以不提供输出终端,换句话说,如果只是想使一些音频数据可视化)。一个简单经典的web  Audio的工作流程如下:

1. 构建音频上下文AudioContext对象;

2. 在AudioContext对象内,构建音源,比如<audio>,oscillator,stream

3. 构建效果节点effectNode,比如混响,双二阶滤波器,声相,压限器

4. 选择最终的音频目的地,比如说你的系统扬声器

5. 连接源到效果,效果到输出终端

构建AudioContext对象

首先,你需要构建一个AudioContext实例,来创建一个音频图。最简单的方法就像这样:

var audioCtx = new AudioContext();

注释: 同样一个文档是可以存在多个audioContext对象的,但是比较浪费。

然而,提供一个版本前缀对于webkit/Blink浏览器是很重要的,对于Firefox(桌面版/手机版/OS版)是不需要的。如下:

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

注释:当创建一个新的conText对象时,如果你不提示window对象,Sarari会无效。

创建AudioSource

现在我们有了AudioContext,可以用这个来做很多事。第一件我们需要做的事是玩音乐。音频可以来自于多样的地方:

对于这些特殊的例子,我们将会为我们的源构建一个 oscillator来提供简单的音调,以及gain node来控制音频音量:

var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();

注释: 为了直接播放一个音乐文件,你通常通过XHR来加载文件,通过Buffer来解码,创建BufferSource. 看这个 例子来自于 Voice-change-O-matic.

注释: Scott Michaud 已经写了一个有用的库来加载和解码一个或多个音频实例, 被称为 AudioSampleLoader. 这个可以帮助简化XHR/buffering的处理操作。

连接输入输出

为了通过你的扬声器来实际输出音质,你需要将它们连接起来。这个被称为节点连接方法,节点来自于很多可获得的不同节点类型。你想要连接的节点都提供了这个方法。

你的设备的默认输出结构(通常是你的设备扬声器),通过AudioContext.destination来允许进入。为了连接oscillator,gain node以及输出端,如以下运用:

oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);

一个更复杂的例子,(比如 Voice-change-O-matic), 你可以链接很多你想要的节点在一起,例如:

source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);
biquadFilter.connect(convolver);
convolver.connect(gainNode);
gainNode.connect(audioCtx.destination);

这个将会创造一个如下音频节点图:

你也可以链接多个节点到一个节点,比如说你想要混合多个音频源在一起,就让它们都通过一个效果节点,比如gain node。

注释:Firefox32以上版本已有完整的firefox开发者工具包括 Web Audio Editor, 一个对测试web audio 表的bug非常有用的东西.

播放音乐及设置音调

现在audio节点图已经建立,我们可以设置属性值及调用音频节点的方法来调节想要的音效。在这个简单的例子,我们可以设置特殊的音调,以赫兹为单位,设置为特殊类型,以及指示音乐播放:

oscillator.type = 'sine'; // sine wave — other values are 'square', 'sawtooth', 'triangle' and 'custom'
oscillator.frequency.value = 2500; // value in hertz
oscillator.start();

在我们的 Violent Theremin例子,设定了一个最大gain以及frequency(频率)值:

var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

var maxFreq = 6000;
var maxVol = 1;

var initialFreq = 3000;
var initialVol = 0.5;

// set options for the oscillator

oscillator.type = 'sine'; // sine wave — other values are 'square', 'sawtooth', 'triangle' and 'custom'
oscillator.frequency.value = initialFreq; // value in hertz
oscillator.start();

gainNode.gain.value = initialVol;

然后我们设置了一个frequency的新的值,以及设置每个时间鼠标的移动,基于目前的鼠标坐标值作为frequency和gain的最大值百分比。

// Mouse pointer coordinates

var CurX;
var CurY;

// Get new mouse pointer coordinates when mouse is moved
// then set new gain and pitch values

document.onmousemove = updatePage;

function updatePage(e) {   
    CurX = (window.Event) ? e.pageX : event.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
    CurY = (window.Event) ? e.pageY : event.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
    
    oscillator.frequency.value = (CurX/WIDTH) * maxFreq;
    gainNode.gain.value = (CurY/HEIGHT) * maxVol;

    canvasDraw();
}

简单的canvas可视化

每次鼠标的移动,canvasDraw()方法会被调用,鼠标停留的位置会画出一个多圆圈组成的小簇,它的大小以及颜色会基于frequency/gain的值。

function random(number1,number2) {
  var randomNo = number1 + (Math.floor(Math.random() * (number2 - number1)) + 1);
  return randomNo;
}

var canvas = document.querySelector('.canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;

var canvasCtx = canvas.getContext('2d');

function canvasDraw() {
  rX = CurX;
  rY = CurY;
  rC = Math.floor((gainNode.gain.value/maxVol)*30);
 
  canvasCtx.globalAlpha = 0.2;
 
  for(i=1;i<=15;i=i+2) {
    canvasCtx.beginPath();
    canvasCtx.fillStyle = 'rgb(' + 100+(i*10) + ',' + Math.floor((gainNode.gain.value/maxVol)*255) + ',' + Math.floor((oscillator.frequency.value/maxFreq)*255) + ')';
    canvasCtx.arc(rX+random(0,50),rY+random(0,50),rC/2+i,(Math.PI/180)*0,(Math.PI/180)*360,false);
    canvasCtx.fill();
    canvasCtx.closePath();     
  }    
}

theremin的静音

当静音按钮点击,以下方法会被调用,disconnect方法,将切断gain node与destination节点的链接,有效阻止了节点图的链接,所以没有声音会被产生。再次点击效果相反。

var mute = document.querySelector('.mute');

mute.onclick = function() {
  if(mute.id == "") {
    gainNode.disconnect(audioCtx.destination);
    mute.id = "activated";
    mute.innerHTML = "Unmute";
  } else {
    gainNode.connect(audioCtx.destination);
    mute.id = "";    
    mute.innerHTML = "Mute";
  }
}

其他节点选择

这里有许多通过Web Audio API来构建的节点,一个好消息就是,总体来说,正如我们所见,他们用同一种方法工作:构建节点,连接到图表的另一个节点,然后处理节点属性以及方法来作用于你想要的音源。

我们并不希望通过所有可获得的效果等;你可以在Web_Audio_API不同的参考接口找到如何使用每一个的详述。我们现在来浏览下不同的设置。

Wave shaper 节点

利用 AudioContext.createWaveShaper 方法,你可以构建一个 wave shaper node:

var distortion = audioCtx.createWaveShaper();

这个对象一定会数学化的定义wave shape,一个被应用于基础声音波来创造扭曲的效果。这些波并不好被计算,最好的开始方法是搜索web算法。比如,我们可以从 Stack Overflow 找到:

function makeDistortionCurve(amount) {
  var k = typeof amount === 'number' ? amount : 50,
    n_samples = 44100,
    curve = new Float32Array(n_samples),
    deg = Math.PI / 180,
    i = 0,
    x;
  for ( ; i < n_samples; ++i ) {
    x = i * 2 / n_samples - 1;
    curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) );
  }
  return curve;
};

在 Voice-change-O-matic 的演示中, 我们连接到audio图表上的ditortion节点,当需要的时候可以运用:

source.connect(analyser);
analyser.connect(distortion);
distortion.connect(biquadFilter);

...

distortion.curve = makeDistortionCurve(400);

Biquad filter

biquad filter 拥有很多可选择的方法, 通过 AudioContext.createBiquadFilter 方法来构建:

var biquadFilter = audioCtx.createBiquadFilter();

在Voice-change-o-matic的演示中,运用的制定选项是“lowshelf”过滤器,它提供了低音的基本增幅方法:

biquadFilter.type = "lowshelf";
biquadFilter.frequency.value = 1000;
biquadFilter.gain.value = 25;

我们在这里详述了过滤器的类型,频率值,增幅值。在lowshelf过滤器情况,所有的指定频率拥有25分贝的增幅值。

 Web Audio API的其他

Web Audio API 可以做不仅仅音频可视化及专业化(如panning)的事情。我们将会在之后的文章涉及其他的更多内容。

文档标签和贡献者

 此页面的贡献者: Helloooooooooo, smilewalker
 最后编辑者: Helloooooooooo,