/** * Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights * Reserved. MIT License (https://opensource.org/licenses/MIT) */ /* 2021-2023 by zhaoming,mali aihealthx.com */ let isfilemode = false; let configwords = ""; let wssBaseUrl={}; //------------------- WebSocket 配置初始化 start------------------ async function init() { // 在这里请求接口 wssBaseUrl = await getBaseUrlFromApi(); console.log(wssBaseUrl, "获取baseUrl"); } // 示例:请求接口获取 baseUrl async function getBaseUrlFromApi() { try { const response = await fetch( `getUserConfig`, { method: "GET", } ); if (!response.ok) { throw new Error("请求失败"); } const data = await response.json(); configwords = data return configwords; } catch (error) { console.error("获取 configwords 出错:", error); } } // 初始化程序入口 init(); console.log("进入ws"); //--------------------WebSocket 配置初始化 end------------------ //------------------- FunASR WebSocket 连接器 start------------------ function WebSocketConnectMethod(config) { //定义socket连接方法类 var speechSokt; var connKeeperID; var msgHandle = config.msgHandle; var stateHandle = config.stateHandle; let modeType = "offline"; // 发送队列和流量控制 var sendQueue = []; var isSending = false; var maxQueueSize = 1000; // 最大队列长度,防止内存溢出 var queueWarningThreshold = 500; // 队列警告阈值 var lastDisconnWarnTime = 0; // WebSocket未连接警告节流 function trimQueueIfNeeded() { if (sendQueue.length <= maxQueueSize) { return false; } const dropCount = sendQueue.length - maxQueueSize; console.warn( `⚠️ 发送队列超过最大限制 (${maxQueueSize}),丢弃最旧的 ${dropCount} 个数据包` ); sendQueue = sendQueue.slice(-maxQueueSize); return true; } function enqueueData(data) { sendQueue.push(data); trimQueueIfNeeded(); if (sendQueue.length > queueWarningThreshold) { console.warn(`⚠️ WebSocket发送队列堆积: ${sendQueue.length} 个数据包待发送`); } } this.wsStart = function (e) { if (speechSokt) { speechSokt.close(); } //------------------- FunASR 连接模式选择 start------------------ // app.js 中 rec 和 rec2 可能连接同一个 /websocket_offline 地址。 // online/offline 的区别不靠 URL,而靠 onOpen() 握手 JSON 中的 mode 字段: // wsStart() 不传参数 => mode:"online"; wsStart(url) 传参 => mode:"offline"。 var Uri = e ? e : `wss://${wssBaseUrl.FUNASR_WEBSOCKET_IP}/websocket_offline`; if (e) { modeType = "offline"; } else { modeType = "online"; } //--------------------FunASR 连接模式选择 end------------------ if (Uri.match(/wss:\S*|ws:\S*/)) { console.log("Uri" + Uri); } else { alert("请检查wss地址正确性"); return 0; } if ("WebSocket" in window) { speechSokt = new WebSocket(Uri); // 定义socket连接对象 speechSokt.onopen = function (e) { onOpen(e); }; // 定义响应函数 speechSokt.onclose = function (e) { console.log("onclose ws!"); //speechSokt.close(); onClose(e); }; speechSokt.onmessage = function (e) { onMessage(e); }; speechSokt.onerror = function (e) { onError(e); }; return 1; } else { alert("当前浏览器不支持 WebSocket"); return 0; } }; // 定义停止与发送函数 this.wsStop = function () { if (speechSokt != undefined) { console.log("stop ws!"); speechSokt.close(); } // 清空发送队列 sendQueue = []; isSending = false; }; // 处理发送队列 function processQueue() { if ( isSending || sendQueue.length === 0 || !speechSokt || speechSokt.readyState !== 1 ) { return; } isSending = true; const data = sendQueue.shift(); try { speechSokt.send(data); isSending = false; // 队列警告 if (sendQueue.length > queueWarningThreshold) { console.warn(`⚠️ WebSocket发送队列堆积: ${sendQueue.length} 个数据包待发送`); } // 继续处理队列 if (sendQueue.length > 0) { // 使用 setTimeout 避免阻塞 setTimeout(() => processQueue(), 0); } } catch (e) { console.error("❌ WebSocket发送失败,数据已放回队列:", e); sendQueue.unshift(data); // 放回队列头部 isSending = false; trimQueueIfNeeded(); } } this.wsSend = function (oneData) { if (speechSokt == undefined) { // WebSocket未初始化,缓存数据 enqueueData(oneData); return true; } if (speechSokt.readyState === 1) { // WebSocket已打开 if (sendQueue.length > 0) { // 队列中有数据,先加入队列 enqueueData(oneData); processQueue(); return true; } else { // 队列为空,尝试直接发送 try { speechSokt.send(oneData); return true; } catch (e) { console.error("❌ WebSocket直接发送失败,加入队列:", e); enqueueData(oneData); processQueue(); return true; } } } else if (speechSokt.readyState === 0) { // WebSocket正在连接,缓存数据 enqueueData(oneData); return true; } else { // WebSocket已关闭或关闭中,丢弃数据并警告(节流 5 秒) var now = Date.now(); if (now - lastDisconnWarnTime > 5000) { console.warn("⚠️ WebSocket未连接,数据无法发送 (state:", speechSokt.readyState, ")"); lastDisconnWarnTime = now; } return false; } }; // 获取队列状态(用于调试) this.getQueueStatus = function () { return { length: sendQueue.length, isSending: isSending, readyState: speechSokt ? speechSokt.readyState : -1 }; }; async function getBaseHot(params) { try { const response = await fetch(`/tool/speakr/api/hotwords`, { method: "GET", }); if (!response.ok) { // 请求失败时返回空 console.warn("getBaseHot 请求失败,使用默认 hotwords"); return "{}"; } const data = await response.json(); const resultObj = data.hotwords.reduce((obj, item) => { obj[item.keyword] = item.weight; return obj; }, {}); return JSON.stringify(resultObj); } catch (err) { // 发生异常时返回空 console.error("getBaseHot 调用出错:", err); return ""; } } // SOCEKT连接中的消息与状态响应 async function onOpen(e) { console.log(e, "e"); // 发送json var chunk_size = new Array(10, 10, 10); var request = { chunk_size: chunk_size, wav_name: "h5", is_speaking: true, chunk_interval: 10, itn: true, // 是否使用ITN // "mode":"2pass",// 模型模式 // 对应 app.js 的 rec/rec2 双链路:同地址下通过 mode 区分在线预览和离线正式结果。 mode: modeType, hotwords: await getBaseHot(), }; console.log(request, "request"); if (isfilemode) { request.wav_format = file_ext; if (file_ext == "wav") { request.wav_format = "PCM"; request.audio_fs = file_sample_rate; } } // var hotwords=getBaseHot(); // console.log(hotwords); // if(hotwords!=null ) // { // request.hotwords=hotwords; // } // console.log(JSON.stringify(request),'热词'); speechSokt.send(JSON.stringify(request)); console.log("连接成功"); stateHandle(0); // 连接成功后,处理队列中积压的数据 if (sendQueue.length > 0) { console.log(`📤 处理连接前积压的 ${sendQueue.length} 个数据包`); processQueue(); } } function onClose(e) { stateHandle(1); } function onMessage(e) { msgHandle(e); } function onError(e) { // info_div.innerHTML="连接"+e; console.log(e); stateHandle(2); } //--------------------FunASR WebSocket 连接器 end------------------ }