xiaoyuan-assistant 0.5.51 → 0.5.53
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 +12 -0
- package/README.md +6 -0
- package/XIAOYUAN-BUSINESS-FLOW.md +13 -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/components/XiaoyuanAssistant.vue +69 -21
- 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 +211 -19
package/CHANGELOG.md
CHANGED
|
@@ -424,3 +424,15 @@ 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
|
+
- 手动打开小园仍保持输入框模式,不自动抢占麦克风。
|
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,16 @@ 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,再解析并按现有步骤流程执行。
|
package/package.json
CHANGED
|
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.53'
|
|
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
|
+
wakeController?.pause?.()
|
|
227
|
+
wakeListening.value = false
|
|
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?.()
|
|
@@ -304,6 +351,8 @@ async function listenForCommand({ auto = false } = {}) {
|
|
|
304
351
|
// 手动或唤醒后的单次聆听期间,暂停持续唤醒监听,避免两个 Recognition 实例抢麦克风。
|
|
305
352
|
wakeController?.pause?.()
|
|
306
353
|
wakeListening.value = false
|
|
354
|
+
// 等待上一个 recognition.onend 真正释放麦克风,避免 Chrome/Edge 报 audio-capture/not-allowed。
|
|
355
|
+
await new Promise((resolve) => window.setTimeout(resolve, manual ? 120 : 80))
|
|
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 the configured TTS voice.
|
|
2
|
+
// Embedded directly into the package to avoid NPM/Vite asset URL rewriting.
|
|
3
|
+
export const BUILTIN_WAKE_RESPONSE_AUDIO = Object.freeze({
|
|
4
|
+
'zh-CN-XiaoxiaoNeural': '//NkxAAAAANIAAAAAExBTUVVVVUeQy2iECwBnjDyZPSd3f7RBOoxoz3ERHvXTe7c0J4LmlPmjnEKJQBC6BsT137ET9ESvlBG7u8KgbxC4kEcJz3IOaFTpi+u8CJ5xECKLMoTuedJ8OLiaP8SIToiE8Jz0zoGLmHk0/4IIRpgQhEHk7IY92TTaLJk9gwhEWxA//NkxHwAAANIAAAAAIR8PJxD3d20b7J6YQzbtoIIOsrAc9Md1wgUNJg4BCURBGwk8eDSmQZYuKoBQ08nglhIz1iU2SXFYyoShuAXLLivCIVMyXQQRlDxeEEaMV62YRwTQKoERVojXaUc1CDorUogJZSo2jZSJT7I8SRQKDCFGmaXIlYagQkQkYLoEiPSqKI0//NkxP8jnDHgADDNXSZVtRQQYvjkksUidQBcycQwicYJOI0wLPGy0BRNQoggF2l10w+PHlBI9XHm2GCQjcPlCBGwaI3vaMaKgEgBhAYJXwmQisjkdJm3xOk4qUIGGV6OAOJwfPjZc1jNsHAYWARyNtlJI+mC5tk2bHwoYCBvKht1SwKgG5Wc/EI1t/FngVkT//NkxPM4VDnwIMGSFI4MarZXR6pFuWeEJ1ZQknGaNq6QLtznOmLXbq26YXbQdJEwKxWjSFB0DYGHPKOmjFbVxGRinTQNSMMIMvSa4gvT9zN5ekEMsgZjOWLJsnRkR220bjTyYXWY2dkdEBAW07KaRiCRnaNy3w+/85kMghjsQIY2pprgg9275zwd5+S+/Gw8//NkxJQsnDokNHpMvJ2szJP7zGFpn4YY5Cydwhr3TQXblHxZ1fx31d7EMlU74Ar36VTUGYish4Z1xKZ+3xjrd3/pnGdiWRqakj/YymKicKig4WVRISQq9oncQFERyREZmrJCWmQoz6pZEcTE6AZLtJJOUJEYbGWNUgq0gWIlk0JRREvKaRB0zKjdI0WxbRQi//NkxGQzBDIgAVhIAdNoIykwYRo9ZkqujlBYk/c0m885YyQQUILaMzUJLcbX7bMHooTVnF2XFiE0Ntqi6xepuVum9rvSil3vV7tj1Yt2g1pmp7H7ryTYpMe51mQpaSTV5J06UlSBVQllPE55Udiu79v+NzeVBOlXhiqq+5Cc+73hXtKvW3Vxu3EzgEgvoy+b//NkxBsjzDJUKZiQASwDQAPQdeg901igR6E6DospbqQQdROjjcmDyDpNqQWxxaKTV3STVdC9Ba03Vp01u1Bld+leXE9RoTZkVq1Pq6D3dVn+aLoVM+1X69G31eve6b0G/TMCcKx500vbr/2/t/+/e600EFummmdL7k2V1LJs2GTEqkAgITCdnrttrbJrtJY9//NkxA4g0qrSX49oApBJBTKlzeikZHeVgfReCWM16pcLWJmkP7pjOXBwFqbpkELwePhLnZ9KtN02Ry6kfTdN7629PWn8wQbsZGLVum63X6TIG/zBd37m7Gqk6f+s1TS/5qYlx3r6a3/rqL5fTAH1OH+0JDgotve3AYP6e+lxD/FSZmoiAwpzbau+yf50vpQ4//NkxA0gMxriX89oAqMS9IE1i103gtktb6WN51Kcxuk8ew4ARwJwSjDvDnnk0kB6EupnZJnMDROp1oNUh1p9NNS102emmZupaaD33//9D10tBGnUgm7UP+g1XrUswQL49CTSLiDs3//+swZPM2TRW6CBxnlnf+rcQEN303SRpro6YYNRM29ktbdlR9jyofi2//NkxA8ewWLmXpPS6i6LWsUOCpEYYnRlyryya/mPXNXEH2JgOlRGyCrQpQErEgB+JeGz0ayuo9udGR9GTkAG2ZbC63V9v+p5KEUd1BJNzRTjVHgUWsOlxIMKzrs5zZ+fB8e3//6yf/9Q0H2AARiRxo+p0/dD5oDjLoElZJLdbbY3jMmT5z8NwCWlbabzMFUh//NkxBch6xbqXnjLgjFPkQCLF1kRqemDNmi/rhNvtVVrGTk5VCDOIOTZUs4siw3VgPJcXXnOC+qzKtjma0l+EQjuKsWBhQEFDDiuww3dkobstKblOhjq+spHR1/60ZVKdLGViojMRf//+/NXMh1OI//2vYeeWIvd6WKHFDNNwDbIxVljQFL1YwlKOfbKIXE4//NkxBIcqeK1vnmEmF9c/VlLYBQUYvBI2W5pHHY5L9q0j3lkTdItsnEpnmM91f0N0NfL0MZSARpfsei0Xy0Sjsjgj5INxIJB3QpbJGsRHqRXk3DNho0bDJlP+pMVHNxzw86ZUCpo0ae5a2POpOICLVWhmu1tZA9pWwIcPJSsphKl9lDaBrynKSLJ4SMHm8qd//NkxCIdQP6afnmGcEElCdVrUIdIUt2/5ugGYXefLgQoWEwANiigrSTIh1QciXS8acGBASR4CAKYe5Lv5Y0+sq7XFeIlirs7qHjFuPUgVwdOig8yXOiMIvuGvHqCYRaRRneJlnh/9/agMtUjogadDmAIwKmTmULiDFTRHLze0oXohCzBEL8II4yFX00dnMrC//NkxDAcJAK3HhiH6S+QniT0hf+VfsKWQv0/O79X8npV/Ve/0ajkUX//nP6Ek759PLkT57xK5fYXX3y6aF83/ju4cXK7qBmAAHzT50j6H3///+121QH9PSVH0cWfxa4ccwi+whAhObyvAMZ+NgNtZAgzHL/Je4MQJyQEvP/yXnYADaKe8im7euyEPIyncPI0//NkxEIbo/rWXhhFOcEET3Vv9Ho2iqLyK77VS7Tu0k5GaejIEIShGa7UbkY84GCAGLODMJiGuaPfyvv/rv9rdrERdiUmqSxngGAMxPrQwAQCCF5twLYP6yfanuY5iTiqxCPAkgIkKqAyKyN0YS/hl+UmWEl19R6gQe6v16n9LK9Q+HhEWAiLR0ewq5UWVHZZ//NkxFYcqosCXsJKukaQyPWs8KCIgWt3fs0v/f23ZnRRVbv/V3Hi3v9d+8/yrv0ZDQzVurlBLsHAIIOFpTE83lh4dUjpixA+tCJDOsxL/sn/n1OQEGdvmd0RJnm/XqB8xZe2Lg8Yxw8QBqAW9Nup1JZsjAhB2dqsYyBTzC9Nf//8rPUJPJ///KvcRt+7//sQ//NkxGYb0e7OVNsE8LWs+QQxr4W/+lXwJ2yx//FE5mQ3t/v41/4fraBA5HgKvQS2gbuFukXpz8bkNrKElvny0KkBM5H/YaN+NAdxCnGG6kECHhvTt5EAL1WArXqdJnaCb6v+jfRvlEnV9L//////qLMlzOBlt6f//////9F0uebLBSUc3Gvu1pYAAqTqipNI//NkxHkcO872VsLFDtsiq7QR3T4qBDvxmU9gqEtSur5YxRb8stpQMqbmrWPQu56xIWKWaxd2RWvM7tAQm2M/Gmwp5yxMDG7REZ1RVP6ibfn/ILXMrMAnTOn7/RF+c3RVVHWY8RZCuUhBVj93If//qvUTVfa0+HYyupa6liIEktaDtzs1rZ1ujeDmnzT/BgE2//NkxIsdYr7W/mvKuL/GJT0C7pDguoJ7A8AupsZzZPiBRr8vcKK98bwSyVKJSgIh8Gr8rGJQgLkScFEjCw8Sw7vYhU+pn6nS6shxgizpq3///t/+6Gj1JOoqFyIs3Z///4uv1sLNGMH1DXRthRM7CVRBczYusV9YUEJ2bH12MD2GIh5nf0Nw4s2j0RAC4LU5//NkxJgc6rbe/nrLJH+mwRwW5pq++W9TM2vpjBqVs3SFlb/7qZ+cDBl2oWmcGKEs3I/zoZ/L6oxisAMIBlmX/+pFkQmTQNBQLOr9v///jhMPUwBIE4OAR6ibJxw9pwA2k4G4o4zMsKaygfFL/byD5LFHow5YzHLzWPVmOwg0e2/wfAWL6KmFk1JsadX9MFAF//NkxKcc8e7W/nmFRITy+IYsus6AKOhEYdW4soEKqxz+5FKaqLu9crMjMZxlnSb///+tfaqJZYqpGQSQzvvyWfT//////u6ioa86iALaXlpd9wrgEgQDbvhWw/bwBYbe/+VZpDBp7DdyjgW9S509xlCmwEwaueSV2DU2a31CCKXnNmREeClkxCMBYaZc2zFe//NkxLYeQ7LO1noFDVHKQSUzJVaFDFnIV5NqpXe307gpjkV3Nv///m1XVl/K0KAlOgqQQSLPX0f//sHkQudWsAgipAnCZPc9CoADYkK4VL/v/aAqc0KAvV/q75zkcJTFAFXM9gEj7TCUPLHJzT/aIy20Rav2P3dDGoLZvVPIkNozCqp5ZJVZ1TFaHloaZTpM//NkxMAeGqq6FMJE8Ln6keqqOWAZ1v/4bDtK4cPPfAJ1gLP2ahZLv0yBJhHUDoiYGUPKpGlLFuLT1FVhRlaiE5ZJMKpmJBTmAi02ShKSiMXmGlJGrRnGr0bUkkjQmH8ar3hhQFD2b9hVGASP1LVmpNhQEqrXPDCsmY243/tdqEFZBhQYPmZ0X/iUNSVRWp4C//NkxModCeLG/nmGnORDlCw0VO+RDqyp2sxIq4FG5UBB0Gi1B6/drcpMQU0IBUqX8rF2kEEHKrYPhOedW0czvM6dYTJH0cSdmbgk5KtlyJiNSxI2YJZ6rfzQCSNnhyEYcgDFpaY0aXXrRcytOTExitZoBB1QVAJHSMeh20iCo48boTft6LWhrOkI75tX/X3a//NkxNgcGfayPjBHIt27xF9/8soKTSVjbevsjkmFLqGWY0JBCgGUTXpVkKoTVExJSJtYziBJmSO2lpXst+RMZe0htGmFcODfIHATCjZdM0J0OPCJahkbuzrmDTIyY1Fq6ERlZDM0eO7tFYVO1jZxRo5kgvJLAd+WeaFR4MueGjCjWhpCDRjdaNuBQmgzG+vL//NkxOcawUZUNHmY7FYPtrfBnsF/q3VkT14kJU1VTEFNRTMuMTAwVVVVVVVVVVUAjG31Rdwe8fa4iy2xndGECGVkwG0j8JyOZKed8jYRosQIB0MIOmBJJt8x793VYQZvpT6Vrzqj6amHlNPlSB5CBQLgVdIQVWAA1j1RNGtYaMz1xlN4shdVF9apv2XkuF1G//NkxP8jCrosAMJGfStKGsU+tSoLXJjqZLANMgl2p+PR2ngHCZxfOtDFSWxCoFhgNEkA0TjyKdthWBiJFcoxlLxNI25CTDmNuGBZRarxqKEFG464kdWWsQk8UlYjodx9rw9yUBaHpCMXq0VrSPiRn9nNApFEihx5RrCPOeiCxS/RdR7UM9gKG/g1nCNJwARn//NkxOQZyW5IXHhMfBnmvHc2tuUvaccJQRIUH9n/nnzrNSAwEQWM3y+FuIa1Q3mxIwPhp00k/aSAMRkMka6aoQpNrM9vn+7jGoW9ONIDjcRnNMx9w3TDyiMj01nqLl3xJ22d7N6mo64MBclDwDVlm52YZ2pGv4UXJ6pamsTL2wd7pjtW/r7st8sW//MDCUD5//NkxP8kGmIoAMGG1X2DL6mZ+pldy1yavinaa9vlhaoxV8n2xZibTnBJJNvcNaYkVR4JWaxJAoofJ6JDfmK7Ug/X0c3PdFYoss2vm0rY031ZFJdahRZBI/cN2nN7PZmDYS3PUvu43+fa+v9x2iGfP9QSHHJPiHKyFlzfiAfxWFE+BOE6CV+gasvWew9B7ljZ//NkxPEgCZ4sCHpMRbC34DuY6NtePJbbfL+dffPUnrFV9iroOM/9SMMnhFFSAfARMBYPYVdFzclkDTP0iv0SBplliT0J1Ig+KocXkqcIHqKpSOpPqLBQORUleqvapnM9HiMeWTMbGiu/m9OFpyEyZH16dluJxT7ueRMS/5024CxUjzo4BwuYyymDJlcUNgZW//NkxPMhMgosCMPMKZPa38xrundqk9/zMtc9x1vX/GpUR3gq+dJocFB00DSgaPlxNNNpHFppAm+kTm2llir8O01BjBUwoN8oLOGwWlY4P8IQsHBQiidsids9UcjzT468NyM+pO8xrsFWcbu9tsTngtyPSshlkeVRTglihETnDT/KL3KcWUb63BbayTzIhS32//NkxPEgErIsAMGGWSyzrvbc/WOU0Gxm5uuEq2f/uvPqG4Q6QG6OcqhVT1yjvp3Um4wdoxKMUspZ5IH823vHyRzPz2O4FigO5NKl4nOXuR9kwDQ9sWUw8KJceaa000pMn1buMGGDCRzJDkjBJX4FJ+7UrVn6FjvJx71wN44/YJ+/2xn/eumWHQZIshP8tpE+//NkxPMg80IsAMpGPZhjt+yWY6U5+rJvPdsdLHwC4METTzuNNvPm6eNnOrFtVhFJxtvpSCRAdVObhDCcIlXC6ZN0HxvemzLe5KXFJmpWw50IiuVoyIxODhrAgtGVCD0Z41ftgJGoQ4hlYd4IRzEi48BhlTHJKRwigdq0N6rDKSOulrO5jU0cPUNGrb0hbJs7//NkxPIfAco0EnjQnfdEMyS9KEl3MyNvy3XU9Ck8d1zFKxhNemOClsqguk9gOIToWow1hPvxTQ587M4TADQNOkxFHkqURtJZxPESGPkXHEstTtznlDDDeXWJqg9DVW4MA4lMyY9nIlRmrDDkGTZFlPUINo0wUc2ZvzGvTMkhpBV0SkbZXIUAhwRsas2TZa5c//NkxPkk48ooAMmGvWHIHc9sCu5nvR6EaLiT/khEc4Ad79FX5WKes0iSz+xYcL9gmHbqEYCMCAkwIIDFrLN1gk5NiBMKGlzqYFAg4DENTA4FIQINARl8MUM+DjxBAgaIkBopM9QmmksgfpCV9qk8kEA5yIXKYIEhYhEBpDwRFNsdmIIk4LDkHHvHv3wycxN///NkxOgiksYsANGGnRxy/XcTAot8X1x/HPX8JFXxCPbu8JW/un9X/x83e98c2lxW55Apu9oi3vPPXwooXdOUAoImLuH4wkG4fkA0PIMkXPYUHAoEQtyxd0hE/RRoefJ1f//8SbG/YMBMlIE4lbEwlBEKAAkcW7MQRnBwvlADOLeYnLO4XZdIL6yqzocLbrGp//NkxOAr28ZAAOGQvJjyLhUVviXcfW84xLS0N5EhwmGNAg42pJnj1Rwx9l7ebHOHOzRWysiGLhUQ+pzTJ2XNRqCJTV388POf+eaN80ZGCYbkxdP916h////WT/y/6YtiCCFfYXewuoeGff/9hONIMhnTJ3UdC4rRvgwj26QMKILICTFGF5EEYKitYkSIHFJI//NkxLM1rCp8yuvS3d7cDBRhAJ6Lo9UgcEiUEDM9Izajc5zmo7UFXuL1cmKt8FyoYgJJJJI5JJI5/f0qiIkBsAKXbWHRNggGy63IzclLj51vo/kpXxRosdtfNd4xAclBreYMKn/////14M2JLdS2eQplYa62hByroMoTApDhYSCmg/mRfL5oPYDxMF5YLfMO//NkxF80tC7CXtPYvpwT7QuO/2vsK3z5hQQk5ylbaX06k1mZn5mc3qdTqq2Jhh2H65MfV+u7OzaZ3b268bCS9yktdWxnHSpmOs0gr1OfYg/6Oe/X7cwt6vnKTcvXXcTnubHho6wtfDpEZcFZgEDyUuOHFi/H371/zd6v9Vm+fEwmQIststRS+bHKz2GRTFH+//NkxA8gK8rGXMpFFy3NRJb4CTbPLMphibNLO6RJ12pbRtxSqV3r+7onmgVQhkOkEE1WmMhVX/b2P/W/LncPCKrbJ8kzxq82WubhrBb2ZixZCoi0pWrDGexSXv/rXV7orFrVuRrnUn9v8lCMYkyLX////7U+fpNYrOBGCCxUXYANnwCQcp2XS1iRitJ6wCmc//NkxBEhK8KdlNIE+Lvi4IElpIp/9qsNkfa25TCY3b5FR1/JJwmm6JWK4a1v+WT2aUtVU4TMwKgqIwgmh0d8RBTDzJ0q1iLyp8u8zzqti6f373Kwk73vQ1F2SY5Rk/9fS01SqZ/9vX2ZnuUrEdmW5ueZSqUqsqlAQokaIXfYvXDiQAFRZkV8nKpbNRlpQgMT//NkxA8gqoqWVNpKlIqjNnB41QxeAXFcG+3EhkRH2L8tGgIEb5RuK2eSyJGgXnK/l36sm5UUo1HKMHmczsrMs2aIlRaOv9H9DcxjGM4k62Oj9XJFlIV0Cxz133rVnMtTi6vNfkeehowpuwYaYeIh1JUWJOOsoaZAo0Sup9oFLDTNoANiRlMmrQNmnjkQnF4P//NkxA8ciT6mXsJMSKga1uYCstpsh1GFE6QIt+YgVRJByZPWz3pAEgsmQNTIGEMFj6Ikmjv9a98Snv29aELD6w+KLJL0KpMn7HWiB2xwF3PZLvapQITa7c//8Qb/3ENigwHyHcTLhYEHQffcFgwJzSqWFrB0EA6E6jJN1i1N//2ur5Vf/Zjt//Vt29DMzUq///NkxB8iVDKoAECTPf6nW3////IQnk7bq8jNvydGU93Ood/dUhe/1D0gQKMLoIk6zlGIEZgjFYrbIBQx0hQhJ25EAoJDcr9t7VtisE6CgAEFkYoGAwKRWsIDLc20kF0wgYLisVj7bZ2UUIJrEk8I2mJk4rRzg0jqkP0/9f////////wEAJHP/jFAFs8o//v1//NkxBggxDrAqghTHMgMAEpEueOZ3y+34VGfo9OXyoIFCRhnwlS6iZpNi87W9ORMKdesIwDpDq4UMjb0zoIRHjsqZRqvbv+FUwj1kyUNieimE4ycLhDVxUOmTapAKzgeZC6rrQkDKRCfFSglYtaeqlBkuVgQoIIreHh3+31cc4FDLaJIeiUleLZPdSgE4qFx//NkxBgcyoLy/GGG3ojdTuR2szQ2/emcE5AKjeS3rWPqHjYf5nv/Nb6rC3OAjMy33LQ3abXTbBCnj533DnqjOUs5vc2V1UmzPyhJl3/M9x/Or01QEDyIqHP6Oosa/+5iP2jYWa+Y/VRVpnl3dYd4iplKmvZw7TOtkQdg7yE/tHYIgK9I6AIGIGrGyLKU6EZ+//NkxCcdUdLTGmDMvGfDJ//NqkM9RIGAZ5f+5VlWyLuClXnppbfrNWP21/OlEhEBQo3WVjUINMBkJOaAWAU9O53qaOIsSi2pq/g0xLQ7O0zylgqswOenLPiVhZXShSuSwAKp7DgVcnUMZWzkUMAASw/4umg/B/Et04gTRmwdeR9Jtu+4V3Yv3MtpTU/0y6/Z//NkxDQcW/K+XNmEmOxUcEdJA6TyXCMYMgY/KXpfXu23MaalWp7LvvnKVuUpWZW/L/f/0ZVLdk1/r3/p2/v/1pe93c6oVHqFH/Oq1ort5eKgta25q1L4cXWZRAgPaW5J/xIQbSre/DASmBHYJycnFLJp4N04paakPBW03l4of6+kNuyI50J3kFndKlNR6lZE//NkxEUccja6ftvKXDgCKlL/++UrdPKWpS1RjOy0fY2qs4iMIPgItd/+SJZV5ZPfmzw6oWXFr+tIBgTUkAZpCrdylLAjDSaOAhd1K9DJFsiQHrZ/qBnn7/x5RKBoAu2LjDpZY/TgsmtucOQnLZ//zj4iGCj07d6m9WVPKnuYPAOPY1v9dqUcmyPf7OfVZ9nU//NkxFYb0iKRHOGLDEy4nBcJX///21jXVo120uQI3n2G2F0gEQqqgEdpqT3kqnd1LsAGDRH3hBjC9hSNAwlfv+qeW3nhKYlRcxcGnUO12VyMYxyDQ+NKjtv2MJKKmY9O+57LSzo7ONcRElaZP+v7ImRC9TLe0pNBxqyiIGFjBoaYma//Xj2JYZFcVc859QsY//NkxGkcAjKlvtPKVDwfOGzKTuiAAyQSNz+XOZUVBdghnpikIDM6gFpwRDwEKt5do22JxUVQ197mEKLLgYsEnpIn01tEjMarO3nglfIukY4I0zlP3n+mp/XhX+mxl/0v+Q//oY4DDQsCdA49knt81q3jS7FHn0Y4laQxKWaWBkcEhay6pLX8vYA0g41IAT01//NkxHwc6jKiXtJGWKUi0MQ1UYbO3nX05rpY5Fjlmxti0XSeR81NnnWJSdh+5rGW2Ln6Yy2ses6XtU6iYt08VHUd//////7xXxzf/vlnulPFaUlO/FfCek2t2KVFeNuj+HIQu4MBvBEiiDlBeIA50LDse51RqQhiQWqIkdzslKnM1j61Izvlnr3a6f/T0FHE//NkxIscrDqpvFgRyED27H09OudeHEcEI3Qi/7/1/OPXrmVsDi0CJUsEIgSz2W/pf5hs92NRv4zA71JEhMKjY1eqJIkDESAhkFggKRQMkdkbFZMZd9MZlwHR/hGGF8qVWjEELqa4+C7BKD8PR0oEKQK+xoQxtMRTm+cZ+FsDAQJbg6SVBjmUAHCEDxOYTsT4//NkxJsylDqYACjfjNVkinGIQfpCzTL8PwehyQ8BfLGhQ5yLLZEb5hAAW7ic6FD5MY5IquYFSW9idtBO6nQUetttZdrWBVijkCVjHzBoWExaC7RQozEU7dS0zOzT3C3T3tWmXPE96MwTMDQOzxzqPtp4V3FNMuSHz5qCQwJBw5VvaPzaaz9atXovjtjbqZe9//NkxFMvxDLG/kMNsQr3CmqBiuy8tbSF2rTVzmLIzltjYHnlbDCSNQUVxwclM4K4MTk4QY6fZhLCkZq9iE9MUKxe5YyWj2JZ4DYnDy4OSCicXlq4pLsS85Wz6atn1NAEJdVGs7Ntb/T1IKRmTrgkVWsSJJfM5ufkVYyHXNpHCWbLJGBhUEAoSGY0CZoSxpeA//NkxBchGe6+8uZQUIqdiV4Apo3iJQ7NjyaO78zMjBhgxoKm7WIzar5OQBM4d4oDQOA8FJIW+piHH8S6IWfM8YwgRxQkJ3AXPQUqb3f6lXe9XaV//f////4q4eCIgcT/1oucKJa9G1pcwn8C657R9xP/9trTTBd+LoKVaFN3hEREQc6K9azlCbZk5JqxxfKJ//NkxBUgW47bHtGE8OcREIE1roxjcDRY6zIxBIInGUAuLFmAJiPbLszgAAEa2T0z6820O6sjO+7ec+0z4RSpN8ZGpRP6IW9HmX5NinR+pXRyIZGUtW//+9vqW4GStbZv/////Zb7kvuVCIdRAYAIVFIwgBIb/7OTTQriBlWyJOXWO+2fAH4ecXFz/ZTpmY4m//NkxBYdUU7eXn4MjOFLqtJGG0JvISYommk2uLCcfSBIfCXGBYIeiWeQNxFRtSgZiehPokblF6l/vlvpWS25lKZxRdrS4HAJA8InGwqBSp4kv9CLtgNCV4V//6er3VlFXT2erOSCIfw9tSrmKqKhRHuKeMwtF+B+tzCe5Sl6riAqTmUqkhpdQ7sqBXwbqEs5//NkxCMa4VLJ9nmLBP6FafQDQai9o0+T6miesHhaedtVR283UpTKZpbBpAbBF/jj4sXF5ZRZY1yER+oi8JAN4efDgiHnf/7u6RSLKMxD/5UwMNo4doqo5mNuftzvc44DEvb26zNBaFYthlknZbPr3K45CM+XzIQjJ1EfPL06bI4l+21DTRV5XdHoosxT8hNq//NkxDoc6krJ/sMGeF/8lSQ9jIHJBY7kWSWF6khUIU1/8pvM/vtPzOlrw8CCsVQpn/17iEycChk0wnMzQNP/QsNNGjFuF0UwKTB1p+hPG1jTD1pG8bx0HaNxfE8RzXgs3ssZhTeVa/F6srpMqJiyuHDTezFQoo4qATiWUjAWHIyPOg8OWZtbM+Ges4saUlAj//NkxEkdCba6nsPGPHN3zYNg/L4LgQQvpDQZXefNoXITZCESbUp/+tjGbHQqXJIIGRIj/elYwdUqRUahKayauWaMFMVOq6JWSscnah9Y0gr1ll0667ouqOTMtBkWBMf/1Hz6jkHNDJFYY6stO5LUZaR6QKpmg96sLEk8MLTKHPwrmVCHqlqVbr+lBVKymM6s//NkxFccozLWPmDE/lb//9W1boiI9elkcoCCgoDo0GurbcDJB6nvOvoSM/a3b3asBO+FBo0C799a7Z5DPbXVbXjocQ9PiqVlVGv0xTGVCqWcNK1HzOylXEwUhjlK2pUeZSzc3/R1McsxlzPMrF/ytrRy//1Ywk5Slo/ylv3T//6Pt1Z1UspbKUOgKdI3/8GH//NkxGcbyy6g3sMKPIq4q8VCX5XM1W4VECCUq1oIMHyBwEOZ5rf7Yk8TqUvqnaG9Gi2pEeoTInyNN1KC5J46nIbreoo6t04USom8Fd8K7CnGDCjrfJpfbzfLyoKWGFIqIBDo5QEBChHOkbi5FORSuwqCo1oh5L/8ReqCgNSzw6/54Kz2dXwC+Jyh4iCUS1S5//NkxHobsbJgHnjFKIget/HTxWfXeVgSE2IU5q2KxM0KNhXMR1Lt/F8GowDLFlPLgMCDxQIIHNBUapQHNn2hhcqtTSagBNIpRWVWgVkVJX2Wb6+3XZ3K7o5isXh1tHsX6aU6mdJQwNFDTHErpwTwVQtIDrF4NTtA9TmHxMDgsbFSQaLPYLE3FkNgJoeQZMlG//NkxI4YuKZACnseFKXFWajJVab9xq++o21RJqp7OkkMa9p4KA4RcsgeDIlakKlqXQEdIsg1nSKKWsn0x9yYaICoSKJYtnlhlQiqWX4jD2HoZwQ6lhZ6AqLPcwWPgEKpa2oqdPOeLbXFELIE0LTYtTqlSYHi8mpCE1LF0NrbX7BUmwF4SKhB8ek/NRCMqRoA//NkxK4ZmGI0AMJMKOVcbPAcTyaaBSx1S0vcKDDNR5hxkpaH1QCYIFg0oxR08t/ACHIgUyINMSGAciohCK9AphWWK6ExUyhxkOl32K1SHTJqChmqJaTHVqzRz+Wp7Sm1hXOv+bbT1sJq00O5Xz7YaqTK8LJyRddyUHNoOwVijmqio+xUhmqOTlQtGGOUXDwW//NkxMoXkCIo8mDGAMHmRYRqJDoFSKa4zZgsgRjB4wyoFSkC48VOKooQ7QfXIwZw9ydJtezMpM9FfjZxOKlMqsK1Zcs0NH0W5EZLmV2TGq5Q0sJK30rlLnK1pR8rouc8tD9/ebI+uTLsU7DRiNIvpTR1cqeROO+rqQtERXOqOgk3lZ85YPH50xiDMMOwogwp//NkxO4f1DIVaDBGfZyxkDmhh4aFmaLqAcspNScIoW2KfV0tJtSd+N41Bk/zhj70l+PVyvrwCqkuUGujMK5IBgt5rLsVWHQESGxUpYIVmVWZKZYr1pBqcqEbl5Up2EOm1lZjlzKrUUcFoo4QLcSCChtqpXP6mKPkMakF6JZVhyqKjwKQkNFBlHDNeCmUMoMZ//NkxPEfI+oQqGDGAZjVLQgEgdMCMmAWUUIB4zykY1oYdQkiYDcLxwfHkpzTirEysdOX///NYcNYak1O9RlICg6k2uxBg4kM6xo1WGu2zqzl/P/+w1hrDh0mpchrn/NqVPlS+bLf7L5WmwSLRNQJGqJHjQaEpzf/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
|
@@ -26,13 +26,38 @@ export class SpeechService {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async requestMicrophonePermission() {
|
|
29
|
-
if (typeof navigator === 'undefined'
|
|
29
|
+
if (typeof navigator === 'undefined') {
|
|
30
|
+
throw new Error('当前环境无法访问麦克风')
|
|
31
|
+
}
|
|
32
|
+
if (!navigator.mediaDevices?.getUserMedia) {
|
|
33
|
+
// Web Speech 在部分浏览器可用,即使 MediaDevices API 不可用,也继续让 Recognition 自己处理。
|
|
30
34
|
return true
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
try {
|
|
38
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
39
|
+
audio: {
|
|
40
|
+
echoCancellation: true,
|
|
41
|
+
noiseSuppression: true,
|
|
42
|
+
autoGainControl: true,
|
|
43
|
+
channelCount: 1
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
for (const track of stream.getTracks()) track.stop()
|
|
47
|
+
return true
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const name = error?.name || 'unknown'
|
|
50
|
+
if (name === 'NotAllowedError' || name === 'SecurityError') {
|
|
51
|
+
throw new Error('麦克风权限被拒绝,请在浏览器地址栏允许当前页面使用麦克风')
|
|
52
|
+
}
|
|
53
|
+
if (name === 'NotFoundError' || name === 'DevicesNotFoundError') {
|
|
54
|
+
throw new Error('没有检测到麦克风设备,请检查麦克风是否连接')
|
|
55
|
+
}
|
|
56
|
+
if (name === 'NotReadableError' || name === 'TrackStartError') {
|
|
57
|
+
throw new Error('麦克风正在被其他程序占用,请关闭其他录音/视频程序后重试')
|
|
58
|
+
}
|
|
59
|
+
throw error
|
|
60
|
+
}
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
createRecognition({ continuous = false } = {}) {
|
|
@@ -169,30 +194,59 @@ export class SpeechService {
|
|
|
169
194
|
}
|
|
170
195
|
}
|
|
171
196
|
|
|
172
|
-
listenOnce() {
|
|
197
|
+
async listenOnce({ retries = 2 } = {}) {
|
|
198
|
+
if (!this.supported()) {
|
|
199
|
+
throw new Error('当前浏览器不支持语音识别,请使用 Chrome / Edge')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let lastError = null
|
|
203
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
204
|
+
try {
|
|
205
|
+
return await this._listenOnceInternal()
|
|
206
|
+
} catch (error) {
|
|
207
|
+
lastError = error
|
|
208
|
+
const msg = String(error?.message || '')
|
|
209
|
+
const retryable = /启动失败|audio-capture|麦克风|网络异常|network|aborted/i.test(msg)
|
|
210
|
+
if (!retryable || attempt >= retries) throw error
|
|
211
|
+
await new Promise((resolve) => window.setTimeout(resolve, 220 + attempt * 280))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
throw lastError || new Error('语音识别失败')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
_listenOnceInternal() {
|
|
173
218
|
return new Promise(async (resolve, reject) => {
|
|
174
|
-
|
|
175
|
-
|
|
219
|
+
try {
|
|
220
|
+
await this.requestMicrophonePermission()
|
|
221
|
+
} catch (error) {
|
|
222
|
+
reject(error instanceof Error ? error : new Error(String(error)))
|
|
176
223
|
return
|
|
177
224
|
}
|
|
178
225
|
|
|
226
|
+
let recognition
|
|
179
227
|
try {
|
|
180
|
-
|
|
228
|
+
recognition = this.createRecognition({ continuous: false })
|
|
181
229
|
} catch (error) {
|
|
182
|
-
|
|
183
|
-
reject(new Error(`麦克风权限不可用:${reason}`))
|
|
230
|
+
reject(error)
|
|
184
231
|
return
|
|
185
232
|
}
|
|
186
233
|
|
|
187
|
-
const recognition = this.createRecognition({ continuous: false })
|
|
188
234
|
let finalText = ''
|
|
189
235
|
let settled = false
|
|
236
|
+
let startTimer = null
|
|
237
|
+
let stopTimer = null
|
|
238
|
+
|
|
239
|
+
const cleanup = () => {
|
|
240
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
241
|
+
if (stopTimer) window.clearTimeout(stopTimer)
|
|
242
|
+
this.listening = false
|
|
243
|
+
this.recognition = null
|
|
244
|
+
}
|
|
190
245
|
|
|
191
246
|
const finish = (error = null) => {
|
|
192
247
|
if (settled) return
|
|
193
248
|
settled = true
|
|
194
|
-
|
|
195
|
-
this.recognition = null
|
|
249
|
+
cleanup()
|
|
196
250
|
this.onEnd()
|
|
197
251
|
if (error) reject(error)
|
|
198
252
|
else resolve(finalText.trim())
|
|
@@ -200,6 +254,7 @@ export class SpeechService {
|
|
|
200
254
|
|
|
201
255
|
recognition.onstart = () => {
|
|
202
256
|
this.listening = true
|
|
257
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
203
258
|
}
|
|
204
259
|
|
|
205
260
|
recognition.onresult = (event) => {
|
|
@@ -215,17 +270,16 @@ export class SpeechService {
|
|
|
215
270
|
}
|
|
216
271
|
this.onInterim(interim)
|
|
217
272
|
|
|
218
|
-
// 已得到 final 文本就尽快结束本轮,减少“说完以后还要等很久”的感觉。
|
|
219
273
|
if (finalText.trim()) {
|
|
220
|
-
window.
|
|
274
|
+
if (stopTimer) window.clearTimeout(stopTimer)
|
|
275
|
+
stopTimer = window.setTimeout(() => {
|
|
221
276
|
try { recognition.stop() } catch (_) {}
|
|
222
|
-
},
|
|
277
|
+
}, 60)
|
|
223
278
|
}
|
|
224
279
|
}
|
|
225
280
|
|
|
226
281
|
recognition.onerror = (event) => {
|
|
227
282
|
const code = event?.error || 'unknown'
|
|
228
|
-
// no-speech 更像“没有听到”,不是系统故障。
|
|
229
283
|
if (code === 'no-speech') {
|
|
230
284
|
finish(new Error('没有听到您的声音,请再说一次。'))
|
|
231
285
|
return
|
|
@@ -235,30 +289,168 @@ export class SpeechService {
|
|
|
235
289
|
return
|
|
236
290
|
}
|
|
237
291
|
if (code === 'audio-capture') {
|
|
238
|
-
finish(new Error('
|
|
292
|
+
finish(new Error('没有检测到可用麦克风,或麦克风正在被其他程序占用。'))
|
|
239
293
|
return
|
|
240
294
|
}
|
|
241
295
|
if (code === 'network') {
|
|
242
296
|
finish(new Error('语音识别网络异常,请检查网络后再试。'))
|
|
243
297
|
return
|
|
244
298
|
}
|
|
299
|
+
if (code === 'aborted') {
|
|
300
|
+
// aborted 通常来自外部 stop/pause,不把它当成系统崩溃。
|
|
301
|
+
if (finalText.trim()) finish()
|
|
302
|
+
else finish(new Error('语音识别被中断,请再试一次。'))
|
|
303
|
+
return
|
|
304
|
+
}
|
|
245
305
|
finish(new Error(`语音识别失败:${code}`))
|
|
246
306
|
}
|
|
247
307
|
|
|
248
308
|
recognition.onend = () => {
|
|
249
|
-
finish()
|
|
309
|
+
if (finalText.trim()) finish()
|
|
310
|
+
else if (!settled) finish(new Error('语音识别结束但没有识别到内容,请再试一次。'))
|
|
250
311
|
}
|
|
251
312
|
|
|
252
313
|
try {
|
|
253
314
|
recognition.start()
|
|
254
315
|
this.recognition = recognition
|
|
255
316
|
this.listening = true
|
|
317
|
+
// 防止某些 Chrome 版本 start() 成功但迟迟没有 onstart,释放状态并让上层重试。
|
|
318
|
+
startTimer = window.setTimeout(() => {
|
|
319
|
+
if (!settled && !this.listening) {
|
|
320
|
+
try { recognition.stop() } catch (_) {}
|
|
321
|
+
finish(new Error('语音识别启动失败,请再试一次。'))
|
|
322
|
+
}
|
|
323
|
+
}, 1800)
|
|
256
324
|
} catch (error) {
|
|
257
325
|
finish(error instanceof Error ? error : new Error('语音识别启动失败'))
|
|
258
326
|
}
|
|
259
327
|
})
|
|
260
328
|
}
|
|
261
329
|
|
|
330
|
+
listenAfterWake({ silenceMs = 3000, retries = 1, onInterim = this.onInterim } = {}) {
|
|
331
|
+
if (!this.supported()) return Promise.reject(new Error('当前浏览器不支持语音识别,请使用 Chrome / Edge'))
|
|
332
|
+
|
|
333
|
+
const run = () => new Promise(async (resolve, reject) => {
|
|
334
|
+
try {
|
|
335
|
+
await this.requestMicrophonePermission()
|
|
336
|
+
} catch (error) {
|
|
337
|
+
reject(error instanceof Error ? error : new Error(String(error)))
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let recognition
|
|
342
|
+
try {
|
|
343
|
+
recognition = this.createRecognition({ continuous: true })
|
|
344
|
+
} catch (error) {
|
|
345
|
+
reject(error)
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let finalText = ''
|
|
350
|
+
let settled = false
|
|
351
|
+
let silenceTimer = null
|
|
352
|
+
let startTimer = null
|
|
353
|
+
|
|
354
|
+
const cleanup = () => {
|
|
355
|
+
if (silenceTimer) window.clearTimeout(silenceTimer)
|
|
356
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
357
|
+
this.listening = false
|
|
358
|
+
this.recognition = null
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const finish = (error = null) => {
|
|
362
|
+
if (settled) return
|
|
363
|
+
settled = true
|
|
364
|
+
cleanup()
|
|
365
|
+
try { recognition.stop() } catch (_) {}
|
|
366
|
+
this.onEnd()
|
|
367
|
+
if (error) reject(error)
|
|
368
|
+
else resolve(finalText.trim())
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const armSilenceTimer = () => {
|
|
372
|
+
if (silenceTimer) window.clearTimeout(silenceTimer)
|
|
373
|
+
silenceTimer = window.setTimeout(() => finish(), Math.max(1200, Number(silenceMs) || 3000))
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
recognition.onstart = () => {
|
|
377
|
+
this.listening = true
|
|
378
|
+
if (startTimer) window.clearTimeout(startTimer)
|
|
379
|
+
armSilenceTimer()
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
recognition.onresult = (event) => {
|
|
383
|
+
let interim = ''
|
|
384
|
+
let gotSpeech = false
|
|
385
|
+
for (let i = event.resultIndex; i < event.results.length; i += 1) {
|
|
386
|
+
const part = event.results[i][0]?.transcript || ''
|
|
387
|
+
if (part) gotSpeech = true
|
|
388
|
+
if (event.results[i].isFinal) {
|
|
389
|
+
finalText += part
|
|
390
|
+
this.onFinal(part)
|
|
391
|
+
} else {
|
|
392
|
+
interim += part
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
onInterim?.(interim)
|
|
396
|
+
this.onInterim(interim)
|
|
397
|
+
if (gotSpeech) armSilenceTimer()
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
recognition.onerror = (event) => {
|
|
401
|
+
const code = event?.error || 'unknown'
|
|
402
|
+
if (code === 'no-speech') {
|
|
403
|
+
// 3 秒无输入视为用户说完,不当成崩溃。
|
|
404
|
+
finish()
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
if (code === 'aborted' && finalText.trim()) {
|
|
408
|
+
finish()
|
|
409
|
+
return
|
|
410
|
+
}
|
|
411
|
+
if (code === 'not-allowed' || code === 'service-not-allowed') {
|
|
412
|
+
finish(new Error('麦克风权限被拒绝,请允许当前页面使用麦克风后再试。'))
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
if (code === 'audio-capture') {
|
|
416
|
+
finish(new Error('没有检测到可用麦克风,或麦克风正在被其他程序占用。'))
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
if (code === 'network') {
|
|
420
|
+
finish(new Error('语音识别网络异常,请稍后再试。'))
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
finish(new Error(`语音识别失败:${code}`))
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
recognition.onend = () => {
|
|
427
|
+
if (settled) return
|
|
428
|
+
if (finalText.trim()) finish()
|
|
429
|
+
else finish(new Error('语音识别结束但没有识别到内容,请再说一次。'))
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
recognition.start()
|
|
434
|
+
this.recognition = recognition
|
|
435
|
+
this.listening = true
|
|
436
|
+
startTimer = window.setTimeout(() => {
|
|
437
|
+
if (!settled && !this.listening) finish(new Error('语音识别启动失败,请再试一次。'))
|
|
438
|
+
}, 1800)
|
|
439
|
+
} catch (error) {
|
|
440
|
+
finish(error instanceof Error ? error : new Error('语音识别启动失败'))
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
return run().catch(async (error) => {
|
|
445
|
+
const retryable = /启动失败|audio-capture|网络异常|network|aborted/i.test(String(error?.message || ''))
|
|
446
|
+
if (retryable && retries > 0) {
|
|
447
|
+
await new Promise((resolve) => window.setTimeout(resolve, 220))
|
|
448
|
+
return this.listenAfterWake({ silenceMs, retries: retries - 1, onInterim })
|
|
449
|
+
}
|
|
450
|
+
throw error
|
|
451
|
+
})
|
|
452
|
+
}
|
|
453
|
+
|
|
262
454
|
startWakeWordListener({ wakeWord = '你好小园', onWake, onError }) {
|
|
263
455
|
if (!this.supported()) return { stop: () => {}, pause: () => {}, resume: () => {} }
|
|
264
456
|
|