Files
RGKT/rg-09112127/js/speechSensation.js
2025-10-10 19:44:14 +08:00

282 lines
8.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
let barrierFreeOpenSpeech;
(function () {
var APPID = "43341b7e";
var API_SECRET = "MWQzMWEyYTAyYzVlZWRjOTM1NjE0MjI4";
var API_KEY = "a46e1fc5d062a14b28cde0ff2c0046e1";
let btnStatus = "UNDEFINED"; // "UNDEFINED" "CONNECTING" "OPEN" "CLOSING" "CLOSED"
const btnControl = document.getElementById("speechStartButton");
const recorder = new RecorderManager("./dist");
recorder.onStart = () => {
changeBtnStatus("OPEN");
}
let iatWS;
let resultText = "";
let resultTextTemp = "";
let countdownInterval;
/**
* 获取websocket url
* 该接口需要后端提供,这里为了方便前端处理
*/
function getWebSocketUrl() {
// 请求地址根据语种不同变化
var url = "wss://iat-api.xfyun.cn/v2/iat";
var host = "iat-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/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 = 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;
btnControl.innerText = `录音中(${seconds}s`;
countdownInterval = setInterval(() => {
seconds = seconds - 1;
if (seconds <= 0) {
clearInterval(countdownInterval);
recorder.stop();
} else {
btnControl.innerText = `录音中(${seconds}s`;
}
}, 1000);
}
function changeBtnStatus(status) {
btnStatus = status;
if (status === "CONNECTING") {
btnControl.innerText = "建立连接中";
document.getElementById("speechPartialResult").innerText = "";
resultText = "";
resultTextTemp = "";
} else if (status === "OPEN") {
countdown();
} else if (status === "CLOSING") {
btnControl.innerText = "关闭连接中";
} else if (status === "CLOSED") {
btnControl.innerText = "开始录音";
}
}
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"时表示该片结果是追加到前面的最终结果;取值为"rpl" 时表示替换前面的部分结果替换范围为rg字段
if (data.pgs) {
if (data.pgs === "apd") {
// 将resultTextTemp同步给resultText
resultText = resultTextTemp;
}
// 将结果存储在resultTextTemp中
resultTextTemp = resultText + str;
} else {
resultText = resultText + str;
}
document.getElementById("speechPartialResult").innerText =
resultTextTemp || resultText || "";
// 调用方法传输文字
// console.log("sendSpeechText", resultTextTemp || resultText || "");
checkVoiceCommand(resultTextTemp || resultText || "");
// 网页间通信
htmlPostMessage(
{
type: "语音识别",
status: "process",
data: resultTextTemp || 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: APPID,
},
business: {
language: "zh_cn",
domain: "iat",
accent: "mandarin",
vad_eos: 5000,
dwa: "wpgs",
},
data: {
status: 0,
format: "audio/L16;rate=16000",
encoding: "raw",
},
};
iatWS.send(JSON.stringify(params));
// 网页间通信
htmlPostMessage(
{
type: "语音识别",
status: "start",
data: "",
}
);
};
iatWS.onmessage = (e) => {
renderResult(e.data);
// console.log(e.data);
};
iatWS.onerror = (e) => {
console.error(e);
recorder.stop();
changeBtnStatus("CLOSED");
};
iatWS.onclose = (e) => {
recorder.stop();
changeBtnStatus("CLOSED");
// 网页间通信
htmlPostMessage(
{
type: "语音识别",
status: "end",
data: "",
}
);
};
}
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);
};
btnControl.onclick = function () {
if (btnStatus === "UNDEFINED" || btnStatus === "CLOSED" || btnStatus === "CONNECTING") {
connectWebSocket();
} else {
// 结束录音
recorder.stop();
}
};
barrierFreeOpenSpeech = function () {
// console.log("barrierFreeOpenSpeech", btnStatus);
if (btnStatus === "UNDEFINED" || btnStatus === "CLOSED" || btnStatus === "OPEN") {
connectWebSocket();
} else {
// 结束录音
recorder.stop();
}
};
})();
function showSpeech() {
let speechDom = document.getElementsByClassName("box-speech")[0];
speechDom.style.display = speechDom.style.display == 'none' ? 'block' : 'none';
voiceBroadcast(`${speechDom.style.display == 'block' ? '开启' : '关闭'}语音识别`);
// 无障碍模式
if (window.location.href.indexOf("courseHomeBarrierFree.html") !== -1) {
barrierFreeOpenSpeech();
}
}
// 检查语音命令
function checkVoiceCommand(text) {
if (!text) return false;
// 语音命令配置
const voiceCommands = [
{ keywords: ['打开首页', '跳转首页', '回到首页'], action: () => { window.open('./indexHome.html', '_blank'); }, description: '打开首页' },
{ keywords: ['打开百度', '跳转百度', '搜索'], action: () => { window.open('https://www.baidu.com', '_blank'); }, description: '打开百度' },
];
// 将文本转换为小写,去除标点符号和空格,以便更好地匹配
const cleanText = text.toLowerCase().replace(/[.,,。?!、;:""'']/g, '').replace(/\s+/g, '');
console.log("checkVoiceCommand", cleanText);
// 检查是否匹配任何命令
for (const command of voiceCommands) {
for (const keyword of command.keywords) {
// 同样处理关键词,去除空格
const cleanKeyword = keyword.replace(/\s+/g, '');
if (cleanText.includes(cleanKeyword)) {
// 执行命令动作
command.action();
return true;
}
}
}
return false;
}