[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 的代码,效果预览:
代码:
![](/assets/blank.gif)
![](/assets/blank.gif)
<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 草案阶段(很多标准被提出来,至于取舍需要等待稳定版文档来确定,草案阶段的文档很多都会被再次编辑甚至重写.全部删除 ...
- 【Photon Voice】介绍
入门 Photon Voice 2是Photon Voice的后续版本,它带来了以下功能: 改进了API和更好的Unity组件. 与PUN2兼容. 灵活性:由于现在它与PUN2分离,它可以与Photo ...
- 录屏的知识片段的记录与理解
最近做一个关于录屏的功能 注: 前面一段是查看源码,证明无法录制内置音,和自己踩坑的过程. 下面有CV大法拿过来直接可以使用的录屏代码,心急的朋友可以直接略过 分割线以上内容 首先:目前没有发现可以录 ...
- 医疗保健数据接口_为什么需要用于医疗保健业务的应用程序
医疗保健数据接口 Whether you own a medical business or want to found a healthcare startup, if you're plannin ...
- Android 使用MediaRecorder实现录音
Android开发中,实现录音的方式主要有两种:MediaRecorder 和 AudioRecord. 两者区别在于MediaRecorder录制的文件是经过压缩的,需要设置编码器,录制的文件系统可 ...
- 基于java的连连看游戏系统设计与实现(项目报告+答辩PPT+源代码+数据库+截图+部署视频)
项目说明报告 基于Java的连连看游戏设计与实现 连连看是一种消除类益智游戏,核心要求是在规定的时间内,消除游戏界面中选中的两张相同的图案,直至完全消除所有图案.这款游戏操作简单(只需单击鼠标左键操作 ...
- [计算机毕设]基于java的连连看游戏系统设计与实现(项目报告+答辩PPT+源代码+数据库)
项目说明报告 基于Java的连连看游戏设计与实现 连连看是一种消除类益智游戏,核心要求是在规定的时间内,消除游戏界面中选中的两张相同的图案,直至完全消除所有图案.这款游戏操作简单(只需单击鼠标左键操作 ...
最新文章
- 抗住 8 亿人买买买!双 11 背后黑科技大曝光
- Qt中多线程间的互斥
- idea中提交mapper.xml到svn后代码变灰色
- 链接服务器---无效的产品名称
- 神奇的x -x,Lowbit函数的实现方式!
- C语言获取系统当前时间的两种方式
- CSS-float详解,深入理解clear:both[转+部分原创]
- MC-GAN:Multi-Content GAN for Few-Shot Font Style Transfer
- LIO-SAM探秘第三章之代码解析(三) --- mapOptmization.cpp (1)
- 预测模型——灰色模型
- 推荐6本React在线电子版书籍
- 汉语词典 mdd mdx 下载_三款你必须拥有的英文词典软件
- 你该把前端外包出来了
- 乐高太多没处放?解放女朋友双手,1 个顶 100 个的乐高智能分拣机来了!
- 美丽的余霞风景mac高清动态壁纸
- Elasticsearch:创建一个 Elasticsearch Ingest 插件
- c语言字符三维数组定义时赋值,c语言中三维数组的赋值顺序?
- 计算机网络 与信息安全专业就业,信息安全专业是学什么的 毕业后的就业方向有哪些...
- 新疆最新建筑八大员(资料员)模拟真题集及答案解析
- 物联网行业网络解决方案_提供RFID物联网解决方案,东识科技将亮相IOTE 2019苏州物联网展...
热门文章
- 烂泥:net use与shutdown配合使用,本机重启远程服务器
- 微信小程序把玩(二十八)image组件
- HTML5 Audio/Video 标签,属性,方法,事件汇总 (转)
- RotateAnimation详解
- CacheDependency缓存依赖里面的 absoluteExpiration(绝对到期时间),弹性到期时间(slidingExpiration)...
- Python 命名空间/名称查询 对效率的影响
- 奇虎360不正当竞争官司不断
- 【心情】为什么发英文版免责声明?
- 基于场景建模的自动化配置
- Html 5.2 的简单介绍及新增元素 dialog/dialog