【摘要】 项目中需要利用百度语音接口在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供上层调用,大致的结构如下:

解除exportWAV方法的回调地狱

官方示例中输出wav编码格式的数据这个动作是通过webworker来完成的,也就是说二进制数据处理的开始和结束时间点是通过事件来触发的,recorder.exportWAV( )接收一个回调函数作为入参,在得到wav格式的数据后会执行传入的回调函数,如果要在react中实现,就需要写成:

你或许已经发现了这个【回调地狱】的现象,深度的嵌套会让逻辑变的复杂且代码高度耦合,想把一些方法从react中剥离出去非常困难,我们希望使用一些其他的方式来转换代码的控制权,而不是把一大堆后续的逻辑传进exportData( )方法。

  • 方法一:使用HTML自定义事件

我们在一个存在的DOM元素上添加一个自定义事件recorder.export的监听器,并在传入recorder.exportWAV( )方法的回调函数中,手动初始化触发一个自定义事件(暂不考虑兼容性问题),并把recorder.js导出的数据挂在这个event对象上,然后在指定元素上派发这个事件:

这样我们后续的处理逻辑就可以用常规的方式在React组件中继续编写后续的业务逻辑,这样就实现了基本的职责分离代码分离

  • 方法二:监听WebWorker

recorder.js中使用DOM0级事件模型来与webworker通讯,为了不覆盖原功能,我们可以通过DOM2事件模型在recorder实例上绑定额外的监听器:

这样我们就可以在自己的逻辑代码或二次封装的代码中实现对转码动作的监听。

  • 方法三:Promise化

使用Promise来实现异步的调用,将音频处理的代码剥离出去,最终的调用方式为:

参考代码如下:

三. Recorder.js的功能扩展

百度AI语音识别接口接收的语音文件需要满足如下的要求:

  • pcm格式或wav格式文件的二进制数据经过base64转换后的编码

  • 16000Hz采样率

  • 16bit位深

  • 单声道

要利用recorder.js实现上述需求,需要对源码进行一些功能扩展。编码转换可以在服务端进行,而recorder.js中的floatTo16BitPCM( )方法看名字应该是为了满足16bit位深这个条件的,那么我们只需要考虑单声道和16000采样率这两个条件了。

源码中Recorder构造函数是可以接受参数的,而这个参数会被合入实例的config属性,其中numChannles就是声道数,所以我们只需要在实例化是传入自定义的声道数目即可:

new Recorder({    numChannels:1//单声道})

再来看16000采样率这个条件,查看源码可以知道,源码中对于sampleRate的使用,一律使用了音频流数据源上下文的sampleRate,也就是对应着电脑声卡的采样率(48000Hz44100Hz),那如何得到16000Hz采样率的数据呢?比如一个48000Hz采样率的声卡采集的信号点,1秒采集了48000次,那么这48000个数据要变成16000个数据,最简单的办法就是每4个点取1个然后组成新的数据,也就是说实际上声音采集设备传过来的采样率是固定的,我们需要多少的采样率,只需要自己拿一个比例系数去换算一下,然后丢弃掉一部分数据点(当然也可以求平均值)就可以了,封装后的调用方式为:

new Recorder({    numChannels:1,    sampleRate:16000});

那么在源码中需要做一些功能的扩展,关键的部分在下面这段代码:

//recorder.js部分源码function exportWAV(type) {    var buffers = [];    for (var channel = 0; channel < numChannels; channel++) {buffers.push(mergeBuffers(recBuffers[channel], recLength));}    var interleaved = undefined;    if (numChannels === 2) {interleaved = interleave(buffers[0], buffers[1]);} else {interleaved = buffers[0];        //此处是重点,可以看到对于单声道的情况是没有进行处理的,那么仿照双声道的处理方式来添加采样函数,此处改为interleaved = extractSingleChannel(buffers[0]);}    var dataview = encodeWAV(interleaved);    var audioBlob = new Blob([dataview], { type: type });self.postMessage({ command: 'exportWAV', data: audioBlob });
}

extractSingleChannel( )的具体实现参考interleave( )方法

/**
*sampleStep是系统的context.sampleRate/自定义sampleRate后取整的结果,这个方法实现了对单声道的*采样数据处理。
*/function extractSingleChannel(input) {    //如果此处不按比例缩短,实际输出的文件会包含sampleStep倍长度的空录音var length = Math.ceil(input.length / sampleStep);    var result = new Float32Array(length);    var index = 0,inputIndex = 0;    while (index < length) {        //此处是处理关键,算法就是输入的数据点每隔sampleStep距离取一个点放入resultresult[index++] = input[inputIndex];inputIndex += sampleStep;}    return result;
}

这样处理后exportWAV( )方法输出的Blob对象中存放的数据就满足了百度语音的识别要求。

四. 服务端开发细节

在服务端我们使用Express框架来部署一个消息中转服务,这里涉及的知识点相对较少,可以使用百度AI的nodejs-sdk来实现,也可以自行封装,权限验证的方法几乎都是通用的,按照官方文档来做就可以了。

通过multipart/form-data方式提交的表单无法直接通过req.bodyreq.params进行处理,这里使用官方推荐的Multer中间件来处理,此处较为简单,直接附上笔者的参考代码:

此处有一点需要注意的是:在实例化Multer时,传参和不传参时得到的转换对象是不一样的,如果涉及到相关场景可以直接在控制台打印出来确保使用了正确的属性。

来源:华为云社区  作者:大史不说话

【Recorder.js+百度语音识别】全栈方案技术细节相关推荐

  1. 基于React-Native0.55.4的语音识别项目全栈方案

    [摘要] 移动端的API能力验证方案与PC端不一样!不一样!!不一样!!! 即使需要使用的API都存在,也不一定能用,这一点和PC端是有很大区别的,国内的手机系统虽然都是基于Android,但几乎都会 ...

  2. 数据采集标注、模型开发、部署落地,百度大脑全栈 AI 能力详解

    AI 时代已来,软硬件解决方案企业在智能化转型过程中,面临着技术投入成本高.研发周期长等挑战.如何借助业内成熟技术.快速低成本地实现 AI 应用,已成为企业自身与技术平台特别关注的问题. 从核心技术能 ...

  3. 解读大众地平线合作:中国市场纯电车型,单颗芯片全栈方案

    作者 | 德新 编辑 | 王博 传闻许久的大众-地平线的合资合作终于落地. 先划重点: 合作主体是CARIAD,CARIAD+地平线,加快中国市场的ADAS和自动驾驶系统开发: 24亿欧元,包含投资地 ...

  4. 两个读书笔记:springboot+vue.js分布式组件全栈开发训练营 + 大数据开发基础

    (springboot+vue.js分布式组件全栈开发训练营原文在notion中, 大数据开发在思维导图中, 这个博客只是保存, 无法阅读. ) what is different between s ...

  5. 高通再次「押宝」中科创达,挑战智能驾驶软硬件全栈方案

    随着英伟达.地平线在中国市场刮起智能驾驶计算平台的「市场替代战」,作为后来者的高通,也坐不住了.这家全球移动芯片巨头此前在中国市场仅拿到了长城的智能驾驶定点. 目前,英伟达在中国市场已经拿到了蔚来.理 ...

  6. 腾讯云IoT全栈方案助力智慧交通基建,详解四大重点与两个案例

    5月22日,腾讯举办了以"交通行业的数字化转型观"为主题的智慧交通分论坛.论坛上,腾讯在智慧交通领域的多家合作伙伴到场,分享了各自"创造数字化交通.数字化出行愿景&quo ...

  7. uniapp+egg.js+react实现全栈笔记App

    软件介绍 这是一个笔记App,主要使用的技术是uniapp+egg.js+react 软件预览图片: 首页: 编辑页面.png 编辑和登录页面 编辑页面2.png 项目地址: https://gith ...

  8. wafer php demo,GitHub - forging2012/wafer-php-server-demo: Wafer - 企业级微信小程序全栈方案...

    Wafer 服务端 Demo - PHP 本仓库是 Wafer 组成部分,提供 PHP 版本业务服务器示例.示例需要和微信小程序客户端示例配合一起使用. 运行示例 选择合适的方式部署 Wafer,并按 ...

  9. 打造无所不及的智能:徐直军发布华为AI战略及全栈全场景方案

    来源:C114通信网 摘要:选择正确的问题比寻找新奇的方案更重要. "这是一个伟大的时代,华为立志为推动人类进步和世界繁荣做出贡献.2017年底,我们提出了新的愿景和使命,'把数字世界带入每 ...

最新文章

  1. c如何正常中断一个运行的线程
  2. [LeetCode] Invert Binary Tree - 二叉树翻转系列问题
  3. lightroom手机版下载_vnc控制手机版下载,vnc控制手机版下载的使用教程
  4. MySQL数据库备份的10个教程
  5. mysql自动定时备份数据,mysql数据库自动定时备份脚本
  6. 带你入门SpringCloud服务发现 | Eurka搭建和使用
  7. Web前端笔记(5)
  8. 怎样安装php5_如何安装php5.3
  9. c语言作业大全,C语言练习题(答案)
  10. 什么样的水平才算是java高级工程师?
  11. 深度学习——安装Nvidia 驱动(亲测有效)
  12. 【阿里102句土话集锦】菜鸟必备
  13. VMware虚拟机安装Ubuntu系统步骤详解
  14. Mac和Windows哪个好 ?windows和mac对比介绍
  15. DPI和分辨率的转换
  16. 怎么用python编写记事本_利用Python开发实现简单的记事本
  17. 《鸟哥Linux私房菜》——第十一章、认识与学习bash
  18. 指示灯循环控制C语言代码,指示灯循环控制.doc
  19. MySQL搜索: WHERE 多条件
  20. 非参数贝叶斯模型概述

热门文章

  1. java中常见的编译错误的是_编译时JAVA最常见的错误有哪些
  2. java exception e抛异常_Java-------异常处理try{}catch(){Exception e}finally{}
  3. python怎样填充颜色_python中如何给图形填充颜色
  4. 4 linux编辑器
  5. OO前三次作业简单总结
  6. 2017282110261-高级软件工程第二次作业
  7. 根据条件控制参数控件是否显示(可用)
  8. Codeforces Round #198 (Div. 1) B,C 动态规划
  9. 如何在excel 单元格中增加换行
  10. JBoss-4.2.3.GA下发布EJB3项目遇到的问题之JSF篇