MyVoix2.0.js 源码分析 WebSpeech与WebAudio篇
楔 子
随着移动互联网时代的开启,各种移动设备走进了我们的生活。无论是日常生活中人手一部的手机,还是夜跑者必备的各种智能腕带,亦或者是充满未来科技感的google glass云云,它们正渐渐改变着我们的生活习惯以及用户交互习惯。触摸屏取代了实体按键,Siri开始慢慢释放我们的双手,而leap motion之类的硬件更是让我们彻底不需要接触IT设备便能通过手势控制它们。在这样的大背景下,前端的交互将涉及越来越多元的交叉学科,我们正如十几年前人们经历Css的诞生一样,见证着一场带动整个行业乃至社会的变革。
放下IE6正如你放下table布局
如果现今你的工作还只是每天严谨地去兼容IE6,那么是时候起身遥望身边的风景了,HTML5已经远远不只是那些渐变和圆角了。这篇博文中,我会像大家介绍Web Speech和Web Audio两组API,这是MyVoix.js框架的核心组成部分,在早前好几个版本的chrome这就已经实现,你可以通过webkit前缀调用他们。
Web Speech
WebSpeech API是由Speech API Community Group发布,主要功能是将语音输入转换成文字,如果你能加上一个语义分析的服务器,那它就是一个Siri。你也可以简单地把它理解成一个可编程的input语音输入框(如下代码)。
<input type=”text” x-webkit-speech lang=”zh-CN” />
在Siri刚刚出世的那个年代,上面这行代码就是前端界让屌丝变高富帅的利器,老板才不知道你才写了一行代码就搞出一个语音输入框(偷偷在input上加过x-webkit-speech的同学们请点赞)。但是,仅仅一个输入框标签显然不能满足程序员们那熊熊燃烧对代码的控制欲,于是Web Speech应运而生。
再回到代码,要使用webSpeech API我们首先要创建一个window.webkitSpeechRecognition对象。
1 var _rec =new window.webkitSpeechRecognition(); 2 _rec.continuous=true; 3 _rec.interimResults=false; 4 _rec.lang='en-US'; 5 _rec.maxAlternatives=1;
为了大家看的清楚,这里我稍稍修改了MyVoix中的源码。可以看到新建SpeechRecognition实例后,有几个参数需要我们配置。
continuous:如果置为false,那么当实例start之后,若没有输入或者输入错误就会立刻返回。这里需要等到有意义的输入才返回,所以我们置为true。
interimResults:如果设置为true,那么在onresult的时候就会不断有分析过程中的单词返回。这里只需要最后的分析结果,所以置为false。
lang:这个是语言,大家应该都看的懂。
maxAlternatives:设置的是每个result最大的SpeechRecognitionAlternatives。
接下来我们调用实例的start方法就会开启SpeechdRecognition的监听。但是在此之前还需要指定实例的onresult事件,语音分析完成后的单词会传入这个方法。
1 _rec.onresult=function(eve){ 2 var len = eve.results.length, 3 i = eve.resultIndex, 4 j = 0, 5 listeners, 6 command; 7 for (i; i < len; i += 1) { 8 if (eve.results[i].isFinal) { 9 // get words 10 command = eve.results[i][0].transcript.replace(/^\s+|\s+$/g, '').toLowerCase(); 11 if(console.log){ 12 console.log(eve.results[i][0].transcript); 13 } 14 //your code here.... 15 } 16 } 17 }; 18 _rec.start();
MyVoix中对于单词事件的绑定有自己的架构,之后的博文有机会会详述。
Web Audio
搞定了语音识别,接下来我们就需要获得麦克风的输入信号,以便实现MyVoix中的绘制波形功能。如果你用javascript调用过摄像头,那你一定用过navigator.webkitGetUserMedia这个东西,Web Audio中获取麦克风的音源数据就需要用到它。先看一下MyVoix中的源码:
1 navigator.webkitGetUserMedia({audio:true},function(e){ 2 var context = new webkitAudioContext(), 3 javascriptNode = context.createScriptProcessor(2048, 1, 1), 4 audioInput = context.createMediaStreamSource(e), 5 analyser = context.createAnalyser(), 6 splitter = context.createChannelSplitter(); 7 analyser.smoothingTimeConstant = 0.3; 8 analyser.fftSize = 1024; 9 audioInput.connect(splitter); 10 splitter.connect(analyser,0,0); 11 analyser.connect(javascriptNode); 12 javascriptNode.connect (context.destination); 13 14 javascriptNode.onaudioprocess = function(e) { 15 var array = new Uint8Array(analyser.frequencyBinCount); 16 analyser.getByteFrequencyData(array); 17 var average = me.getAverageVolume(e.inputBuffer.getChannelData (0)); 18 if (average > 0) { 19 me.changeNoise(average); 20 me.changeFrequence(average); 21 } 22 } 23 },function(){});
初看之下,和WebGL有点类似,你需要链接一堆堆的东西。进一步分析代码:
navigator.webkitGetUserMedia({audio:true},function(e){ //success callback //... },function(){ //error callback //... };
第一步使用webkitGetUserMedia对象调用本地麦克风,主要代码在成功回调函数中实现。
var context = new webkitAudioContext(), audioInput = context.createMediaStreamSource(e);
之后我们需要建立一个webkitAudioContext实例,通过该实例可以创建许多有用的元件。这里通过createMediaStreamSource方法和getUserMedia成功回调函数中的参数可以创建一个输入源。 通过一层层的传递可以最终连接到context.destination这个输出位置。我们简单过一下MyVoix中用到的几个节点:
analyser:这个一看就是分析音源用的,具体一般用在声音可视化上。
splitter:此节点用以声道转换,在MyVoix中我们用它把音源变成了左右两个声道。
javascriptNode:这个节点我们用来进行javascript级别的监听处理。通过onaudioprocess函数,在每次声音完成采样的时候,调用我们绘制波形的函数,并最终通过它连接到输出端。
在MyVoix2.0中,大致就只用到了以上几个AudioContext创造的节点。通过类似的方法加入其他节点,Web Audio还可以实现声音在3D空间中定位播放等功能。
尾 声
本文介绍了在MyVoix中用到的两个核心的技术,通过这篇博文希望大家对语音技术在html端的实现有大致的了解。我们在园子里写作,不用像鲁迅先生一样战斗,却也盼着技术推进这个时代。