项目中需要利用百度语音接口在Web端实现语音识别功能,采用了这样的技术方案,但实现时遇到了很多问题,发现网上大部分文章都只是在详解官方提供的example示例,对实际开发没有提供什么有价值的建议,而recorder.js是无法直接适配百度AI的语音接口的,故本篇将开发中各个细节点记录与此,欢迎指点交流。
一. 技术栈选择需求:利用百度语音接口在Web端实现语音识别功能
技术栈:React+recorder-tool.js +recorder.js + Express + Baidu语音识别API
recorder.js项目地址:https://github.com/mattdiamond/Recorderjs
演示效果:
二. 前端开发细节 为recorder.js提供一个代理对象前端的主框架采用React,在基本结构和语法上并没有太多问题,为了使用recorder.js,我们封装了一个recorder-tool.js作为代理,其实现方法较为简单,就是将官方示例中example示例中的html文件的脚本部分封装成一个单例对象作为recorder.js的代理,然后暴露一组API供上层调用,大致的结构如下:
import Recorder from './recorder-src'; //Singleton var recorder; //start record function startRecord() { recorder && recorder.record(); } //stop record function stopRecord(button) { recorder && recorder.stop(); } //....其他一些方法 export default { init : init, start: startRecord, stop: stopRecord, exportData: exportData, sendRequest: sendRequest, clear: clearRecord, createDownloadLink : createDownloadLink } 解除exportWAV方法的回调地狱官方示例中输出wav编码格式的数据这个动作是通过webworker来完成的,也就是说二进制数据处理的开始和结束时间点是通过事件来触发的,recorder.exportWAV( )接收一个回调函数作为入参,在得到wav格式的数据后会执行传入的回调函数,如果要在react中实现,就需要写成:
//record-page.js ... //处理录音-事件监听 proce***ecord(){ RecorderTools.exportData(function(blob){ var wav = preProcessData(blob); //发送请求 axios.post({...}) .then(function(response){ handle(response); }) }); } ...你或许已经发现了这个【回调地狱】的现象,深度的嵌套会让逻辑变的复杂且代码高度耦合,想把一些方法从react中剥离出去非常困难,我们希望使用一些其他的方式来转换代码的控制权,而不是把一大堆后续的逻辑传进exportData( )方法。
方法一:使用HTML自定义事件
我们在一个存在的DOM元素上添加一个自定义事件recorder.export的监听器,并在传入recorder.exportWAV( )方法的回调函数中,手动初始化触发一个自定义事件(暂不考虑兼容性问题),并把recorder.js导出的数据挂在这个event对象上,然后在指定元素上派发这个事件:
//export data function exportData() { recorder && recorder.exportWAV(function (blob) { //init event var exportDone = document.createEvent('HTMLEvents'); exportDone.initEvent('recorder.export', true, true); //add payload exportDone.data = blob; //dispatch document.getElementById('panel').dispatchEvent(exportDone); }); }这样我们后续的处理逻辑就可以用常规的方式在React组件中继续编写后续的业务逻辑,这样就实现了基本的职责分离和代码分离。
方法二:监听WebWorker
recorder.js中使用DOM0级事件模型来与webworker通讯,为了不覆盖原功能,我们可以通过DOM2事件模型在recorder实例上绑定额外的监听器:
recorder.worker.addEventListener('message',function(event){ //event.data中就包含了转换后的WAV数据 processData(event.data); ... })这样我们就可以在自己的逻辑代码或二次封装的代码中实现对转码动作的监听。
方法三:Promise化
使用Promise来实现异步的调用,将音频处理的代码剥离出去,最终的调用方式为:
RecorderTools.exportData().then(data){ //继续在React组件文件中编写其他逻辑或调用方法 }参考代码如下:
//RecorderTools.js中的方法定义 function exportData(){ return new Promise(function(resolve, reject){ recorder && recorder.exportWAV(function(blob){ resolve(blob); }) }); }回调,事件监听,Promise都是javascript中重要的异步模式,根据个人喜好和实际场景选择使用即可。
如何提交Blob对象通过recorder.js的官方示例可以看到,如果不将录音输出为本地wav格式的文件,我们得到的是一个Blob对象,Blob对象需要使用form表单的方式进行提交,具体方法如下(使用axios发送http请求):
var formData = new FormData(); formData.set('recorder.wav',blob);//blob即为要发送的数据 axios({ url:'http://localhost:8927/transmit', method : 'POST', headers:{ 'Content-Type': 'multipart/form-data'//此处也可以赋值为false }, data:formData }); 三. Recorder.js的功能扩展百度AI语音识别接口接收的语音文件需要满足如下的要求:
pcm格式或wav格式文件的二进制数据经过base64转换后的编码
16000Hz采样率
16bit位深
单声道