资讯专栏INFORMATION COLUMN

努力翻译一篇中文最友好的,Web Audio API的使用相关的文章

caikeal / 1670人阅读

摘要:前言本文翻译自上的利用,这是中的的一个入门教程。原文是英文,但有日本同志翻译的日文版。这是为了提供一个基本的低音増幅效果在这个例子中可以设定过滤器的种类,周波数,甚至的值。如果是过滤器的话,可以提供一个比指定周波数低的低音増幅。

前言

本文翻译自MDN上的《Web Audio APIの利用》,这是HTML5中的Web Audio API的一个入门教程。原文是英文,但有日本同志翻译的日文版。我更熟悉日文,所以主要根据日文版翻译成简体中文,也会对照英文版的。

Web Audio API的使用

Web Audio API提供给你一个简单却强大的机制,你可以利用它去实现与操作web应用内的音频内容。通过它,在网页中你也可以进行混音、音效、平移等各种复杂的音频处理功能的开发。这篇文章主要想通过两个简单的例子来说明Web Audio API的基础使用方法。

Web Audio API并不是替代

Web Audio API的一个强大之处是它没有严格的[发出声音的数目的限制]。例如,一秒内可以同时发出32个声音或64个声音,但这并不是上限。CPU的处理能力足够的话,1000多种声音也可以不经过压缩直接播放。如果这样发展下去的话,几年之后的中高端声卡的负荷会大大降低的吧。

PS:补充点背景知识,这里提到的32,64指的是复音数。复音数指MIDI乐曲在一秒钟内发出的最大声音数目。关于复音数和MIDI乐曲可以百度一下。声卡硬件会限制复音数,部分软件使用CPU实现声音播放的也会限制复音数。但理论上只要CPU处理能力够强复音数是不受限的。所以上段提及Web Audio Api是软件,但它没有复音数限制,所以说到只要CPU处理能力够强,未来或许能够降低对中高端声卡性能的需求

例子

为了展示Web Audio API的用法,我们编写了一些例子,这些例子会不断地增加更新。请大家发扬开源
精神为项目添加用例或者提出更好的改善意见!

PS:这些例子最好在最新版chrome中运行

首先介绍一下 Voice-change-O-matic 。这是一个有变声及声音可视化功能的网页应用,且有一些变声效果与可视化效果可供选择。虽然这个应用还有许多可以改善的地方,但也不失为综合使用Web Audio API的许多功能的一个好例子。(可以在这里运行 Voice-change-O-matic)

PS:上面的这个例子我运行没效果啊,不知道怎么玩
*PS:感谢vino24补充的,该例子需要https,在chrome下可运行

为了理解Web Audio而编写的另一个例子就是这个Violent Theremin。这是一个简单的应用,它允许你通过鼠标指针的移动来改变频率和音量。另外,鼠标移动过程还有迷幻光彩的视觉效果。(Violent Theremin的源码在这里)

基本思路

备注:下面列举的大多数的代码片段都在Violent Theremin中有使用。

Web Audio API的架构设计使得它能够轻松实现模块路由,其中包括对上下文中的音频内容的操作。基本的音频编辑可以使用audio node进行。但这些节点相互可以连接起来,可以构成一个节点图。多个音源或者不同种类的频道最终都可以对应到一个上下文中。这样模块化的设计是为了提供足够灵活的特性以便开发可以动态改变效果的复杂的音频编辑功能。

audio node有入口和出口,多个节点构成类似链表一样的结构。从一个或者多个音源出发,经过一个或者多个处理节点,最终输出到输出节点(输出终端,一般是扬声器)。(如果有需求的话,也可以不指定输出节点。例如,想把音频数据用图表的形式展现的场合等。)web audio的一个简单的典型的流程类是下面这样子:

创建AudioContext对象

在AudioContext对象内设置音源,例如

创建effect node(效果节点)。例如reverb, biquad filter, panner, compressor(这些都是音频特效)

选择音频的最终输出节点。例如,你的电脑的扬声器

音频经过效果节点处理后,然后输出到下一个节点,这些节点连接起来

创建AudioContext对象

首先,为了构建audio节点图,我们首先要创建创建AudioContext对象。最简单的方法就像这样:

var audioCtx = new AudioContext();

备注:虽然可以在一个document中创建多个AudioContext对象,但这恐怕没什么卵用。

但是,Webkit/Blink内核的浏览器不需要前缀,Firefox(desktop/mobile/OS)的浏览器可能需要前缀,所以为了兼容性考虑,最好写成这样:

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

通过创建的AudioContext对象的方法,我们可以进行各种各样的操作。最初进行的就是,准备要播放的音源。下面列出的东西都可以作为音源:

使用震动发声器与JavaScript创建音源的情况:使用AudioContext.createOscillator这个方法创建OscillatorNode。之后就可以利用震动发声器做音源了。

使用原始的PCM数据的情况:如果是可以识别的特定的格式的话(mp3之类的),使用AudioContext的特定的decode方法,来获得PCM数据。详细情况可以看这些,AudioContext.createBuffer()、AudioContext.createBufferSource()、AudioContext.decodeAudioData() 。

使用

从WebRTC MediaStream(WebRTC媒体流)输入音频源的情况:可以使用麦克风或者Web摄像头。具体情况看这个,AudioContext.createMediaStreamSource()

简单地把震动发声器作为音源,使用gain节点控制音量,这就构成我我们接下来要说的例子:

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

备注:如果要播放音乐文件(mp3之类的),一般要利用XHR载入文件数据,那之后创建BufferSource。在这个示例Voice-change-O-matic有代码。

备注:Scott Michaud封装了载入和解码音频的代码,开源了一个库,叫AudioSampleLoader。使用这个的话,XHR以及buffer的操作都会变得异常简单。

输入与输出的连接

要想音源(输入)通过扬声器(输出)播放出来,就必须要把两者连接起来。将被连接的节点作为参数,调用原来节点的的connect方法就可以建立节点间的连接。大多数类型的节点对象都拥有这个方法。

关于标准的输出节点可以参考,AudioContext.destination。标准的输出节点通常是设备的扬声器。把oscillator连接到gainNode上,gainNode的输出连接到标准输出上,代码如下:

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);

上面的代码创建出的audio图如下:

多个节点可以同时连接同一个节点。也可以让多个音源通过一个效果节点,达到混音的效果。

备注:Firefox 32 以上版本,在Firefox开发工具中Web Audio编辑器了。有了它之后大大提高了对audio节点图进行debug的效率。

播放与音调的设定

创建完audio节点图后,就可以通过设定audio节点的属性值或者调用方法来调整节点的效果。下面的例子,我们来设定下震动发声器的音调,使用Hz这个单位,如下:

oscillator.type = 0; // sine wave,正弦波
oscillator.frequency.value = 2500; // value in hertz
oscillator.start();

在Violent Theremin这个程序中,设定了音量与周波数的最大值,如下:

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 = 0; // sine wave
oscillator.frequency.value = initialFreq; // value in hertz
oscillator.start();

gainNode.gain.value = initialVol;

加下来是随着鼠标的移动,按照设定改变周波数。鼠标指针的X坐标和Y坐标以及周波数和音量的最大值,这些决定了最终输出声音的周波数和音量。代码如下:

// Mouse pointer coordinates

var CurX;
var CurY;

// Get new mouse pointer coordinates when mouse is moved
// then set new gain and putch 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()是一个每次鼠标移动都会调用的方法。每次调用这个方法,都会在鼠标指针位置所在的地方,把输出音频的周波数和音量用不同大小和不同颜色表现(画)出来。

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();     
  }    
}
静音

按下静音按钮后,会执行下面的函数。通过把Gain节点与前面连接的节点切断,使得声音的输出消失。再按一次按钮,就会恢复节点之间的连接,使得声音恢复输出。

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这个方法,可以创建WaveShaper节点。

var distortion = audioCtx.createWaveShaper();

要想让这个对象工作,必须给予与决定波形的函数。通过将这个函数应用于输入的波形,WaveShaper节点可以扭曲声音。对新手来说,一开始写出这个函数是很困难的吧。通过在网络上搜索,选用合适的方案就好了。下面是一个发布在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-Metic这个例子中,创建了一个叫做distortion的WaveShaper节点,提供了必要的音频扭曲效果。代码如下:

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

...

distortion.curve = makeDistortionCurve(400);
双二阶过滤器

双二阶过滤器的内部有多个配置项目。可以通过AudioContext.createBiquadFilter这个方法创建。

var biquadFilter = audioCtx.createBiquadFilter();

在Voice-Change-o-Metic这个例子中,使用的是典型的lowshelf 过滤器。这是为了提供一个基本的低音増幅效果:

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

在这个例子中可以设定过滤器的种类,周波数,甚至gain的值。如果是lowshelf过滤器的话,可以提供一个比指定周波数低25dB的低音増幅。

关于其他的Web Audio API的内容

使用Web Audio API的话,可以做到音频的可视化与立体化(例如音频平移等)。关于这些,我们在其他的文档中说明。

后记

终于完了。

翻译技术文章远比想象的要困难,特别是遇到很多陌生的某个领域下的专有词汇。

日语真是神奇,很多硬是用片假名套英文的情况,呵呵呵,让我想起了,特律风。

文中不免有错的地方,希望大家能指出来,帮助文章更好,谢谢。

感谢,@说说说说

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79643.html

相关文章

  • 绝对冷知识,浏览器环境下JavaScript能够利用到提交/请求数据方式有这么多!你都深入了解了

    摘要:你知道在浏览器环境下能够利用到的提交请求数据的方式有哪些吗这些方式各自有什么特点呢在什么情况下使用呢让我们一起来整理一下。其实这也是能够利用到的提交请求数据的方式之一。 你知道在浏览器环境下JavaScript能够利用到的提交/请求数据的方式有哪些吗?这些方式各自有什么特点呢?在什么情况下使用呢?让我们一起来整理一下。 基础知识 建议大家先看完这几篇文章,了解一下基础知识 HTTP访...

    bingchen 评论0 收藏0
  • 各种API+教程+练习

    摘要:做一个搬运工,希望自己能努力学习,也希望大神们的东西能让更多的人看到不断更新更新日志新增了网络安全分类,整理了排版布局新增了的链接,将一些杂七杂八的东西弄到了一篇新文章上了,叫做积累与杂货铺一以及相关教程的规范与相关中文学习大本营中文文档简 做一个搬运工,希望自己能努力学习,也希望大神们的东西能让更多的人看到 不断更新 更新日志:2017.10.13 新增了网络安全分类,整理了排版布局...

    saucxs 评论0 收藏0
  • 各种API+教程+练习

    摘要:做一个搬运工,希望自己能努力学习,也希望大神们的东西能让更多的人看到不断更新更新日志新增了网络安全分类,整理了排版布局新增了的链接,将一些杂七杂八的东西弄到了一篇新文章上了,叫做积累与杂货铺一以及相关教程的规范与相关中文学习大本营中文文档简 做一个搬运工,希望自己能努力学习,也希望大神们的东西能让更多的人看到 不断更新 更新日志:2017.10.13 新增了网络安全分类,整理了排版布局...

    20171112 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<