Recorder.js+百度语音识别全栈方案技术细节

2020 年 3 月 30 日

Recorder.js+百度语音识别全栈方案技术细节

一. 技术栈选择


需求:利用百度语音接口在 Web 端实现语音识别功能


技术栈:


React+recorder-tool.js+recorder.js+Express+Baidu 语音识别 API recorder.js


演示效果:



二. 前端开发细节


为 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,也就是对应着电脑声卡的采样率(48000Hz 或 44100Hz),那如何得到 16000Hz 采样率的数据呢?比如一个 48000Hz 采样率的声卡采集的信号点,1 秒采集了 48000 次,那么这 48000 个数据要变成 16000 个数据,最简单的办法就是每 4 个点取 1 个然后组成新的数据,也就是说实际上声音采集设备传过来的采样率是固定的,我们需要多少的采样率,只需要自己拿一个比例系数去换算一下,然后丢弃掉一部分数据点(当然也可以求平均值)就可以了,封装后的调用方式为:


new Recorder({    numChannels:1,    sampleRate:16000});
复制代码


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



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



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


四. 服务端开发细节


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


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



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


本文转载自 华为云产品与解决方案 公众号。


原文链接:https://mp.weixin.qq.com/s/uyaZpia9yhlZUTOIMgYOqg


2020 年 3 月 30 日 15:03172

评论

发布
暂无评论
发现更多内容

Mobileye携手吉利汽车共同推出领先的驾驶辅助功能

intel001

二.图说Eureka源码(环境搭建)

阿亮

源码 SpringCloud Eureka

智能体:华为给时代炼一炉钢

脑极体

Week09

SuperLab

有的程序员写代码写到头秃,有的程序员却通过黑吃黑获利百万。

Java架构师迁哥

阿里P9架构师推荐的Spring领域巅峰之作,颠覆了我对Spring的认知

Java成神之路

Java spring 编程 程序员 面试

大作业

SuperLab

无代码平台,完成业务的最后一公里

钟杰

「架构师训练营第 1 期」第二周作业

睡不着摇一摇

极客大学架构师训练营

12周作业-大数据

飞雪

一个草根的日常杂碎(9月23日)

刘新吾

随笔杂谈 生活记录 社会百态

Week08总结

SuperLab

7周作业-性能测试与优化

飞雪

Week10总结

SuperLab

架构师训练营笔记2则分布式系统架构和数据结构

tuuezzy

牛皮了!头一次见有大佬把「Java高并发编程」详解得如此清晰明了

Java成神之路

Java 程序员 面试 并发编程

架构师训练营第二周作业

Geek_4c1353

这份阿里P8整理的Java学习资源,简直把所有Java知识操作都写出来了

Java成神之路

Java 编程 程序员 面试

英特尔重磅发布物联网增强处理器,产品性能、AI能力、功能安全提升显著

intel001

Week12

SuperLab

Week13

SuperLab

朋友入职阿里请我吃饭,只因为面试前我逼他看了这些,经验很重要

小Q

Java 学习 程序员 架构 面试

学完这篇Spring Cloud技术提升一个点!

Java架构师迁哥

实践案例丨ACL2020 KBQA 基于查询图生成回答多跳复杂问题

华为云开发者社区

搜索 数据集 知识图谱

JAVA集合之LinkedList底层实现和原理

彭阿三

linkedlist

Week11

SuperLab

英特尔扩大技术及用户端部署,加速推动智能边缘发展

intel001

「架构师训练营第 1 期」第二周课后总结

睡不着摇一摇

极客大学架构师训练营

GaussDB(for MySQL)如何在存储架构设计上做到高可靠、高可用

华为云开发者社区

数据库 GaussDB

大作业

任小龙

智谱 AI 首席科学家唐杰团队荣获国际数据挖掘顶会时间检验应用科学奖

极客播报

Recorder.js+百度语音识别全栈方案技术细节-InfoQ