评论

微信小程序使用科大讯飞语音评测,保姆级教程!

小程序调用科大讯飞语音评测功能,保姆级教程!一次到位!

最近微信小程序项目中,需要添加语音评测功能,就选用了科大讯飞的语音评测流式版接口,但在使用过程中,遇到了很多问题,再网上搜资料,搜了好多,也没直接能用的,好在后来参考了许多资料后,终于调试成功了,接下来,跟大家分享一下我是怎么处理的。

1.第一步,准备所用到的工具,下载官方jsdemo,将 base64js 文件复制到自己的小程序项目中,用npm安装crypto-js xmldom这2个工具

然后,将工具导入到页面中

const CryptoJS = require('crypto-js')
const Base64 = require('../../tools/base64js').Base64;
var DOMParser = require('xmldom').DOMParser;


2.第二步,初始化用到的变量,定义用到的关键函数

const APPID = '替换成你自己的'
const API_SECRET = '替换成你自己的'
const API_KEY = '替换成你自己的'

let audioData = [] //存储音频流的数组
let socketTask = null //小程序的socketTask
let handlerInterval = null // 定时器,用来定时发送数据流


function getWebSocketUrl() {//生成socket使用的url
return new Promise((resolve, reject) => {
    var url = 'wss://ise-api.xfyun.cn/v2/open-ise'
    var host = 'ise-api.xfyun.cn'
    var apiKey = API_KEY
    var apiSecret = API_SECRET
    var date = new Date().toGMTString()
    var algorithm = 'hmac-sha256'
    var headers = 'host date request-line'
    var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/open-ise HTTP/1.1`
    var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
    var signature = CryptoJS.enc.Base64.stringify(signatureSha)
    var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
    var authorization =Base64.encode(authorizationOrigin)
    url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
    resolve(url)
  })
}

3.开始录音

//开始录音
  startVoiceRecord(){
    let that = this
    that.setData({recordState:'recording'})
    recorderManager.onStart(() => {
      console.log('recorder start')
    })
    recorderManager.onPause(() => {
      console.log('recorder pause')
    })
    recorderManager.onStop((res) => {
      console.log('recorder stop', res)
      const { tempFilePath } = res
      that.startUpRecord()//录音完成,准备调用讯飞接口
    })
    recorderManager.onFrameRecorded((res) => {
      const { frameBuffer } = res
      console.log('frameBuffer.byteLength', frameBuffer.byteLength)
      let u8Arr = new Uint8Array(frameBuffer)
      audioData.push(u8Arr) //将每一帧的数据取出,放到audioData中,准备使用
    })
    
    const options = {
      duration: 180000,
      sampleRate: 16000,
      numberOfChannels: 1,
      encodeBitRate: 44100,
      frameSize: 2,
      format: 'pcm',
    }
    recorderManager.start(options)
  },

4. 开始socket连接,准备上传数据并处理

startUpRecord(){
    let that = this
    getWebSocketUrl().then(( url)=>{
      let newURL = encodeURI(url)
      socketTask = wx.connectSocket({
        url: newURL,
      })
      socketTask.onOpen(()=>{
        console.log('打开了socket')
        that.webSocketSend()
      })
      socketTask.onMessage((e)=>{
        // result 在这里做信息处理
        console.log('收到了结果:',e)
        that.result(e.data)
      })
      socketTask.onError((err)=>{
        //结束录音
        console.log('socket 出错:',err)
      })
      socketTask.onClose(()=>{
        // 结束录音
        console.log('socket 关闭:')
      })
    })
  },

webSocketSend() {
    console.log('开始发送数据',audioData)
    let that = this
    let audioDataUp = audioData.splice(0, 1)
    var params = {
      common: {
        app_id:APPID,
      },
      business: {
        category: 'read_sentence', // read_syllable/单字朗读,汉语专有 read_word/词语朗读  read_sentence/句子朗读 https://www.xfyun.cn/doc/Ise/IseAPI.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
        rstcd: 'utf8',
        group: 'pupil',
        sub: 'ise',
        ent: 'cn_vip',
        tte: 'utf-8',
        cmd: 'ssb',
        auf: 'audio/L16;rate=16000',
        aus: 1,
        aue: 'raw',
        text: '\uFEFF' +  '今天天气怎么样?'
      },
      data: {
        status: 0,
        encoding: 'raw',
        data_type: 1,
        data: that.toBase64(audioDataUp[0]),
      },
    }
    console.log(JSON.stringify(params))
    socketTask.send({data: JSON.stringify(params)})
    handlerInterval = setInterval(() => {
      // websocket未连接
      if (!socketTask) {
        clearInterval(handlerInterval)
        return
      }
      // 最后一帧
      if (audioData.length === 0) {
          console.log('数据发送完毕')
          socketTask.send(
            {data:
            JSON.stringify({
              business: {
                cmd: 'auw',
                aus: 4,
                aue: 'raw'
              },
              data: {
                status: 2,
                encoding: 'raw',
                data_type: 1,
                data: '',
              },
            })}
          )
          audioData = []
          clearInterval(handlerInterval)
        
        return false
      }
      audioDataUp = audioData.splice(0, 1)
      // 中间帧
      console.log('audioDataUp:',audioDataUp[0])
      socketTask.send(
        {
          data:
        JSON.stringify({
          business: {
            cmd: 'auw',
            aus: 2,
            aue: 'raw'
          },
          data: {
            status: 1,
            encoding: 'raw',
            data_type: 1,
            data: that.toBase64(audioDataUp[0]),
          },
        })}
      )
    }, 40)
  },
  result(resultData) {
    // 识别结束
    let jsonData = JSON.parse(resultData)
    if (jsonData.data && jsonData.data.data) {
      let data = Base64.decode(jsonData.data.data)
      const doc=new DOMParser().parseFromString(data,'text/xml');
      let sentence = doc.getElementsByTagName('read_sentence')[1]
      
      let accuracy_score = sentence.getAttribute('accuracy_score')
      let emotion_score = sentence.getAttribute('emotion_score')
      let fluency_score = sentence.getAttribute('fluency_score')
      let total_score = sentence.getAttribute('total_score')
      let integrity_score = sentence.getAttribute('integrity_score')
      let phone_score = sentence.getAttribute('phone_score')
      let tone_score = sentence.getAttribute('tone_score')
      let is_rejected = sentence.getAttribute('is_rejected')
      
      console.log('parseRes:',accuracy_score,emotion_score,fluency_score,total_score)
     //评测结果在这里,就出来了,然后就可以拿评测数据去使用了

    }
    if (jsonData.code === 0 && jsonData.data.status === 2) {
      // 在这里结束socket
      socketTask.close()
    }
    if (jsonData.code !== 0) {
      socketTask.close()
      console.log(`${jsonData.code}:${jsonData.message}`)
    }
  },


到这里,整个流程就完了,祝愿大家都能一次调用成功,有什么问题的话,咱们再讨论!

最后一次编辑于  2023-06-13  
点赞 6
收藏
评论

20 个评论

  • 微笑的你
    微笑的你
    2023-09-25

    你好,问下返回结果中的xml中文部分是乱码,你们有没有遇到这种情况啊?

    2023-09-25
    赞同
    回复 1
    • Mr.G
      Mr.G
      03-25
      乱码是不是因为你没解码啊
      03-25
      回复
  • xiaofengxyz
    xiaofengxyz
    2023-09-11

    你好,访问科大讯飞的地址不会报错:“不在以下 socket 合法域名”吗?不需要配置安全域名?

    2023-09-11
    赞同
    回复 1
  • Steven
    Steven
    2023-07-27

    您好,为什么参数business和data的结构与官网webapi文档中完全不一样,关键是我试了一下还能跑通,这些参数有什么文档介绍一下每个参数的作用吗?另外,business参数中的text: '\uFEFF' +  '今天天气怎么样?'起什么作用?

    2023-07-27
    赞同
    回复 1
    • Mr.G
      Mr.G
      2023-08-09
      应该是不同场景,参数要求有些许差异,或者说api更新了,这个功能做好之后就没看过api了,一般都是兼容老api的,所以能跑通,text里的文本,是语音评测的文本内容,英语,汉语识别,参数略微有差异,可以根据官方文档调整
      2023-08-09
      回复
  • 🌵
    🌵
    2023-06-01

    这里只有开始录音,需要写一个结束录音吗?点击之后就一直再录音没有结束啊

    2023-06-01
    赞同
    回复 1
    • Mr.G
      Mr.G
      2023-06-13
      开始录音方法里,有给录音管理器设置回调
      recorderManager.onStop((res) => {
            console.log('recorder stop', res)
            const { tempFilePath } = res
            that.readyForXF()//录音完成,准备调用讯飞接口
          })
      2023-06-13
      回复
  • 空中飞人
    空中飞人
    2023-04-05
    我发现在真机上,new Date().toGMTString()这个参数建立socket连接的时候会报错。好像是URL格式不对
    
    2023-04-05
    赞同
    回复 1
    • Mr.G
      Mr.G
      2023-04-07
      let newURL = encodeURI(url) 你是不是没编码
      2023-04-07
      回复
  • 韩文博
    韩文博
    2023-02-17

    请问这个必须在真机环境下测试吗?我用了上面的代码在模拟器里试 能正常发送 但测评结果都是0


    2023-02-17
    赞同
    回复 1
    • Mr.G
      Mr.G
      2023-02-20
      是要连真机调试的,电脑上录的格式好像跟真机是不一样的,所以不出结果
      2023-02-20
      回复
  • 哈儿
    哈儿
    2022-11-08

    我的项目也需要做语音评测,但是科大讯飞的这个评测结果分值太不准了。最近转战用智聆口语评测了,不知道后续评分会不会更准确一些

    2022-11-08
    赞同
    回复 4
    • Why not?
      Why not?
      2022-12-06
      智聆准吗?
      2022-12-06
      回复
    • Mr.G
      Mr.G
      2023-01-06
      腾讯的这个准吗,用着咋样啊,我做的时候,还没这东西呢,好用的话,回头我也换过来试试
      2023-01-06
      回复
    • 哈儿
      哈儿
      2023-03-20回复Why not?
      准确度到底以什么为基准,我也不清楚,只知道系统返回多少分就是多少分,但是智聆的确实比科大讯飞的流畅
      2023-03-20
      回复
    • 哈儿
      哈儿
      2023-03-20回复Mr.G
      智聆的确实用着比科大讯飞的流畅
      2023-03-20
      回复
  • dreamhunter
    dreamhunter
    2022-06-14
    const APPID = '替换成你自己的'
    const API_SECRET = '替换成你自己的'
    const API_KEY = '替换成你自己的'
    这些参数直接放前端真的好么?
    
    2022-06-14
    赞同
    回复 1
    • Mr.G
      Mr.G
      2022-06-14
      嗯……怎么不好呢。
      只要你小程序源码自己保存好,这些东西问题不大。
      后台调用这个也可以,具体看你们后台愿不愿意做了。我们后台嫌麻烦,我就都做了。
      要是怕泄露,可以把这些敏感信息,放接口里,请求后台服务获取
      2022-06-14
      回复
  • 姚
    2022-06-08

    您好,想问一下您在使用录音功能时,有无偶现安卓手机的音频听起来快进或者iphone手机录音文件无法播放的问题?

    2022-06-08
    赞同
    回复 6
    • Mr.G
      Mr.G
      2022-06-09
      你录的啥格式啊,宝
      2022-06-09
      回复
    • 姚
      2022-06-10回复Mr.G
      start传参如下:
      sampleRate: 16000,
      numberOfChannels: 1
      我们没有指定格式,看日志在iPhone和安卓手机上输出的文件都是m4a格式,是不是默认的m4a格式兼容性不好?需要制定其他格式呀?
      2022-06-10
      回复
    • Mr.G
      Mr.G
      2022-06-13回复
      你格式指定mp3试试吧,我一直用mp3,挺稳定的
      2022-06-13
      回复
    • 姚
      2022-06-21回复Mr.G
      谢谢大佬! 参照你文中的参数,解决了那两个偶现问题~~万分感谢
      2022-06-21
      回复
    • 姚
      2022-06-21回复
      但是呢,我们又发现另一个问题: 在iPhone手机上,录音时多次暂停(比如暂停10次)再继续,录制结束后拿到的音频文件会丢失大部分内容。不论我怎么调整参数,这个问题都存在~~ 反馈给小程序技术人员了,但还未收到回复~
      2022-06-21
      回复
    查看更多(1)
  • Frank
    Frank
    2022-05-26

    支持一下本土企业

    2022-05-26
    赞同
    回复

正在加载...

登录 后发表内容