使用uni-app开发原生APP之getRecorderManager安卓导出格式错误

1,218 阅读3分钟

项目:北极象沉浸式翻译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是有限制的 image.png

按照文档来说,Android并没有支持wav格式的录音,能生成wav是你因为传了wav,实际上内容是默认的amr

三、要怎么解决?

既然知道了问题所在,那解决方法也很明显了

  • 生成IOSAndroid都支持的格式文件给后端,即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写的,懒得搞了