项目:北极象沉浸式翻译APP
一、需要用到录音功能
项目中有一个语音翻译的功能,即用户说话,语音转文字,文字再翻译成目标语言
首先查看文档,uniapp有提供现成的api: getrecordermanager ,文档很清晰,直接上手写
const recorderManager = uni.getRecorderManager();
export default {
data() {
return {
voicePath: ''
}
},
methods: {
startRecord() {
recorderManager.start({
format: 'wav',
sampleRate: 16000
})
},
stopRecord() {
recorderManager.stop()
},
async handleTranslate() {
try{
const res = await audio2Text(this.voicePath)
concole.log(res)
} catch (e) {
console.error(e)
}
}
},
created() {
recorderManager.onStop(res => {
this.voicePath = res.tempFilePath
this.handleTranslate()
});
}
}
因为后端需要wav的格式,所以在start的时候传的format参数固定写了wav,uniapp也正常生成了一个wav格式的音频文件,音频文件也很顺利地通过接口传给了后端
但是没一会后端就反馈上传的wav文件没有一个能播放的,还都识别不出来!啊?
二、为什么给到后端的WAV文件是有问题的?
后端说虽然我发给他的是wav的后缀,但是内容实际上是amr的
在我一顿百度后,发现了这个问题好像有人踩过
大家踩得都是大同小异,getRecorderManager生成的wav文件或多或少有点问题,具体什么问题呢,大家也没说
既然大家都不说,那我就自己看源码,看看这玩意是怎么实现的,直接翻github,找一下对应getRecorderManager的实现,发现用的是HTML5+的接口
// ...
recorder = plus.audio.getRecorder()
recorder.record(
{
format,
samplerate: sampleRate ? String(sampleRate) : undefined,
filename: TEMP_PATH + '/recorder/',
},
(res) =>
publishRecorderStateChange('stop', {
tempFilePath: res,
}),
(err) =>
publishRecorderStateChange('error', {
errMsg: err.message,
})
)
//...
知道用的是HTML5+,那就再看HTML5+的audio.getRecorder是怎么个回事
发现执行recorder.record传的第一个参数的format是有限制的
按照文档来说,Android并没有支持wav格式的录音,能生成wav是你因为传了wav,实际上内容是默认的amr
三、要怎么解决?
既然知道了问题所在,那解决方法也很明显了
- 生成
IOS和Android都支持的格式文件给后端,即amr,后端自己转成自己想要的格式 - 生成
amr格式文件后再转成wav格式文件给后端
本着我能处理就处理的原则,看看能不能把文件处理完再给后端。但是找了一圈后,发现想在uniapp里面完成音频格式的转换有点困难,因为这种转换的库都是Node的。要么就是花钱买uniapp的插件(搜索 ffmpeg,基本都要花钱)
本着能省就省的原则,我这边做了一下格式的兼容,按照 wav - amr - acc的顺序去判断返回,指定的格式转换的事情就交给后端同学去操作了
getSupportFormat() {
const defaultType = 'amr'
const supportFormats = plus.audio.getRecorder().supportedFormats
if (!supportFormats.length) {
return defaultType
}
const fallbackOrder = ['wav', defaultType, 'aac']
function fallback (fallbackOrder, supportFormats) {
for (let i = 0; i < fallbackOrder.length; i++) {
const target = fallbackOrder[i]
if (supportFormats.includes(target)) {
return target
}
}
return defaultType
}
return fallback(fallbackOrder, supportFormats)
}
四、我能写一个插件吗
Q:我看插件市场都有插件,说明这玩意是能写的,看下怎么写?
A: 用UTS写的,懒得搞了