共计 3062 个字符,预计需要花费 8 分钟才能阅读完成。
一、需求背景
明确需求:微信小程序实现语音识别。
分析需求:目前我们科大讯飞平台目前已经支持语音识别相关业务,我们只要将小程序语音上传(对接)到科大讯飞语音处理后台,就能完成上述需求。
问题拆解:上述需求 => 将小程序语音按照目前接口要求的格式上传到讯飞语音接口。
二、分析和调研问题
小程序支持的录音格式(MP3,AAC),采样率(8k-48k)
2. 科大讯飞语音接口接收的参数
将语音转换为对应的文本和语义
3. 明确需求:
由于小程序支持 MP3 和 AAC 两种录音格式,但是 EPG 的语音接口支持分片上传,所以我们只能选择 mp3 格式
EPG 语音接口支持 四种编码格式(pcm、speex、speex、opus),采样率 8k 或 16k。
思路:(目前的采样率认为是可以 16k=> 16k 不用变)
a) Mp3 => pcm b) Mp3 => speex c) Mp3 => amr d) Mp3 => opus
经过一番调研 github 或者 npm 包(因为小程序没有 window 对象,搜索资料 node.js 不能用浏览器下的对象)
a) 有一个工具 ffmpeg(安装) 命令行转(不满足需求)b)【小程序社区】社区中有两种声音(1)官方能不能增加 pcm 录音格式(官方给出暂时不考虑)(2)MP3 => pcm 放在服务器端做(我们的后端推不动)(3)【很高兴】得知 github js-mp3 开源库可以将 MP3 的 arraybuffer => pcm 格式的 arraybuffer https://developers.weixin.qq….
验证 js-mp3 能不能将 MP3 转为 pcm 格式
a) 网上下载 MP3b) 在 node.js 中写对应的 democ) 可以将 MP3 转为 pcm(很高兴, 也没注意该 MP3 的采样率是多少)d) 微信小程序 demo, 将录音的 mp3,将录音的 MP3 文件由临时文件保存到本地文件(此时不知道保存到哪里了,手机的根目录 /tencent/MicroMsg/wxafiles/tem_XXXXXX,文件名变了,文件管理功能不强大)e) 将录音 MP3(16k)试着用 js-mp3 转码(很可惜失败了,绝望)
分析 js-mp3 转码不成功的原因
a) MP3 有几种格式
b) 下载 ultraEdit 对 MP3 文件头分析 音频数据帧
每个帧都有 4 字节帧头 + 2 字节 CRC 校验(存在是否由帧头决定)+ 尸体数据 (MAIN_DATA)
Ø 帧头
AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
c) js-mp3 支持的 MP3 帧数据的前几位是(1)49 68 1001001 011 01 00 0 保留 保留(2)49 511001001 010 10 00 1 Mpeg2 保留(3)FF FA11111111 111 11 01 0Mpeg1 layer3(4)FF FB11111111 111 11 01 1Mpeg1 layer3
总结:这个包只支持采样率为 32k 以上的音频转码
http://www.mp3-tech.org/progr…d) 微信 MP3(16k,8k)所属格式(几个转码不成功的)微信 wx.mp3Ff f3 68 041111 1111 1111 0011 0110 1000 0000 0100Mpeg2 Layer3 比特率 48 采样率 8000
Wx1.mp3【开头 16 进制】49 44 33 03【二级制】0100 1001 0100 0100 0011 0011 0000 0011Mpeg2.5 layer2 比特率 24 采样率 12000
FF F3 1111 1111 111 10 01 1Mpeg2 layer3
下载的 MP3Ff f3 40 c01111 1111 111 10 01 1 0100 0000 1100 0000MPEG 2 layer3 比特率 32 采样率 22050
【绝望】js-mp3 好像不行了,从新调研其他包
三、解决问题
调研从零开始,理清思路。
通过调研得知 wav = pcm + 44 字节(头信息),于是 MP3=>pcm 转为 MP3=>wav
Pcm 播放器播不了,wav 能播
audiobuffer-to-wav
a)【曙光】Mp3 文件 buffer => arraybuffer => audioBuffer => wav 的 ArrayBuffer => buffer(写入文件)b) Audiobuffer 这个对象是浏览器端的对象(绝望)c) audio-decode 这个包在 node 端也支持 https://github.com/audiojs/au…d) 开始测试普通的 MP3 文件转码 ok, 小程序录音的 MP3 文件又报错断点调试:这个包引入‘is-mp3’
经过分析得知:wx MP3 开头 Ff f3 支持的开头(1)FF FA/FB(2)49 44/33 怀疑:这难道不是真正的 MP3
手动添加
校验过去了,但是还是不支持这种采样率为(16k 或者 8k 的),转换时候报错了
【转换思路】先用 32k 以上的采样率(这样起码 MP3=> pcm 是成功的),然后再同一种格式下降低采样率(32k=> 16k)
在 node 端验证完毕(js-mp3 或者 audio-decode)
在小程序端实现时(audio-decode 报错,暂时没解决。Js-Mp3 没有报错)
得到 pcm 格式(32k 采样率)
let Mp3 = require(‘js-mp3’);
var mp3ArrayBuffer = …; // prepare your mp3 decoded array buffer here
var decoder = Mp3.newDecoder(mp3ArrayBuffer);
var pcmArrayBuffer = decoder.decode(); // now you got decoded PCM data
【新问题】pcm(采样率由 32k 转为 16k)
Pcm-utilpcm.convert(buffer, fromFormat, toFormat) Convert array buffer from one format to another.
注意: 采样率 32k 变为 16k SamplesPerFrame 和采样率是对应的 BitDepth 不设置的时候,默认是 16 保存 pcm(pcm 文件大小约为 MP3 文件大小的 10 倍)文件到本地,此时用 ffmpeg 将 pcm 转为 wav(1) 用采样率为 16k 转换时,时长为原 MP3 的两倍(严重失真)(2) 用采样率 32k 转换时,时长和原 mp3 一致(不失真)结论:上述 pcm 32k=> pcm16k 时没有成功(toFormat 有问题)。通过排查将 bitDepth 设为 原来的一半 8,结果 pcm 16k 文件约为原 mp3 大小的 5 倍,ffmpeg 16k 转为 wav 播放正常(无失真)
四、对接科大讯飞语音识别 Ai
测量转码耗时
录音时长(s)转码时间(s)10.920 7.1185.8 3.9721.48 0.918 提交转码后的 16k pcm, 可以实现语音识别五、分析和优化分析
实现了从 0 到 1 到过程(不用依赖后端,自己也能玩起来)
由于现在没有利用分片上传、分片转码,分片转意的过程。耗时相对较长
现在 js-mp3 转码只能识别 整个 MP3,而分片 不是一个 MP3 格式的数据,是纯数据切断(导致,这个方案不能分片解码、上传)
目前耗时 在两步 MP3(32k)=> pcm(32k)=> pcm(16k)
优化 1. 变两步为一步 mp3 (16k) => pcm (16k)2. 解决 audio-decode 引入错误,如果能将,mp3 的 arraybuffer 分片转为 audioBuffer(不依赖整个 mp3 头),完成 pcm(16k)直接转换
分片上传 10s 的时长,转码耗时只是最后一个分片的 2s 解码和上传时间
六、收获 完成了小程序社区没人敢去尝试的一条路 办法总比问题多 沉着冷静逐一击破 后面再优化