内容简介:利用HTML5 Web Audio API给网页JS交互增加声音
这篇文章发布于 2017年06月10日,星期六,23:41,归类于js实例。 阅读 52 次, 今日 52 次
byzhangxinxu from http://www.zhangxinxu.com/wordpress/?p=6220
本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。
一、庞然的HTML5 Web Audio API
首先务必要弄清这一点,本文这里所说的HTML5 Web Audio API和HTML5 <audio>
元素完全不是一个东西,其体量也完全不是一个等级的,HTML5 Web Audio API接口的丰富程度和体量可以和HTML canvas API相提并论,其能实现的功能也非常令人瞠目。
HTML5 Web Audio API可以让我们无中生有创造声音,而且是各种音调的声音,换句话说,我们通过JavaScript就会创建一个完整的音乐出来,类似卡农将有固定的音乐,或是通过一些随机算法创建随机的音乐都都可以由前端开发工程师来完成,如果再配合一点人工智能的东西,我们说不定可以造出一个人工智能音乐生成工具。
当然其功能绝不仅限于这一点,我们还可以:
- 对简单或复杂的声音进行混合;
- 精确控制声音的密度和节奏;
- 内置淡入/淡出,颗粒噪点,音调控制等效果;
- 灵活的处理在音频流的声道,使它们成为拆分和合并;
- 处理从
<audio>
音频或<video>
视频的媒体元素的音频源; - 使用MediaStream的getUserMedia()方法事实处理现场输入的音频,例如变声;
- 立体音效,可以支持多种3D游戏和沉浸式环境;
- 利用卷积引擎,创建各类线性音效,例如小/大房间、大教堂、音乐厅、洞穴、隧道、走廊、森林、圆形剧场等。尤其适用于创建高质量的房间效果。
- 高效的实时的时域和频分析,配合CSS3或Canvas或webGL可以实现音乐可视化支持;
- 以及其他多种音频波形算法控制,几乎就是把一个音频编辑类软件搬到web上。
但是对大多数的Web开发人员,我们并不需要和音频打交道这么深,因为毕竟不是做音乐网站的,此时,那些很高级的HTML5 Web Audio API功能就离我们实际工作的价值有些远,如果贸然花大量时间去深入学习其实是一件投入产出比很低的事情。当然,我这种担心实际上是多余的,“贸然花大量时间去深入学习?”搞反了吧,其实真实的现状是开发人员对此鸟都不鸟,看都不看。一来因为毕竟不火嘛,二来资料少看不懂嘛,三来自己项目很少有声音打交道,学了也白学。
考虑到这种现状,如果本文内容洋洋洒洒讲各种api,估计很多人瞟两眼就直观把页面关掉了,所以接下来我们从一个最简单最小的而且非常实用的案例来慢慢解开HTML5 Web Audio API的面纱。
二、HTML5 Web Audio API给网页交互增加声音
先来看实例效果,您可以狠狠地点击这里: 鼠标hover按钮无中生有播放声音demo
当我们鼠标hover下图所示的按钮的时候,在非IE浏览器下,就会播放出类似电子琴琴键按下的声音,而且每次hover的时候,音调都会发生有规律的变化:
下面这句话是重点,demo页面中播放的 声音是硬件自动生成 的,不是调用现成的mp3音频播放出来的。
换种说法就是:无需任何音频文件,只需要几行JS代码,我们就能让网页发出各种各样的音调。
10年前,flash炫酷网页风兴起的时候,交互音效是非常普遍的,可以说是flash酷站必备元素。只是后来flash衰落了,而web对于多媒体资源的支持长久以来是比较弱的,于是Web网站全部都是安安静静的。在移动端,H5广告营销性质的网页都是音频用的比较多,但大多都是作为背景音乐使用的,交互时候其实用的并不多。我想,可能与大多数开发并不知道HTML5 Web Audio API可以直接弄出声音,知识的广度局限了技术的选型。
于是我就想到,HTML5 Web Audio API提及的人不多,使用并不火,并不一定是因为这个技术本身的局限或者使用场景不多,而是具有较大影响力的人,社区或企业并没有在这一块身先士卒进行推动。很多企业的官网、产品宣传页,广告营销性质的专题活动非常讲求炫酷,音视频层出不穷,很显然(参考flash时代),交互音效也其实是非常欢迎的,只是技术眼界的局限认为需要专门的mp3/wav音频调用成本高而不使用罢了。
HTML5 Web Audio API可以直接产生声音,并且可以随意控制音调,时长以及淡入淡出等效果,更强大更灵活。而这个功能点是HTML5 Web Audio API中最简单最基本也是最实用的,没有理由不给web开发带来交互音效热潮。
三、网页交互音效AudioContext,AudioParam等API介绍
下面就是声音出现的JS相关代码:
1. window.AudioContext = window.AudioContext || window.webkitAudioContext; 2. var audioCtx = new AudioContext(); 3. var oscillator = audioCtx.createOscillator(); 4. var gainNode = audioCtx.createGain(); 5. oscillator.connect(gainNode); 6. gainNode.connect(audioCtx.destination); 7. oscillator.type = 'sine'; 8. oscillator.frequency.value = 196.00; 9. gainNode.gain.setValueAtTime(0, audioCtx.currentTime); 10. gainNode.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.01); 11. oscillator.start(audioCtx.currentTime); 12. gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1); 13. oscillator.stop(audioCtx.currentTime + 1);
总共13行代码,虽然行数不多,但其中出现的API数量以及其中的含义还是有点厚度的。
我们一行一行来看。
1. window.AudioContext = window.AudioContext || window.webkitAudioContext
没啥好说的,向前兼容一下老的 webkit
浏览器。
2. var audioCtx = new AudioContext()
AudioContext
是HTML5 Web Audio最基础估计也是最常用的API了,其角色和地位可以和Canvas的
canvas.getContext
一拼。顾名思义,指定一个音频上下文,包含大量的属性和方法,而且这些方法名又多又长,根本就记不住,随便截个图感受一下
:
所以,必须要有文档: MDN-AudioContext
但,不要怕,对于简单的音调播放,我们只需要关心下面几个属性和方法就好了,上面那么多高级的东西都是留给需要深入和音频打交道的开发人员用的。
3. var oscillator = audioCtx.createOscillator();
createOscillator()
是 AudioContext
的一个方法,创建一个 OscillatorNode 。 OscillatorNode
表示一个周期性波形,比如一个正弦波(就是上下弯弯的那个波),大家应该都知道声音本质其实就是震动,是和波形紧密关联的,因此波形不一样,最终的声音也就不一样,例如有的波形是“哔哔哔…”,有的波形出来的是“嘟呜呜…”。波还受频率影响,最终表现为不一样的音调高低。因此, OscillatorNode
就有 frequency
和 type
下面2个即将登场的属性。
总之,这里的变量 oscillator
基本上来说创造了一个音调。
4. var gainNode = audioCtx.createGain()
createGain()
也是 AudioContext
的一个方法,创建一个 GainNode ,它可以控制音频的总音量。也就是说 createOscillator()
控制音调, createGain()
控制音量,互相配合,黄金搭档。
GainNode
只有一个简单的只读属性,名为 gain
,完整书写 GainNode.gain
,本身没什么,但是 GainNode.gain
的返回值可是个大家伙,是个 a-rate
类型的 AudioParam 。
AudioParam
总共有2个类型,一个是 a-rate
还有一个是 k-rate
,前者 AudioParam
为音频信号每个样品帧的当前声音参数值;后者的 AudioParam
使用整个块(即128个样本帧)相同的初始音频参数值。
AudioParam
包含诸多属性和方法,都是与音量控制相关的。
5. oscillator.connect(gainNode)
音调和音量关联。
6. gainNode.connect(audioCtx.destination)
这里的 audioCtx.destination
返回 AudioDestinationNode 对象, AudioDestinationNode
接口表示音频图形在特定情况下的最终输出地址 – 通常为扬声器。话句话说就是和音频设备关联。
这个关联为audio context中所有节点的最终节点,看下面的工作流图:
7. oscillator.type = ‘sine’
我们的声音波形指定为 'sine'
,也就是正弦波,还支持其他3种波形,为 'square'
方波, 'triangle'
三角波以及 'sawtooth'
锯齿波。形状如下图(图来自 css-tricks )
下面这个Codepen可以感受4中波形带来的哔哔啵啵的声音:
以上四种波形是内置的,我们还可以使用 setPeriodicWave()
方法自定义波形。
8. oscillator.frequency.value = 196.00
设置波形的频率,实际上,可以理解为设置声音的音调,也就是设置最后的声音是“多瑞米发嗦啦西”其中的一个。数值越小,声音越低沉,越大提琴;数值越大,声音越清脆,越小提琴。
demo页面中的音调完整范围如下:
[196.00, 220.00, 246.94, 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.46, 783.99, 880.00, 987.77, 1046.50]
每次鼠标hover我都会变换一个频率,这就是为什么每次经过按钮就会听到有规律的音调变化。
9. gainNode.gain.setValueAtTime(0, audioCtx.currentTime)
先来了解下 audioCtx.currentTime
,其值是以双精度浮点型数字返回硬件调用的秒数。只读,也就是说这个时间你是无法改变的,无论是暂停或者重置都不可以。当我们使用类似 new AudioContext()
创建 AudioContext
的时候,这个时间就可以从 0
开始走了,然后就像离弦的箭,一直不回头。
gainNode.gain
这个前面介绍过了,返回的是 AudioParam
,所以这里实际上执行的是 AudioParam.setValueAtTime()
方法,此方法支持两个参数,一个是音量(范围0~1),一个时间,这里 setValueAtTime(0, audioCtx.currentTime)
表示当下就把音量设为 0
,也就是没声音,因为快速鼠标hover时候,之前声音可能还没播放结束,需要从 0
开始。
10. gainNode.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.01)
linearRampToValueAtTime()
方法表示音量在某时间线性变化到某值,这里 linearRampToValueAtTime(1, audioCtx.currentTime + 0.01)
实际上表示的是在 0.01
秒的时间内,声音音量线性从 0
到 1
。
11. oscillator.start(audioCtx.currentTime)
开始出声啦……
12. gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1)
exponentialRampToValueAtTime()
方法表示音量在某时间指数变化到某值,这里的 exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1)
实际上表示的是在 1.00
秒的时间内,音量由之前的 1
以指数曲线的速度降到极低的 0.001
音量。
我们把10, 12行代码联系起来,可能就是这样一个曲线(自己手绘的,凑合看吧):
这样处理,声音消失就有“念念不忘,必有回响”的效果,就很有琴键按下去声音的感觉。
13. oscillator.stop(audioCtx.currentTime + 1)
1秒后,声音关闭。
最终,我们把源码和注释重新整合下就有:
// 创建音频上下文 var audioCtx = new AudioContext(); // 创建音调控制对象 var oscillator = audioCtx.createOscillator(); // 创建音量控制对象 var gainNode = audioCtx.createGain(); // 音调音量关联 oscillator.connect(gainNode); // 音量和设备关联 gainNode.connect(audioCtx.destination); // 音调类型指定为正弦波 oscillator.type = 'sine'; // 设置音调频率 oscillator.frequency.value = 196.00; // 先把当前音量设为0 gainNode.gain.setValueAtTime(0, audioCtx.currentTime); // 0.01秒时间内音量从刚刚的0变成1,线性变化 gainNode.gain.linearRampToValueAtTime(1, audioCtx.currentTime + 0.01); // 声音走起 oscillator.start(audioCtx.currentTime); // 1秒时间内音量从刚刚的1变成0.001,指数变化 gainNode.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1); // 1秒后停止声音 oscillator.stop(audioCtx.currentTime + 1);
这下就好理解多了!
四、结束语
本文展示的交互音效仅仅是HTML5 Web Audio API众多特性中的冰山一角,但已经足够让广告性质类的网页熠熠生辉,甚至传统web站点在一些特殊场合也是可以尝试使用的。
最后,介绍几个和web audio相关的JS库, Pizzicato.js 、 webaudiox.js 、 howler.js 、 WAD 、 Tone.js ,适合复杂音频处理场景。
HTML5 Web Audio API Chrome,Safari以及Firefox浏览器都是支持的,兼容性还是不错的。
参考文章:
感谢阅读!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址: http://www.zhangxinxu.com/wordpress/?p=6220
(本篇完)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 通过WinAPI播放PCM声音
- Firefox 66 将自带自动屏蔽声音功能
- 如何让机器像人一样听声音
- PulseAudio 13 发布,跨平台声音服务
- Chrome 66 禁止声音自动播放,开发怎么应对?
- 这个 GitHub 项目能克隆你的声音
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。