前言
先上波科大讯飞官方文档地址:https://www.xfyun.cn/doc/asr/voicedictation/API.html
需要提前准备好密钥信息APPID、APISecret、APIKey,怎么获取这三个配置我就省略了
这个案例使用的是webAPI
下载官方demo
这里我下载的是webAPI 语音听写流式API demo js语言,下载后得到以下文件
官方demo直接用原始html+js写的,我这里直接改成vue写法
需要把index.umd.js、processor.worker.js、processor.worklet.js放到项目/public/js/xf
安装依赖
npm i crypto-js
直接上代码吧,自行修改APPID、APISecret、APIKey就能直接用
完整代码
<template>
<div>
<button @click="SR">{{ btnTxt }}</button>
<br>
<!-- {{ resultTextTemp }} -->
<br>
{{ result }}
</div>
</template>
<script>
import CryptoJS from 'crypto-js'
// import RecorderManager from '@/../public/static/js/xf/index.umd.js'
export default {
data() {
return {
RecorderManager: require('@/../public/static/js/xf/index.umd.js'),
APPID: "自行修改",
API_SECRET: "自行修改",
API_KEY: "自行修改",
btnStatus:"UNDEFINED", // "UNDEFINED" "CONNECTING" "OPEN" "CLOSING" "CLOSED"
recorder: null,
iatWS: null,
resultText: '',
resultTextTemp: '',
result: '',
countdownInterval: null,
btnTxt: '开始录音',
};
},
mounted() {
this.recorder = new this.RecorderManager('./static/js/xf'); // processor.worker.js和processor.worklet.js的根目录(注意: 这里的路径是相对于index.html的,而不是相对于本vue)
this.recorder.onStart = () => {
this.changeBtnStatus("OPEN");
}
this.recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => {
if (this.iatWS.readyState === this.iatWS.OPEN) {
this.iatWS.send(
JSON.stringify({
data: {
status: isLastFrame ? 2 : 1,
// status: 1,
format: "audio/L16;rate=16000",
encoding: "raw",
audio: this.toBase64(frameBuffer),
},
})
);
if (isLastFrame) {
this.changeBtnStatus("CLOSING");
}
}
};
this.recorder.onStop = () => {
clearInterval(this.countdownInterval);
};
},
methods: {
// 录音按钮点击事件
SR() {
if (this.btnStatus === "UNDEFINED" || this.btnStatus === "CLOSED") {
this.connectWebSocket();
} else if (this.btnStatus === "CONNECTING" || this.btnStatus === "OPEN") {
// 结束录音
this.recorder.stop();
}
},
// 获取websocket地址
getWebSocketUrl() {
// 请求地址根据语种不同变化
let url = "wss://iat-api.xfyun.cn/v2/iat";
let host = "iat-api.xfyun.cn";
let apiKey = this.API_KEY;
let apiSecret = this.API_SECRET;
let date = new Date().toGMTString();
let algorithm = "hmac-sha256";
let headers = "host date request-line";
let signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`;
let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
let signature = CryptoJS.enc.Base64.stringify(signatureSha);
let authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
let authorization = btoa(authorizationOrigin);
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
return url;
},
// 连接服务器
connectWebSocket() {
const websocketUrl = this.getWebSocketUrl();
if ("WebSocket" in window) {
this.iatWS = new WebSocket(websocketUrl);
} else if ("MozWebSocket" in window) {
this.iatWS = new MozWebSocket(websocketUrl);
} else {
alert("浏览器不支持WebSocket");
return;
}
this.changeBtnStatus("CONNECTING");
this.iatWS.onopen = (e) => {
console.log(e);
// 开始录音
this.recorder.start({
sampleRate: 16000,
frameSize: 1280,
});
let params = {
common: {
app_id: this.APPID,
},
business: {
language: "zh_cn",
domain: "iat",
accent: "mandarin",
vad_eos: 5000,
dwa: "wpgs",
ptt: 0, // 标点符号
},
data: {
status: 0,
format: "audio/L16;rate=16000",
encoding: "raw",
},
};
this.iatWS.send(JSON.stringify(params));
};
this.iatWS.onmessage = (e) => {
this.renderResult(e.data);
};
this.iatWS.onerror = (e) => {
console.log(e);
this.recorder.stop();
this.changeBtnStatus("CLOSED");
};
this.iatWS.onclose = (e) => {
console.log(e);
this.recorder.stop();
this.changeBtnStatus("CLOSED");
};
},
toBase64(buffer) {
let binary = "";
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
},
changeBtnStatus(status) {
this.btnStatus = status;
if (status === 'CONNECTING') {
this.btnTxt = '建立连接中';
this.resultText = '';
this.resultTextTemp = '';
} else if (status === 'OPEN') {
this.countdown();
} else if (status === 'CLOSING') {
this.btnTxt = '关闭连接中';
} else if (status === 'CLOSED') {
this.btnTxt = '开始录音';
}
},
countdown() {
let seconds = 60;
this.btnTxt = `录音中(${seconds}s)`;
this.countdownInterval = setInterval(() => {
seconds = seconds - 1;
if (seconds <= 0) {
clearInterval(this.countdownInterval);
this.recorder.stop();
} else {
this.btnTxt = `录音中(${seconds}s)`;
}
}, 1000);
},
// 渲染结果
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"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果,替换范围为rg字段
if (data.pgs) {
if (data.pgs === "apd") {
// 将resultTextTemp同步给resultText
this.resultText = this.resultTextTemp;
}
// 将结果存储在resultTextTemp中
this.resultTextTemp = this.resultText + str;
} else {
this.resultText = this.resultText + str;
}
this.result = this.resultTextTemp || this.resultText || '';
}
if (jsonData.code === 0 && jsonData.data.status === 2) {
this.iatWS.close();
}
if (jsonData.code !== 0) {
this.iatWS.close();
console.error(jsonData);
}
}
}
};
</script>
