fix(voice): ship non-asyncify ort-wasm + force single-thread
All checks were successful
Deploy / deploy (push) Successful in 3m46s
All checks were successful
Deploy / deploy (push) Successful in 3m46s
В public/vad/ были только asyncify-варианты, а onnxruntime-web по дефолту
просит ort-wasm-simd-threaded.{mjs,wasm} → 404 → MicVAD init falls.
- Положили ort-wasm-simd-threaded.{mjs,wasm} рядом.
- ortConfig forces numThreads=1, чтобы не требовать SharedArrayBuffer
(нет COOP/COEP headers и не хотим их вешать на весь сайт).
- Раздельный getUserMedia probe перед VAD init, чтобы отличить отказ
по микрофону от ошибки VAD/wasm в UI-сообщении.
This commit is contained in:
@@ -112,17 +112,38 @@ export default function VoiceController() {
|
||||
const start = async () => {
|
||||
if (state !== 'idle' && state !== 'error') return
|
||||
setState('loading')
|
||||
|
||||
// 1. Сначала отдельно проверяем mic — иначе ошибка VAD маскирует разрешения.
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
// VAD откроет свой stream; этот закрываем, мы только проверяли разрешение.
|
||||
stream.getTracks().forEach((t) => t.stop())
|
||||
} catch (e: any) {
|
||||
console.error('[voice] mic permission failed:', e?.name, e?.message)
|
||||
setState('error')
|
||||
const reason = e?.name === 'NotAllowedError' ? 'Нет доступа к микрофону'
|
||||
: e?.name === 'NotFoundError' ? 'Микрофон не найден'
|
||||
: 'Микрофон не открылся'
|
||||
emitLocal('error', AGENT, reason)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Инициализируем VAD. Single-threaded WASM — без COOP/COEP headers
|
||||
// threaded режим всё равно не заработает, плюс не нужна SharedArrayBuffer.
|
||||
try {
|
||||
const { MicVAD } = await import('@ricky0123/vad-web')
|
||||
const vad = await MicVAD.new({
|
||||
model: 'v5',
|
||||
baseAssetPath: '/vad/',
|
||||
onnxWASMBasePath: '/vad/',
|
||||
// sensitivity tuned conservatively — тише = меньше ложных срабатываний эха
|
||||
ortConfig: (ort: any) => {
|
||||
ort.env.wasm.numThreads = 1
|
||||
ort.env.wasm.simd = true
|
||||
},
|
||||
positiveSpeechThreshold: 0.6,
|
||||
negativeSpeechThreshold: 0.45,
|
||||
minSpeechMs: 160, // ~5 фреймов по 32мс
|
||||
redemptionMs: 750, // тишина перед onSpeechEnd
|
||||
minSpeechMs: 160,
|
||||
redemptionMs: 750,
|
||||
onSpeechStart: () => {
|
||||
emitLocal('wake', AGENT)
|
||||
},
|
||||
@@ -131,10 +152,10 @@ export default function VoiceController() {
|
||||
vadRef.current = vad
|
||||
vad.start()
|
||||
setState('active')
|
||||
} catch (e) {
|
||||
console.error('[voice] VAD init failed:', e)
|
||||
} catch (e: any) {
|
||||
console.error('[voice] VAD init failed:', e?.name, e?.message, e)
|
||||
setState('error')
|
||||
emitLocal('error', AGENT, 'Микрофон недоступен')
|
||||
emitLocal('error', AGENT, `VAD: ${e?.message?.slice(0, 60) || 'init'}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user