xiaoyuan-assistant 0.5.51 → 0.5.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +6 -0
- package/XIAOYUAN-BUSINESS-FLOW.md +19 -0
- package/package.json +1 -1
- package/src/assets/wake/zh-CN-XiaoxiaoNeural2.mp3 +0 -0
- package/src/assets/wake/zh-CN-XiaoyiNeural2.mp3 +0 -0
- package/src/assets/zh-CN-XiaoxiaoNeural2.mp3 +0 -0
- package/src/assets/zh-CN-XiaoyiNeural2.mp3 +0 -0
- package/src/components/XiaoyuanAssistant.vue +70 -22
- package/src/index.js +1 -1
- package/src/voice/builtinWakeTTS.js +13 -0
- package/src/voice/hewoyi.js +29 -0
- package/src/voice/speech.js +230 -22
package/CHANGELOG.md
CHANGED
|
@@ -424,3 +424,21 @@ ls -lh /mnt/data/xiaoyuan-assistant-siliconflow-v0.5.29.zip
|
|
|
424
424
|
|
|
425
425
|
- 修复 NPM + Vite 场景下内置“收到指令”TTS 使用 `new URL(..., import.meta.url)` 被预构建到 `node_modules/.vite/assets` 后产生 404 的问题。
|
|
426
426
|
- 两个内置 voice 的提示音改为随 JS 包内嵌 Base64,运行时直接生成 Blob,不再请求静态 MP3 URL。
|
|
427
|
+
|
|
428
|
+
## v0.5.52
|
|
429
|
+
- 移除小园面板右上角的停止播报/静音图标。
|
|
430
|
+
- 优化语音输入:手动录音前先暂停唤醒识别并等待浏览器释放麦克风,避免双 Recognition 抢占。
|
|
431
|
+
- 增加麦克风权限、设备占用、audio-capture、aborted、启动超时等兼容处理与最多 2 次轻量重试。
|
|
432
|
+
- 语音识别异常不再强制触发 TTS,避免识别与播报互相干扰。
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
## v0.5.53
|
|
436
|
+
- 新增“你好小园”唤醒后的本地“我在您请讲”语音,按配置 voice 使用对应内置 MP3。
|
|
437
|
+
- 唤醒后进入连续语音收音,连续 3 秒无新输入才结束收音并进入指令处理。
|
|
438
|
+
- 手动打开小园仍保持输入框模式,不自动抢占麦克风。
|
|
439
|
+
|
|
440
|
+
## v0.5.54
|
|
441
|
+
- 修复手动打开小园后语音识别偶发 aborted:识别实例改为串行释放,启动前等待旧 Recognition 真正结束。
|
|
442
|
+
- 唤醒监听 pause 改为可等待的异步暂停,避免持续唤醒与手动录音/唤醒后的连续监听抢占麦克风。
|
|
443
|
+
- 首次麦克风授权不再每次识别重复 getUserMedia + stop,减少 Chrome/Edge 音频设备切换导致的 aborted。
|
|
444
|
+
- 内置“我在您请讲”改用本次提供的 zh-CN-XiaoxiaoNeural2.mp3 与 zh-CN-XiaoyiNeural2.mp3,并继续按 voice 选择。
|
package/README.md
CHANGED
|
@@ -240,3 +240,9 @@ app.use(Xiaoyuan, {
|
|
|
240
240
|
|
|
241
241
|
- 修复 NPM + Vite 场景下内置“收到指令”TTS 使用 `new URL(..., import.meta.url)` 被预构建到 `node_modules/.vite/assets` 后产生 404 的问题。
|
|
242
242
|
- 两个内置 voice 的提示音改为随 JS 包内嵌 Base64,运行时直接生成 Blob,不再请求静态 MP3 URL。
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
## v0.5.53 语音唤醒
|
|
246
|
+
- `wakeWord` 默认:`你好小园`。
|
|
247
|
+
- 唤醒成功后播放本地 `我在您请讲`,按 `voice` 匹配。
|
|
248
|
+
- 唤醒后的后续语音连续收音,3 秒静默后进入指令处理。
|
|
@@ -1246,3 +1246,22 @@ AI 分析开始提示音与数据获取/模型分析并行;多个数据源采
|
|
|
1246
1246
|
|
|
1247
1247
|
- 修复 NPM + Vite 场景下内置“收到指令”TTS 使用 `new URL(..., import.meta.url)` 被预构建到 `node_modules/.vite/assets` 后产生 404 的问题。
|
|
1248
1248
|
- 两个内置 voice 的提示音改为随 JS 包内嵌 Base64,运行时直接生成 Blob,不再请求静态 MP3 URL。
|
|
1249
|
+
|
|
1250
|
+
## v0.5.52 变更
|
|
1251
|
+
- 移除面板右上角停止播报/静音图标,避免用户误触。
|
|
1252
|
+
- 语音输入采用“先暂停唤醒监听 -> 短暂释放麦克风 -> 单次 Recognition”的顺序,降低语音输入偶发失效。
|
|
1253
|
+
- 麦克风权限、设备缺失、设备占用、浏览器启动失败和网络异常增加明确错误处理与轻量重试。
|
|
1254
|
+
- 语音识别错误不自动播报错误 TTS,减少 SpeechRecognition 与 SpeechSynthesis 之间的资源竞争。
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
## v0.5.53 唤醒与连续语音流程
|
|
1258
|
+
- 手动打开:只展开输入框,不自动监听。
|
|
1259
|
+
- 说“你好小园”:命中后暂停唤醒监听,立即播放对应 voice 的本地“我在您请讲”MP3。
|
|
1260
|
+
- 播放开始后进入连续收音;3 秒没有新的语音输入,结束本轮收音。
|
|
1261
|
+
- 收音结束后调用“收到指令,请您稍等”本地/缓存 TTS,再解析并按现有步骤流程执行。
|
|
1262
|
+
|
|
1263
|
+
### v0.5.54 语音输入稳定性
|
|
1264
|
+
- 手动打开小园后点击麦克风:先等待唤醒 Recognition 完整释放,再启动一次性识别。
|
|
1265
|
+
- “你好小园”唤醒:播放本地对应 voice 的“我在您请讲”,随后再进入后续 3 秒静默监听。
|
|
1266
|
+
- 识别启动前只做一次麦克风权限确认,避免反复 getUserMedia 导致浏览器出现 aborted。
|
|
1267
|
+
- 新增可等待 pause/resume,保证全局唤醒监听与用户录音不会并发抢占同一个 Recognition。
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -30,9 +30,6 @@
|
|
|
30
30
|
<button type="button" class="xy-icon-btn" title="收起" @click="collapse" aria-label="收起">
|
|
31
31
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg>
|
|
32
32
|
</button>
|
|
33
|
-
<button type="button" class="xy-icon-btn" title="停止播报" @click="stopSpeech" aria-label="停止播报">
|
|
34
|
-
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4l16 16M9.5 6.2A7 7 0 0 1 19 12v1.2M4.8 9.1A7 7 0 0 0 5 13"/></svg>
|
|
35
|
-
</button>
|
|
36
33
|
</div>
|
|
37
34
|
</header>
|
|
38
35
|
|
|
@@ -120,7 +117,7 @@ const runtime =
|
|
|
120
117
|
// 如果宿主项目因为 HMR / 重复依赖导致当前模块暂时拿不到注入值,
|
|
121
118
|
// 组件保持可挂载,并在 mounted 后再次尝试解析,避免整个页面崩溃。
|
|
122
119
|
const safeRuntime = runtime || { manager: null, speech: null, options: {} }
|
|
123
|
-
const XIAOYUAN_SDK_VERSION = '0.5.
|
|
120
|
+
const XIAOYUAN_SDK_VERSION = '0.5.54'
|
|
124
121
|
const { manager, speech, options } = safeRuntime
|
|
125
122
|
|
|
126
123
|
const wakeWord = options.wakeWord || '你好小园'
|
|
@@ -220,19 +217,60 @@ function collapse() {
|
|
|
220
217
|
}
|
|
221
218
|
|
|
222
219
|
async function activate(reason = 'manual') {
|
|
223
|
-
// 用户点击/唤醒时先完成本地前置 TTS 的解锁与预解码,避免收到识别结果后播放被浏览器拦截。
|
|
224
220
|
await speech.unlockTTS?.()
|
|
225
221
|
expanded.value = true
|
|
226
|
-
showSubtitle(reason === 'wake' ? '
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
222
|
+
showSubtitle(reason === 'wake' ? '我在您请讲' : '小园已唤醒,请输入或说出您的指令。')
|
|
223
|
+
|
|
224
|
+
if (reason !== 'wake') return
|
|
225
|
+
|
|
226
|
+
wakeListening.value = false
|
|
227
|
+
try { await wakeController?.pause?.() } catch (_) {}
|
|
228
|
+
|
|
229
|
+
// 唤醒后的固定应答必须先本地播放,但不等待播放完成;声音开始后即可进入后续收音。
|
|
230
|
+
if (options.enableTTS) {
|
|
231
|
+
try {
|
|
232
|
+
const started = await speech.ttsProvider?.speakWakeResponseAsync?.()
|
|
233
|
+
if (started !== false) {
|
|
234
|
+
await new Promise((resolve) => window.setTimeout(resolve, 120))
|
|
235
|
+
}
|
|
236
|
+
} catch (_) {}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!expanded.value || processing.value || listening.value) return
|
|
240
|
+
void listenAfterWake()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function listenAfterWake() {
|
|
244
|
+
if (!speech?.listenAfterWake) return
|
|
245
|
+
listening.value = true
|
|
246
|
+
interimText.value = ''
|
|
247
|
+
showSubtitle('我在您请讲…', 0)
|
|
248
|
+
try {
|
|
249
|
+
const spoken = await speech.listenAfterWake({
|
|
250
|
+
silenceMs: 3000,
|
|
251
|
+
retries: 2,
|
|
252
|
+
onInterim: (value) => { interimText.value = value || '' }
|
|
253
|
+
})
|
|
254
|
+
interimText.value = ''
|
|
255
|
+
if (spoken) {
|
|
256
|
+
// 3 秒无后续输入后,才进入“收到指令”前置提示与业务解析流程。
|
|
257
|
+
await handleCommand(spoken, 'voice-wake')
|
|
258
|
+
} else {
|
|
259
|
+
showSubtitle('没有听到您的指令,请再说一次。', 5000)
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
interimText.value = ''
|
|
263
|
+
showSubtitle(String(error?.message || '语音识别失败'), 6000)
|
|
264
|
+
} finally {
|
|
265
|
+
listening.value = false
|
|
266
|
+
if (!processing.value && options.enableWakeWord) {
|
|
267
|
+
window.setTimeout(() => {
|
|
268
|
+
if (!processing.value && !listening.value) {
|
|
269
|
+
wakeController?.resume?.()
|
|
270
|
+
wakeListening.value = true
|
|
271
|
+
}
|
|
272
|
+
}, 120)
|
|
273
|
+
}
|
|
236
274
|
}
|
|
237
275
|
}
|
|
238
276
|
|
|
@@ -286,12 +324,21 @@ function interruptForNewCommand() {
|
|
|
286
324
|
|
|
287
325
|
async function toggleListening() {
|
|
288
326
|
if (!manager || !speech) return
|
|
289
|
-
if (listening.value) return
|
|
290
|
-
|
|
291
|
-
|
|
327
|
+
if (listening.value || processing.value) return
|
|
328
|
+
try {
|
|
329
|
+
await speech.unlockTTS?.()
|
|
330
|
+
// 先彻底暂停唤醒识别,给浏览器一个很短的释放麦克风窗口,避免两个 Recognition 抢占。
|
|
331
|
+
wakeController?.pause?.()
|
|
332
|
+
wakeListening.value = false
|
|
333
|
+
await new Promise((resolve) => window.setTimeout(resolve, 120))
|
|
334
|
+
await listenForCommand({ manual: true })
|
|
335
|
+
} catch (error) {
|
|
336
|
+
const message = String(error?.message || error || '语音识别启动失败')
|
|
337
|
+
showSubtitle(message, 6000)
|
|
338
|
+
}
|
|
292
339
|
}
|
|
293
340
|
|
|
294
|
-
async function listenForCommand({ auto = false } = {}) {
|
|
341
|
+
async function listenForCommand({ auto = false, manual = false } = {}) {
|
|
295
342
|
// 在开始语音识别时就完成本地前置 TTS 的 AudioContext 解锁/预解码,
|
|
296
343
|
// 这样识别结束后收到结果时无需依赖新的用户手势。
|
|
297
344
|
await speech.unlockTTS?.()
|
|
@@ -302,8 +349,10 @@ async function listenForCommand({ auto = false } = {}) {
|
|
|
302
349
|
}
|
|
303
350
|
|
|
304
351
|
// 手动或唤醒后的单次聆听期间,暂停持续唤醒监听,避免两个 Recognition 实例抢麦克风。
|
|
305
|
-
wakeController?.pause?.()
|
|
306
352
|
wakeListening.value = false
|
|
353
|
+
try { await wakeController?.pause?.() } catch (_) {}
|
|
354
|
+
// 等待旧 Recognition 真正 onend,再创建新的 Recognition,减少 Chrome/Edge 的 aborted。
|
|
355
|
+
await new Promise((resolve) => window.setTimeout(resolve, manual ? 220 : 180))
|
|
307
356
|
|
|
308
357
|
listening.value = true
|
|
309
358
|
interimText.value = ''
|
|
@@ -321,9 +370,8 @@ async function listenForCommand({ auto = false } = {}) {
|
|
|
321
370
|
} catch (error) {
|
|
322
371
|
interimText.value = ''
|
|
323
372
|
const message = String(error?.message || error || '语音识别失败')
|
|
324
|
-
addMessage('assistant', message)
|
|
325
373
|
showSubtitle(message, 6000)
|
|
326
|
-
|
|
374
|
+
// 语音识别错误不再强制播报,避免把浏览器 TTS 与识别重新启动互相干扰。
|
|
327
375
|
} finally {
|
|
328
376
|
listening.value = false
|
|
329
377
|
// 当前指令处理结束后恢复全局唤醒监听。
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Built-in wake response: “我在您请讲” audio, keyed by configured voice.
|
|
2
|
+
// Exact MP3s supplied by the project owner; embedded to avoid Vite/NPM asset URL rewriting.
|
|
3
|
+
export const BUILTIN_WAKE_RESPONSE_AUDIO = Object.freeze({
|
|
4
|
+
'zh-CN-XiaoxiaoNeural': "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RNQJGqJHjQaEpzf/6fKfKfHypbZp/3bf/VPG5Ut5KtE7ZbfT//NkxPciJDYAAEhGxMbLPn+vlPiTbNP3w5kjTEiMJG9AhRVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxPEhBBHUAFDNOVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//NkxHwAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV",
|
|
5
|
+
'zh-CN-XiaoyiNeural': "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qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqpMQU1FMy4xMDCqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq//NkxHwAAANIAAAAAKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export function base64ToWakeBlob(base64, mimeType = 'audio/mpeg') {
|
|
9
|
+
const binary = atob(base64)
|
|
10
|
+
const bytes = new Uint8Array(binary.length)
|
|
11
|
+
for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)
|
|
12
|
+
return new Blob([bytes], { type: mimeType })
|
|
13
|
+
}
|
package/src/voice/hewoyi.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getReceivedCommandAudio, saveReceivedCommandAudio } from './localReceivedTTS.js'
|
|
2
|
+
import { BUILTIN_WAKE_RESPONSE_AUDIO, base64ToWakeBlob } from './builtinWakeTTS.js'
|
|
2
3
|
|
|
3
4
|
const RECEIVED_COMMAND_TEXT = '收到指令,请您稍等。'
|
|
4
5
|
|
|
@@ -641,6 +642,34 @@ export class HewoyiTTSProvider {
|
|
|
641
642
|
}
|
|
642
643
|
}
|
|
643
644
|
|
|
645
|
+
getBuiltinWakeResponseBlob() {
|
|
646
|
+
const base64 = BUILTIN_WAKE_RESPONSE_AUDIO[String(this.voice || '').trim()]
|
|
647
|
+
if (!base64) return null
|
|
648
|
+
try {
|
|
649
|
+
return base64ToWakeBlob(base64, 'audio/mpeg')
|
|
650
|
+
} catch (error) {
|
|
651
|
+
this.error('内置“我在您请讲”语音解析失败:', error?.message || error)
|
|
652
|
+
return null
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async speakWakeResponseAsync() {
|
|
657
|
+
// 唤醒词响应只走本地音频,不请求网络。
|
|
658
|
+
const blob = this.getBuiltinWakeResponseBlob()
|
|
659
|
+
if (!blob) return false
|
|
660
|
+
try {
|
|
661
|
+
this.stopCurrentSpeechOnly()
|
|
662
|
+
const played = await this.playWithAudioElement(blob, { waitForEnd: false, minStartHoldMs: 80 })
|
|
663
|
+
if (played) {
|
|
664
|
+
this.log('本地“我在您请讲”语音已开始播放,voice:', this.voice)
|
|
665
|
+
return true
|
|
666
|
+
}
|
|
667
|
+
} catch (error) {
|
|
668
|
+
this.error('本地“我在您请讲”播放失败:', error?.message || error)
|
|
669
|
+
}
|
|
670
|
+
return false
|
|
671
|
+
}
|
|
672
|
+
|
|
644
673
|
async speakReceivedCommandAsync() {
|
|
645
674
|
const value = RECEIVED_COMMAND_TEXT
|
|
646
675
|
|
package/src/voice/speech.js
CHANGED
|
@@ -18,6 +18,18 @@ export class SpeechService {
|
|
|
18
18
|
return await this.ttsProvider?.unlock?.()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
async waitForRecognitionRelease(timeoutMs = 1200) {
|
|
22
|
+
const current = this.recognition
|
|
23
|
+
if (!current) return true
|
|
24
|
+
try { current.__xiaoyuanWaitingRelease = true } catch (_) {}
|
|
25
|
+
try { current.stop() } catch (_) {}
|
|
26
|
+
const started = Date.now()
|
|
27
|
+
while (this.recognition === current && Date.now() - started < timeoutMs) {
|
|
28
|
+
await new Promise((resolve) => window.setTimeout(resolve, 40))
|
|
29
|
+
}
|
|
30
|
+
return this.recognition !== current
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
supported() {
|
|
22
34
|
return typeof window !== 'undefined' && !!(
|
|
23
35
|
window.SpeechRecognition ||
|
|
@@ -26,13 +38,33 @@ export class SpeechService {
|
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
async requestMicrophonePermission() {
|
|
29
|
-
if (typeof navigator === 'undefined'
|
|
41
|
+
if (typeof navigator === 'undefined') throw new Error('当前环境无法访问麦克风')
|
|
42
|
+
// Web Speech 自己持有识别麦克风;不要在每次 start 前额外 getUserMedia -> stop,
|
|
43
|
+
// 否则 Chrome/Edge 容易在两个音频会话切换瞬间返回 aborted。
|
|
44
|
+
if (this._micPermissionGranted) return true
|
|
45
|
+
if (!navigator.mediaDevices?.getUserMedia) return true
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
49
|
+
audio: true
|
|
50
|
+
})
|
|
51
|
+
this._micPermissionGranted = true
|
|
52
|
+
// 只用于首次授权,立即释放独立权限流;后续识别不再反复申请。
|
|
53
|
+
for (const track of stream.getTracks()) track.stop()
|
|
30
54
|
return true
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const name = error?.name || 'unknown'
|
|
57
|
+
if (name === 'NotAllowedError' || name === 'SecurityError') {
|
|
58
|
+
throw new Error('麦克风权限被拒绝,请在浏览器地址栏允许当前页面使用麦克风')
|
|
59
|
+
}
|
|
60
|
+
if (name === 'NotFoundError' || name === 'DevicesNotFoundError') {
|
|
61
|
+
throw new Error('没有检测到麦克风设备,请检查麦克风是否连接')
|
|
62
|
+
}
|
|
63
|
+
if (name === 'NotReadableError' || name === 'TrackStartError') {
|
|
64
|
+
throw new Error('麦克风正在被其他程序占用,请关闭其他录音/视频程序后重试')
|
|
65
|
+
}
|
|
66
|
+
throw error
|
|
31
67
|
}
|
|
32
|
-
|
|
33
|
-
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
|
34
|
-
for (const track of stream.getTracks()) track.stop()
|
|
35
|
-
return true
|
|
36
68
|
}
|
|
37
69
|
|
|
38
70
|
createRecognition({ continuous = false } = {}) {
|
|
@@ -169,30 +201,60 @@ export class SpeechService {
|
|
|
169
201
|
}
|
|
170
202
|
}
|
|
171
203
|
|
|
172
|
-
listenOnce() {
|
|
204
|
+
async listenOnce({ retries = 2 } = {}) {
|
|
205
|
+
if (!this.supported()) {
|
|
206
|
+
throw new Error('当前浏览器不支持语音识别,请使用 Chrome / Edge')
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let lastError = null
|
|
210
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
211
|
+
try {
|
|
212
|
+
return await this._listenOnceInternal()
|
|
213
|
+
} catch (error) {
|
|
214
|
+
lastError = error
|
|
215
|
+
const msg = String(error?.message || '')
|
|
216
|
+
const retryable = /启动失败|audio-capture|麦克风|网络异常|network|aborted/i.test(msg)
|
|
217
|
+
if (!retryable || attempt >= retries) throw error
|
|
218
|
+
await new Promise((resolve) => window.setTimeout(resolve, 220 + attempt * 280))
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
throw lastError || new Error('语音识别失败')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_listenOnceInternal() {
|
|
173
225
|
return new Promise(async (resolve, reject) => {
|
|
174
|
-
|
|
175
|
-
|
|
226
|
+
try {
|
|
227
|
+
await this.waitForRecognitionRelease()
|
|
228
|
+
await this.requestMicrophonePermission()
|
|
229
|
+
} catch (error) {
|
|
230
|
+
reject(error instanceof Error ? error : new Error(String(error)))
|
|
176
231
|
return
|
|
177
232
|
}
|
|
178
233
|
|
|
234
|
+
let recognition
|
|
179
235
|
try {
|
|
180
|
-
|
|
236
|
+
recognition = this.createRecognition({ continuous: false })
|
|
181
237
|
} catch (error) {
|
|
182
|
-
|
|
183
|
-
reject(new Error(`麦克风权限不可用:${reason}`))
|
|
238
|
+
reject(error)
|
|
184
239
|
return
|
|
185
240
|
}
|
|
186
241
|
|
|
187
|
-
const recognition = this.createRecognition({ continuous: false })
|
|
188
242
|
let finalText = ''
|
|
189
243
|
let settled = false
|
|
244
|
+
let startTimer = null
|
|
245
|
+
let stopTimer = null
|
|
246
|
+
|
|
247
|
+
const cleanup = () => {
|
|
248
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
249
|
+
if (stopTimer) window.clearTimeout(stopTimer)
|
|
250
|
+
this.listening = false
|
|
251
|
+
this.recognition = null
|
|
252
|
+
}
|
|
190
253
|
|
|
191
254
|
const finish = (error = null) => {
|
|
192
255
|
if (settled) return
|
|
193
256
|
settled = true
|
|
194
|
-
|
|
195
|
-
this.recognition = null
|
|
257
|
+
cleanup()
|
|
196
258
|
this.onEnd()
|
|
197
259
|
if (error) reject(error)
|
|
198
260
|
else resolve(finalText.trim())
|
|
@@ -200,6 +262,7 @@ export class SpeechService {
|
|
|
200
262
|
|
|
201
263
|
recognition.onstart = () => {
|
|
202
264
|
this.listening = true
|
|
265
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
203
266
|
}
|
|
204
267
|
|
|
205
268
|
recognition.onresult = (event) => {
|
|
@@ -215,17 +278,16 @@ export class SpeechService {
|
|
|
215
278
|
}
|
|
216
279
|
this.onInterim(interim)
|
|
217
280
|
|
|
218
|
-
// 已得到 final 文本就尽快结束本轮,减少“说完以后还要等很久”的感觉。
|
|
219
281
|
if (finalText.trim()) {
|
|
220
|
-
window.
|
|
282
|
+
if (stopTimer) window.clearTimeout(stopTimer)
|
|
283
|
+
stopTimer = window.setTimeout(() => {
|
|
221
284
|
try { recognition.stop() } catch (_) {}
|
|
222
|
-
},
|
|
285
|
+
}, 60)
|
|
223
286
|
}
|
|
224
287
|
}
|
|
225
288
|
|
|
226
289
|
recognition.onerror = (event) => {
|
|
227
290
|
const code = event?.error || 'unknown'
|
|
228
|
-
// no-speech 更像“没有听到”,不是系统故障。
|
|
229
291
|
if (code === 'no-speech') {
|
|
230
292
|
finish(new Error('没有听到您的声音,请再说一次。'))
|
|
231
293
|
return
|
|
@@ -235,28 +297,166 @@ export class SpeechService {
|
|
|
235
297
|
return
|
|
236
298
|
}
|
|
237
299
|
if (code === 'audio-capture') {
|
|
238
|
-
finish(new Error('
|
|
300
|
+
finish(new Error('没有检测到可用麦克风,或麦克风正在被其他程序占用。'))
|
|
239
301
|
return
|
|
240
302
|
}
|
|
241
303
|
if (code === 'network') {
|
|
242
304
|
finish(new Error('语音识别网络异常,请检查网络后再试。'))
|
|
243
305
|
return
|
|
244
306
|
}
|
|
307
|
+
if (code === 'aborted') {
|
|
308
|
+
// aborted 通常来自外部 stop/pause,不把它当成系统崩溃。
|
|
309
|
+
if (finalText.trim()) finish()
|
|
310
|
+
else finish(new Error('语音识别被中断,请再试一次。'))
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
finish(new Error(`语音识别失败:${code}`))
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
recognition.onend = () => {
|
|
317
|
+
if (finalText.trim()) finish()
|
|
318
|
+
else if (!settled) finish(new Error('语音识别结束但没有识别到内容,请再试一次。'))
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
recognition.start()
|
|
323
|
+
this.recognition = recognition
|
|
324
|
+
this.listening = true
|
|
325
|
+
// 防止某些 Chrome 版本 start() 成功但迟迟没有 onstart,释放状态并让上层重试。
|
|
326
|
+
startTimer = window.setTimeout(() => {
|
|
327
|
+
if (!settled && !this.listening) {
|
|
328
|
+
try { recognition.stop() } catch (_) {}
|
|
329
|
+
finish(new Error('语音识别启动失败,请再试一次。'))
|
|
330
|
+
}
|
|
331
|
+
}, 1800)
|
|
332
|
+
} catch (error) {
|
|
333
|
+
finish(error instanceof Error ? error : new Error('语音识别启动失败'))
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
listenAfterWake({ silenceMs = 3000, retries = 1, onInterim = this.onInterim } = {}) {
|
|
339
|
+
if (!this.supported()) return Promise.reject(new Error('当前浏览器不支持语音识别,请使用 Chrome / Edge'))
|
|
340
|
+
|
|
341
|
+
const run = () => new Promise(async (resolve, reject) => {
|
|
342
|
+
try {
|
|
343
|
+
await this.requestMicrophonePermission()
|
|
344
|
+
} catch (error) {
|
|
345
|
+
reject(error instanceof Error ? error : new Error(String(error)))
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let recognition
|
|
350
|
+
try {
|
|
351
|
+
recognition = this.createRecognition({ continuous: true })
|
|
352
|
+
} catch (error) {
|
|
353
|
+
reject(error)
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let finalText = ''
|
|
358
|
+
let settled = false
|
|
359
|
+
let silenceTimer = null
|
|
360
|
+
let startTimer = null
|
|
361
|
+
|
|
362
|
+
const cleanup = () => {
|
|
363
|
+
if (silenceTimer) window.clearTimeout(silenceTimer)
|
|
364
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
365
|
+
this.listening = false
|
|
366
|
+
this.recognition = null
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const finish = (error = null) => {
|
|
370
|
+
if (settled) return
|
|
371
|
+
settled = true
|
|
372
|
+
cleanup()
|
|
373
|
+
try { recognition.stop() } catch (_) {}
|
|
374
|
+
this.onEnd()
|
|
375
|
+
if (error) reject(error)
|
|
376
|
+
else resolve(finalText.trim())
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const armSilenceTimer = () => {
|
|
380
|
+
if (silenceTimer) window.clearTimeout(silenceTimer)
|
|
381
|
+
silenceTimer = window.setTimeout(() => finish(), Math.max(1200, Number(silenceMs) || 3000))
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
recognition.onstart = () => {
|
|
385
|
+
this.listening = true
|
|
386
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
387
|
+
armSilenceTimer()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
recognition.onresult = (event) => {
|
|
391
|
+
let interim = ''
|
|
392
|
+
let gotSpeech = false
|
|
393
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
394
|
+
const part = event.results[i][0]?.transcript || ''
|
|
395
|
+
if (part) gotSpeech = true
|
|
396
|
+
if (event.results[i].isFinal) {
|
|
397
|
+
finalText += part
|
|
398
|
+
this.onFinal(part)
|
|
399
|
+
} else {
|
|
400
|
+
interim += part
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
onInterim?.(interim)
|
|
404
|
+
this.onInterim(interim)
|
|
405
|
+
if (gotSpeech) armSilenceTimer()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
recognition.onerror = (event) => {
|
|
409
|
+
const code = event?.error || 'unknown'
|
|
410
|
+
if (code === 'no-speech') {
|
|
411
|
+
// 3 秒无输入视为用户说完,不当成崩溃。
|
|
412
|
+
finish()
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
if (code === 'aborted' && finalText.trim()) {
|
|
416
|
+
finish()
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
if (code === 'not-allowed' || code === 'service-not-allowed') {
|
|
420
|
+
finish(new Error('麦克风权限被拒绝,请允许当前页面使用麦克风后再试。'))
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
if (code === 'audio-capture') {
|
|
424
|
+
finish(new Error('没有检测到可用麦克风,或麦克风正在被其他程序占用。'))
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
if (code === 'network') {
|
|
428
|
+
finish(new Error('语音识别网络异常,请稍后再试。'))
|
|
429
|
+
return
|
|
430
|
+
}
|
|
245
431
|
finish(new Error(`语音识别失败:${code}`))
|
|
246
432
|
}
|
|
247
433
|
|
|
248
434
|
recognition.onend = () => {
|
|
249
|
-
|
|
435
|
+
if (settled) return
|
|
436
|
+
if (finalText.trim()) finish()
|
|
437
|
+
else finish(new Error('语音识别结束但没有识别到内容,请再说一次。'))
|
|
250
438
|
}
|
|
251
439
|
|
|
252
440
|
try {
|
|
253
441
|
recognition.start()
|
|
254
442
|
this.recognition = recognition
|
|
255
443
|
this.listening = true
|
|
444
|
+
startTimer = window.setTimeout(() => {
|
|
445
|
+
if (!settled && !this.listening) finish(new Error('语音识别启动失败,请再试一次。'))
|
|
446
|
+
}, 1800)
|
|
256
447
|
} catch (error) {
|
|
257
448
|
finish(error instanceof Error ? error : new Error('语音识别启动失败'))
|
|
258
449
|
}
|
|
259
450
|
})
|
|
451
|
+
|
|
452
|
+
return run().catch(async (error) => {
|
|
453
|
+
const retryable = /启动失败|audio-capture|网络异常|network|aborted/i.test(String(error?.message || ''))
|
|
454
|
+
if (retryable && retries > 0) {
|
|
455
|
+
await new Promise((resolve) => window.setTimeout(resolve, 220))
|
|
456
|
+
return this.listenAfterWake({ silenceMs, retries: retries - 1, onInterim })
|
|
457
|
+
}
|
|
458
|
+
throw error
|
|
459
|
+
})
|
|
260
460
|
}
|
|
261
461
|
|
|
262
462
|
startWakeWordListener({ wakeWord = '你好小园', onWake, onError }) {
|
|
@@ -311,14 +511,22 @@ export class SpeechService {
|
|
|
311
511
|
try { recognition.stop() } catch (_) {}
|
|
312
512
|
buffer = ''
|
|
313
513
|
},
|
|
314
|
-
pause() {
|
|
514
|
+
async pause() {
|
|
315
515
|
paused = true
|
|
316
516
|
try { recognition.stop() } catch (_) {}
|
|
517
|
+
const started = Date.now()
|
|
518
|
+
while (!stopped && this.recognition === recognition && Date.now() - started < 1200) {
|
|
519
|
+
await new Promise((resolve) => window.setTimeout(resolve, 40))
|
|
520
|
+
}
|
|
521
|
+
return this.recognition !== recognition
|
|
317
522
|
},
|
|
318
523
|
resume() {
|
|
319
524
|
if (stopped) return
|
|
320
525
|
paused = false
|
|
321
|
-
|
|
526
|
+
window.setTimeout(() => {
|
|
527
|
+
if (stopped || paused) return
|
|
528
|
+
try { recognition.start() } catch (_) { restart() }
|
|
529
|
+
}, 180)
|
|
322
530
|
},
|
|
323
531
|
recognition
|
|
324
532
|
}
|