[Voice communications] 看得到的音频流
上文介绍了 Web Audio API 的相关知识,以及如何在你的 web 程序中引入 音频流,内容都是介绍性的,所以没有写太多 DEMO。本文重点讲解如何利用 Web Audio API 中的中间节点拿到音频信号的信息,并将信息转化成信号图绘制到 canvas 中。
从上文中我们了解到,AudioContext 是音频播放和处理的一个环境,大概的流程是这个样子:
+---------------+------------------------------------+
| AudioContext | |
+---------------+ |
| +-------+ +-------+ +-------+ |
| | | | | | | |
==========>| Source|===>|Lots of|===>| Dest | Output |
Multi Input| | | | | |===========>
==========>| Node |===>| Nodes |===>| Node | |
| | | | | | | |
| +-------+ +-------+ +-------+ |
| | |
| | Created By Barret Lee |
+-------------------------|--------------------------+
| +-------------+
+========>| Other Tools |
signal +-------------+
在 AudioContext 中,通过一个结点(AudioNode)来接受输入源,中间的一些结点可以过滤、放大、去杂等处理 Source Node 的信号,处理之后可以送到 AudioContext 的输出结点,然后启用 source.start()
播放音频信息;也可以将处理的信息送到外部交给其他对象来处理,比如本文要谈到的,将信号交给 Canvas 来处理,这样就能看到音频信号的波形图了。
本文地址:http://www.cnblogs.com/hustskyking/p/webAudio-show-audio.html,转载请注明源地址。
注:较新版 Google Chrome 和 Firefox 才能运行本文中的 DEMO。
一、节点的连接
先说一说,各个节点在 AudioContext 中的连接是如何用代码实现的:
// 创建一个 AudioContext 环境
var context = new AudioContext(); function playSound() {
// 创建一个 Source 节点
var source = context.createBufferSource();
// 拿到输入源(狗吠)
source.buffer = dogBarkingBuffer;
// 将 source 节点链接到 destination 节点(输出节点)
source.connect(context.destination);
// currentTime 设定为 0,开始播放
source.start(0);
}
上面的连接十分简单,直接将 source 节点连接到 destination 节点,相当于没有经过人任何处理,直接输出了,而下面的方式是创建一个中间节点,对信号做一些处理,不过在拿到 Source 的方式上跟上面有些不一样:
var audio = document.getElementsByTagName("audio")[0];
// context 为一个 AudioContext 环境
// 从 audio 元素中拿到输入源,也就是上图所看到的 Mutil Input
var source = context.createMediaElementSource(audio);
// 建立一个处理延时节点
var delayNode = context.createDelay();
// sourceNode -> delayNode -> destinationNode
source.connect(delayNode);
delayNode.connect(context.destination);
这里需要注意的是,destination 是 AudioContext 实例的固有属性,他就是信号的最终汇聚的位置,也是信号的输出位置。下面是一个简单的 DEMO 代码:
<audio src="http://qianduannotes.duapp.com/file/tankWar.mp3" id="origin"></audio>
<audio src="http://qianduannotes.duapp.com/file/tankWar.mp3" id="audio"></audio>
<input type="button" onclick="origin.play()" value="原始音质 播放" />
<input type="button" onclick="origin.pause()" value="原始音源 暂停" /><br/>
<input type="button" onclick="audio.play()" value="滤波音质 播放" />
<input type="button" onclick="audio.pause()" value="滤波音源 暂停" />
<script type="text/javascript">
var AudioContext = AudioContext || webkitAudioContext;
var context = new AudioContext();
var source = context.createMediaElementSource(audio);
// 低通滤波节点(高频信号被过滤,听到的声音会很沉闷)
var FilterNode = context.createBiquadFilter("lowpass");
// sourceNode -> FilterNode -> destinationNode
source.connect(FilterNode);
FilterNode.connect(context.destination);
</script>
上面的代码,AudioContext 获取 audio 源的原理是这样的:
- audio有一个内置的输出通道
- AudioContext 通过 createMediaElementSource 将 audio 的输出直接拉去到新的环境中,之前 audio 环境被破坏
- 拉去的 source 没有 start 函数,他会一直监听 audio 的操控,当 play 函数被触发的时候,开始播放音频。也可以认为,play 函数触发了 start (老版浏览器中是 noteOn)
下面是一个演示图:
+----------+------------------------------+
| audio | |
+----------+ |
| +--------+ // +-------------+ |
| | Source |=====//==>| Destination | |
| +--------+ | // +-------------+ |
| | |
+-----------------|-----------------------+
| Created By Barret Lee
+--------|-----+--------------------------+
| ↓ |
| +--------+ +-------+ +------+ |
| | Source |===>| Nodes |===>| Dest | |
| +--------+ +-------+ +------+ |
| +--------------+
| | AudioContext |
+--------------------------+--------------+
二、两个中间节点的介绍
1. ScriptProcessorNode
我们可以直接使用 JavaScript 操控这个节点,他的作用是产生、传递、分析一段音频。他有一个 bufferSize 属性和一个 onaudioprocess 事件。初始化一个 ScriptProcessorNode:
var processor=context.createScriptProcessor(4096, 1, 1);
他接收三个参数,第一个是 bufferSize 的大小,取值范围是 Math.pow(2, N) ( 8≤N≤14 )
,第二个参数是送入的 channel 数,第三个参数是输出的 channel 数。信息不会自动通过这个节点需要我们自己将输入的信号复制到输出位置去:
processor.onaudioprocess = function(e){
//获取输入和输出的数据缓冲区
var input = e.inputBuffer.getChannelData(0);
var output = e.outputBuffer.getChannelData(0); //将输入数缓冲复制到输出缓冲上
for(var i = 0, len = input.length; i < len; i++){
output[i] = input[i];
}
}
这样处理的原因是因为多个输入要对应对个输出,也有可能是多对一或者一对多,所以这些信息的设定必须要人为去控制。
关于 ScriptProcessorNode 的介绍,具体请移步http://www.w3.org/TR/webaudio/#ScriptProcessorNode-section
2. AnalyserNode
通过这个节点我们可以对信号进行频域和时域上的分析,学过 通信原理 的同学对这些属于应该是十分熟悉的。
interface AnalyserNode : AudioNode { // Real-time frequency-domain data
void getFloatFrequencyData(Float32Array array);
void getByteFrequencyData(Uint8Array array); // Real-time waveform data
void getByteTimeDomainData(Uint8Array array); attribute unsigned long fftSize;
readonly attribute unsigned long frequencyBinCount; attribute double minDecibels;
attribute double maxDecibels; attribute double smoothingTimeConstant; };
上面是这个节点的接口信息,不要感到奇怪,对接口的描述,都是使用这种方式,从上面我们可以看到,他有三个方法,四个属性。fftSize 是指频率分析下的快速傅里叶变换大小,他的值被限定在 32-2048 的 2 的整数次方。
关于 AnalyserNode 的介绍,具体请移步http://www.w3.org/TR/webaudio/#AnalyserNode-section
三、音频信息的提取
利用上面介绍的两个节点可以十分轻松的提取到音频信息,如使用 ScriptProcessorNode,在他的 onaudioprocess 触发的时候,可以拿到 input 信息,此时也就是音频信息流。
processor.onaudioprocess = function(e){
//获取输入和输出的数据缓冲区
var input = e.inputBuffer.getChannelData(0); doSomething(input);
};
上面这种方式拿到数据的效率是比较低的,一般可以直接使用 AnalyserNode 节点。这个节点中一个获取缓冲数据区的方法叫做 getByteTimeDomainData,这个方法的设计是十分偏底层的,或者对 JSer 来说,这个借口的设计并不合理,可以看看:
//以fftSize为长度创建一个字节数组作为数据缓冲区
var output = new Uint8Array(analyser.fftSize);
// 将获取得到的数据赋值给 output
analyser.getByteTimeDomainData(output);
这里是把 output 作为引用传进 getByteTimeDomainData 函数中,相信大家应该没有在 JS 中遇到过这样的写法吧~ (我觉得在该 web 标准定稿的时候,这里一定会做修改!)
四、信号图的绘制
上面我们已经拿到了信号数据了,绘制工作其实就是 canvas 的事情啦~
var width = canvas.width,
height = canvas.height,
g = canvas.getContext("2d"); // 将坐标原点移动到(0.5, height / 2 + 0.5)的位置
g.translate(0.5, height / 2 + 0.5);
然后绘制图形:
processor.onaudioprocess=function(e){
//获取输入和输出的数据缓冲区
var input = e.inputBuffer.getChannelData(0);
var output = e.outputBuffer.getChannelData(0);
//将输入数缓冲复制到输出缓冲上
for(var i=0; i<input.length; i++)
output[i]=input[i];
//将缓冲区的数据绘制到Canvas上
g.clearRect(-0.5, -height/2 - 0.5, width, height); g.beginPath();
for(i = 0; i < width; i++)
g.lineTo(i, height / 2 * output[output.length * i / width|0]);
g.stroke();
};
下面是整个 DEMO 的代码,效果预览:
代码:
<canvas id="canvas" width="400" height="100"></canvas>
<audio id="audio" autoplay src="http://qianduannotes.duapp.com/file/tankWar.mp3"></audio>
<br/>
<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<script>
var AudioContext=AudioContext||webkitAudioContext;
var context=new AudioContext;
//从元素创建媒体节点
var media=context.createMediaElementSource(audio);
//创建脚本处理节点
var processor=context.createScriptProcessor(4096,1,1);
//Canvas初始化
var width=canvas.width,height=canvas.height;
var g=canvas.getContext("2d");
g.translate(0.5,height/2+0.5);
//连接:媒体节点→控制节点→输出源
media.connect(processor);
processor.connect(context.destination);
//控制节点的过程处理
processor.onaudioprocess=function(e){
//获取输入和输出的数据缓冲区
var input=e.inputBuffer.getChannelData(0);
var output=e.outputBuffer.getChannelData(0);
//将输入数缓冲复制到输出缓冲上
for(var i=0;i<input.length;i++)output[i]=input[i];
//将缓冲区的数据绘制到Canvas上
g.clearRect(-0.5,-height/2-0.5,width,height);
g.beginPath();
for(var i=0;i<width;i++)
g.lineTo(i,height/2*output[output.length*i/width|0]);
g.stroke();
};
</script>
DEMO
该段代码引自次碳酸钴的博客。
五、小结
本文着重讲述了 AudioContext 内部节点之间的交互原理,以及如何使用节点获取数据,关于图形的绘制是 canvas 的操作,不是本系列文章的重点,所以一笔带过。
如果文中有叙述不正确的地方,还请斧正!
六、参考资料
- http://www.w3.org/TR/webaudio/ W3C Group
- http://www.web-tinker.com/ 次碳酸钴
[Voice communications] 看得到的音频流的更多相关文章
- [Voice communications] 音量的控制
改变音频的音量是音频处理中最基础的部分,我们可以利用 GainNode 来构建 Mixers 的结构块.GainNode 的接口是很简单的: interface GainNode : AudioNod ...
- [Voice communications] 声道的转换
本系列文章主要是介绍 Web Audio API 的相关知识,以及 web语音通信 中会遇到的一些问题,阐述可能存在错误,还请多多斧正! 很多粤语剧都提供了两个声道,一个左声道为粤语,一个右声道有国语 ...
- [Voice communications] 声音的滤波
本系列文章主要是介绍 Web Audio API 的相关知识,以及 web语音通信 中会遇到的一些问题,阐述可能存在错误,还请多多斧正! 通过设备获取音频流会不可避免的渗入一些杂音,这些杂音可能来自你 ...
- [Voice communications] 让音乐响起来
本系列文章主要是介绍 Web Audio API 的相关知识,由于该技术还处在 web 草案阶段(很多标准被提出来,至于取舍需要等待稳定版文档来确定,草案阶段的文档很多都会被再次编辑甚至重写.全部删除 ...
- (Step by Step)How to setup IP Phone Server(VoIP Server) for free.
You must have heard about IP Phone and SIP (Software IP Phone).Nowadays standard PSTN phone are bein ...
- week9:个人博客作业
团队作业(5) 以下内容多数是网上的内容,只是做了整合的过程. 要求 在PM 带领下, 每个团队深入分析下面行业的App, 找到行业的Top 5 (从下面的三个备选中,任选一个行业即可) 英语学习/词 ...
- 每日英语:5 Things to Know About Missing Malaysia Airlines Flight and Air Safety
Malaysia Airlines Flight MH370, with 239 people aboard, lost contact early Saturday with the airline ...
- 转载:P2P技术原理及应用(1)
转帖allen303allen的空间 作 者:金海 廖小飞 摘要:对等网络(P2P)有3种主要的组织结构:分布式哈希表(DHT)结构.树形结构.网状结构.P2P技术已 经延伸到几乎所有的网络应用领域, ...
- Method for address space layout randomization in execute-in-place code
The present application relates generally to laying out address space for execute-in-place code and, ...
随机推荐
- Power BI for Office 365 概览
伴随着数据量的日益增长,通过自服务式的商业智能把数据转换成辅助决策支持的工具,越来越成为迫切的需求.在以前,处理这些数据往往只是IT人员范围内的事,但是通过Power BI for Office 36 ...
- Vmware无法获取快照信息 锁定文件失败
今天早上起来发现虚拟机崩了: 造成原因: 如果使用VMWare虚拟机的时候突然系统崩溃蓝屏,有一定几率会导致无法启动, 会提示:锁定文件失败,打不开磁盘或快照所依赖的磁盘: 这是因为虚拟机在运行的时候 ...
- 使用手机展示axure
UC浏览器全屏,去地址栏 发布axure选项设置一下:参考http://sowm.cn/yixieshi/article/A752C3309457FBE32A99CA3A6A50B993.html 启 ...
- HDU5977 Garden of Eden(树的点分治)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5977 Description When God made the first man, he ...
- CompiledEffect Direct3D9 Sample fxc.exe
- C#对象序列化与反序列化zz
C#对象序列化与反序列化(转载自:http://www.cnblogs.com/LiZhiW/p/3622365.html) 1. 对象序列化的介绍........................ ...
- 记录同事的一个bug-ajax-413错误-fullhead
症状表现为在form下面的textarea里的字符数只有几十个的时候,请求可以成功,但是如果有几百字,则会出现413错误,提示fullhead,我第一反应是cookie体积太小,但是清了缓存还是一样的 ...
- Redis 主从配置
环境 Master/Slave 系统 IP Redis版本 Master CentOS6.7 10.10.3.211 redis-3.2.6 ...
- 做 Web 开发少不了这些的
抱歉,似乎有些标题党了.最近做服务器的热备,整理了些李纳斯工具的适用方法.看看还有不错的. 基本命令 sleep 500 暂停 ctrl + z 暂停 progress & 后台运行 jobs ...
- http://devdocs.io/【文档收藏】
http://devdocs.io http://bower.io/ www.bower.iobrowserify.org jsPlumb布局 https://github.com/lndb/jsPl ...