"use strict"; const common_vendor = require("../../common/vendor.js"); const MIN_HOLD_MS = 280; function isBenignSttError(msg, retcode) { const m = String(msg || "").toLowerCase(); if (/internal voice data failed/i.test(m)) return true; if (/voice data failed/i.test(m)) return true; if (/recordfailed|record failed|record manager/i.test(m)) return true; if (/please stop after start/i.test(m)) return true; if (/no recognition|not start|未开始|无识别/i.test(m)) return true; const code = Number(retcode); if (code === -30002 || code === -30003 || code === -30012) return true; return false; } function friendlySttError(msg) { const m = String(msg || "").trim(); if (!m || isBenignSttError(m)) return ""; if (/not allowed|auth|permission|权限|麦克风/i.test(m)) return "请允许使用麦克风"; if (/network|网络|timeout/i.test(m)) return "网络异常,请稍后再试"; return "语音识别失败,请重试"; } let sharedManager = null; let sharedManagerInited = false; let sharedRecording = false; let activeCtl = null; function getSharedRecordManager() { if (sharedManagerInited) return sharedManager; sharedManagerInited = true; try { const plugin = requirePlugin("WechatSI"); sharedManager = plugin.getRecordRecognitionManager(); } catch (e) { common_vendor.index.__f__("warn", "at tongji/composables/useSpeechToText.js:44", "WechatSI record recognition not available", e); sharedManager = null; return null; } if (!sharedManager) return null; sharedManager.onStart = () => { sharedRecording = true; if (activeCtl) activeCtl.handleStart(); }; sharedManager.onRecognize = (res) => { if (activeCtl) activeCtl.handleRecognize(res); }; sharedManager.onStop = (res) => { sharedRecording = false; if (activeCtl) activeCtl.handleStop(res); }; sharedManager.onError = (res) => { sharedRecording = false; if (activeCtl) activeCtl.handleError(res); }; return sharedManager; } function useSpeechToText({ onResult, onSettle, onPartial, onError, showToast } = {}) { const sttListening = common_vendor.ref(false); const sttHolding = common_vendor.ref(false); const sttSupported = common_vendor.ref(false); let wxRecordManager = null; let holdStartTs = 0; let holdSessionId = 0; let startRequested = false; let recordAuthorized = null; let pendingStartSession = 0; function notify(msg) { if (!msg) return; if (typeof showToast === "function") { showToast(msg); return; } common_vendor.index.showToast({ title: msg, icon: "none", duration: 2e3 }); } function emitResult(text) { const t = String(text || "").trim(); const heldMs = Date.now() - holdStartTs; if (typeof onSettle === "function") { const handled = onSettle(t, { heldMs }); if (handled === true) return; } if (!t) { if (heldMs < MIN_HOLD_MS) return; notify("没听清,请再试一次"); return; } if (typeof onResult === "function") { onResult(t); } } function stopListening() { const ownsRecording = sharedRecording && activeCtl === controller; if (wxRecordManager && (sttListening.value || ownsRecording)) { try { wxRecordManager.stop(); } catch (e) { } } startRequested = false; sttHolding.value = false; sttListening.value = false; } function ensureRecordAuth(onGranted) { if (recordAuthorized === true) { onGranted(); return; } common_vendor.index.authorize({ scope: "scope.record", success() { recordAuthorized = true; onGranted(); }, fail() { recordAuthorized = false; sttHolding.value = false; common_vendor.index.showModal({ title: "需要麦克风权限", content: "请在设置中允许使用麦克风,以便长按说话", confirmText: "去设置", success(res) { if (res.confirm) { common_vendor.index.openSetting({}); } } }); } }); } function actuallyStartWechat(session) { if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) { return; } try { startRequested = true; wxRecordManager.start({ duration: 6e4, lang: "zh_CN" }); } catch (e) { startRequested = false; sharedRecording = false; sttHolding.value = false; notify("无法开始录音"); } } function startWechatRecord(session) { if (!wxRecordManager || session !== holdSessionId || !sttHolding.value) { return; } if (sharedRecording || startRequested) { pendingStartSession = session; try { wxRecordManager.stop(); } catch (e) { } return; } actuallyStartWechat(session); } function beginHoldSpeech() { if (!sttSupported.value) { notify("当前环境不支持语音输入"); return; } if (sttHolding.value) return; const session = ++holdSessionId; holdStartTs = Date.now(); sttHolding.value = true; activeCtl = controller; ensureRecordAuth(() => startWechatRecord(session)); } function endHoldSpeech(force = false) { let recording = false; recording = sharedRecording && activeCtl === controller; if (!force && !sttHolding.value && !sttListening.value && !startRequested && !recording) { return; } sttHolding.value = false; pendingStartSession = 0; if (sttListening.value || recording) { stopListening(); return; } if (startRequested) { startRequested = false; holdSessionId += 1; return; } holdSessionId += 1; } const controller = { handleStart() { if (!sttHolding.value) { startRequested = false; sttListening.value = false; return; } startRequested = false; sttListening.value = true; }, handleRecognize(res) { if (typeof onPartial !== "function") return; const t = String((res == null ? void 0 : res.result) || "").trim(); if (t) onPartial(t); }, handleStop(res) { sttListening.value = false; startRequested = false; if (pendingStartSession && pendingStartSession === holdSessionId && sttHolding.value) { const s = pendingStartSession; pendingStartSession = 0; actuallyStartWechat(s); return; } pendingStartSession = 0; emitResult(res == null ? void 0 : res.result); }, handleError(res) { sttListening.value = false; startRequested = false; const msg = res && res.msg || ""; const retcode = res && (res.retcode ?? res.errCode ?? res.code); if (/please stop after start/i.test(msg)) { try { wxRecordManager.stop(); } catch (e) { } pendingStartSession = sttHolding.value ? holdSessionId : 0; return; } pendingStartSession = 0; sttHolding.value = false; const heldMs = Date.now() - holdStartTs; if (isBenignSttError(msg, retcode) || heldMs < MIN_HOLD_MS) { if (typeof onError === "function") onError(msg || ""); return; } if (typeof onError === "function") { const handled = onError(msg || ""); if (handled === true) return; } const tip = friendlySttError(msg); if (tip) notify(tip); } }; wxRecordManager = getSharedRecordManager(); sttSupported.value = !!wxRecordManager; common_vendor.onUnmounted(() => { sttHolding.value = false; holdSessionId += 1; pendingStartSession = 0; stopListening(); if (activeCtl === controller) { activeCtl = null; } }); return { sttListening, sttHolding, sttSupported, beginHoldSpeech, endHoldSpeech, stopSpeechToText: endHoldSpeech }; } exports.isBenignSttError = isBenignSttError; exports.useSpeechToText = useSpeechToText; //# sourceMappingURL=../../../.sourcemap/mp-weixin/tongji/composables/useSpeechToText.js.map