基于科大讯飞语音识别组件(Vue3)

        在之前的文章中讲了科大讯飞语音识别的基础使用,那么如果想将它作为组件放入我们的页面中该如何操作呢?

1、安装crypto-js

npm install crypto-js

        crypto-js是用JavaScript编写的加密库,支持多种加密算法,如AES、DES、RC4、SHA-1、SHA-256等,在科大讯飞提供的语音识别(转文字)接口中需要使用SHA-256加密算法传输数据来保障数据安全

2、下载科大讯飞提供的iat-js-demo

下载地址

下载之后文件结构是这样的

我们只需要提取dist中的index.esm即可,iat-js-demo文件夹可删除

3、导入RecorderManager方法

        index.esm中有一个RecorderManager方法,我们将他与crypto-js一起导入到我们的组件中(核心)

import RecorderManager from '../utils/index.esm'
import CryptoJS from 'crypto-js'

        由于文件结构的规范性的要求,创建utils文件夹,并且将index.esm.js与processor.worker.js两个文件放入utils文件夹(src/utils)

4、更改vite.config.js

        我们需要关闭ws代理以及确保worker文件可以被正确打包

devServer: {
    host: '0.0.0.0',
    port: 5173,
    proxy: {
      '/': {
        ws: false, // 这里把ws代理给关闭
        target: 'wss://iat-api.xfyun.cn/v2/iat',
        changeOrigin: true
      }
    }
  },
build: {
    rollupOptions: {
      output: {
        // 确保worker文件被正确打包
        assetFileNames: (assetInfo) => {
          if (assetInfo.name.endsWith('.worker.js')) {
            return assetInfo.name;
          }
          return `${assetInfo.name}.${assetInfo.ext}`;
        },
      },
    },
  },

5、初始化recorder

        由于Vue3无法使用require导入,所以用下面的方法进行初始化(new URL方法中第一个参数的路径为下载后文件的上级位置)

const recorder = new RecorderManager(new URL('@/utils', import.meta.url));

6、定义变量

let btnStatus = "UNDEFINED"; // "UNDEFINED" "CONNECTING" "OPEN" "CLOSING" "CLOSED"

const recorder = new RecorderManager(new URL('@/utils', import.meta.url));
recorder.onStart = () => {
  changeBtnStatus("OPEN");
}
let iatWS;
let resultText = "";
let resultTextTemp = "";
let countdownInterval;
let user_input_msg = ref("")

7、定义方法

/**
 * 获取websocket url
 * 该接口需要后端提供,这里为了方便前端处理
 */
function getWebSocketUrl() {
  // 请求地址根据语种不同变化
  var url = "wss://iat-api.xfyun.cn/v2/iat";
  var host = "iat-api.xfyun.cn";
  var apiKey = "自己的apikey";
  var apiSecret = "自己的apiSecret";
  var date = new Date().toGMTString();
  var algorithm = "hmac-sha256";
  var headers = "host date request-line";
  var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat 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 = encryption();
  var authorization = btoa(authorizationOrigin);
  url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
  return url;
}

function toBase64(buffer) {
  var binary = "";
  var bytes = new Uint8Array(buffer);
  var len = bytes.byteLength;
  for (var i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  return window.btoa(binary);
}

function countdown() {
  let seconds = 60;
  countdownInterval = setInterval(() => {
    seconds = seconds - 1;
    if (seconds <= 0) {
      clearInterval(countdownInterval);
      recorder.stop();
    }
  }, 1000);
}

function changeBtnStatus(status) {
  btnStatus = status;
  if (status === "CONNECTING") {
    resultText = "";
    resultTextTemp = "";
  } else if (status === "OPEN") {
    countdown();
  } else if (status === "CLOSING") {
  } else if (status === "CLOSED") {
  }
}

function renderResult(resultData) {
  // 识别结束
  let jsonData = JSON.parse(resultData);
  if (jsonData.data && jsonData.data.result) {
    let data = jsonData.data.result;
    let str = "";
    let ws = data.ws;
    for (let i = 0; i < ws.length; i++) {
      str = str + ws[i].cw[0].w;
    }
    // 开启wpgs会有此字段(前提:在控制台开通动态修正功能)
    // 取值为 "apd"时表示该片结果是追加到前面singleData的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段
    if (data.pgs) {
      if (data.pgs === "apd") {
        // 将resultTextTemp同步给resultText
        resultText = resultTextTemp;
      }
      // 将结果存储在resultTextTemp中
      resultTextTemp = resultText + str;
    } else {
      resultText = resultText + str;
    }
    user_input_msg.value = resultTextTemp || resultText || "";
    console.log(resultTextTemp);
    console.log(resultText);
    

  }
  if (jsonData.code === 0 && jsonData.data.status === 2) {
    iatWS.close();
  }
  if (jsonData.code !== 0) {
    iatWS.close();
    console.error(jsonData);
  }
}

function connectWebSocket() {
  const websocketUrl = getWebSocketUrl();
  if ("WebSocket" in window) {
    iatWS = new WebSocket(websocketUrl);
  } else if ("MozWebSocket" in window) {
    iatWS = new MozWebSocket(websocketUrl);
  } else {
    alert("浏览器不支持WebSocket");
    return;
  }
  changeBtnStatus("CONNECTING");
  iatWS.onopen = (e) => {
    // 开始录音
    recorder.start({
      sampleRate: 16000,
      frameSize: 1280,
    });
    var params = {
      common: {
        app_id: "1846696d",
      },
      business: {
        language: "zh_cn",
        domain: "iat",
        accent: "mandarin",
        vad_eos: 1000,
        dwa: "wpgs",
      },
      data: {
        status: 0,
        format: "audio/L16;rate=16000",
        encoding: "raw",
      },
    };
    iatWS.send(JSON.stringify(params));
  };
  iatWS.onmessage = (e) => {
    renderResult(e.data);
  };
  iatWS.onerror = (e) => {
    console.error(e);
    recorder.stop();
    changeBtnStatus("CLOSED");
  };
  iatWS.onclose = (e) => {
    recorder.stop();
    changeBtnStatus("CLOSED");
  };
}

recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => {
  if (iatWS.readyState === iatWS.OPEN) {
    iatWS.send(
      JSON.stringify({
        data: {
          status: isLastFrame ? 2 : 1,
          format: "audio/L16;rate=16000",
          encoding: "raw",
          audio: toBase64(frameBuffer),
        },
      })
    );
    if (isLastFrame) {
      changeBtnStatus("CLOSING");
    }
  }
};
recorder.onStop = () => {
  clearInterval(countdownInterval);
};

const start = () => {

  if (btnStatus === "UNDEFINED" || btnStatus === "CLOSED") {
    user_input_msg.value = ""

    connectWebSocket();
  } else if (btnStatus === "CONNECTING" || btnStatus === "OPEN") {
    // 结束录音
    recorder.stop();
  }


};

const audioEnd = () => {
  if (singleData.value.length == 0) {
    if (btnStatus === "UNDEFINED" || btnStatus === "CLOSED") {
      user_input_msg.value = ""

      connectWebSocket();
    } else if (btnStatus === "CONNECTING" || btnStatus === "OPEN") {
      // 结束录音
      recorder.stop();
    }
  }

}

注明:核心方法为start()方法,需要触发该方法以实现语音转换文字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值