fix(voice): preload VAD один раз — мгновенная реакция после «Космо»
All checks were successful
Deploy / deploy (push) Successful in 1m36s
All checks were successful
Deploy / deploy (push) Successful in 1m36s
В логах было видно: между wake-trigger и реальным VAD recording проходило 1-2с (Loading VAD... → finished loading → started micVAD). Каждый cancel дополнительно destroy'ил VAD, и следующий wake снова ждал инициализацию. Теперь: - VAD создаётся один раз в paused-режиме сразу после wake.start() (в фоне, не блокирует UI). - На каждый wake → vad.start() мгновенно. - onSpeechEnd → vad.pause() (был implicit pause; явно ставим). - voice-cancel → vad.pause(), а не destroy. Wake продолжает слушать. - destroy только при полном выключении ассистента.
This commit is contained in:
@@ -69,13 +69,11 @@ export default function VoiceController() {
|
||||
useEffect(() => {
|
||||
console.log('[VoiceController] mounted, state=idle, ждём тап на микрофон')
|
||||
|
||||
// Кнопка X в overlay шлёт voice-cancel → прерываем активную запись фразы,
|
||||
// но wake-word оставляем слушать в фоне (если он включён).
|
||||
// Кнопка X в overlay шлёт voice-cancel → ставим VAD на паузу
|
||||
// (НЕ destroy — иначе следующий wake снова будет ждать 1-2с на инициализацию).
|
||||
const onCancel = () => {
|
||||
console.log('[voice] cancel — прерываю запись')
|
||||
console.log('[voice] cancel — пауза VAD')
|
||||
try { vadRef.current?.pause?.() } catch {}
|
||||
try { vadRef.current?.destroy?.() } catch {}
|
||||
vadRef.current = null
|
||||
busyRef.current = false
|
||||
try { wakeRef.current?.resume?.() } catch {}
|
||||
setState((s) => (wakeRef.current ? 'listening' : 'idle'))
|
||||
@@ -125,14 +123,18 @@ export default function VoiceController() {
|
||||
emitLocal('error', AGENT, 'Не получилось')
|
||||
} finally {
|
||||
busyRef.current = false
|
||||
// После обработки — возвращаем wake-режим (если активен)
|
||||
// VAD на паузу — переиспользуем при следующем wake (без re-init).
|
||||
try { vadRef.current?.pause?.() } catch {}
|
||||
// Wake возобновляем — снова слушаем фоном.
|
||||
try { wakeRef.current?.resume?.() } catch {}
|
||||
setState((s) => (s === 'busy' ? 'listening' : s))
|
||||
}
|
||||
}
|
||||
|
||||
// Создание VAD по запросу: либо после wake-детекта, либо после ручного тапа.
|
||||
const startVAD = async () => {
|
||||
// Однократная инициализация VAD. Создаётся в paused-состоянии и переиспользуется
|
||||
// на каждый wake — без этого пауза до первой записи ~1-2с.
|
||||
const initVAD = async () => {
|
||||
if (vadRef.current) return
|
||||
try {
|
||||
const { MicVAD } = await import('@ricky0123/vad-web')
|
||||
const vad = await MicVAD.new({
|
||||
@@ -147,16 +149,15 @@ export default function VoiceController() {
|
||||
negativeSpeechThreshold: 0.45,
|
||||
minSpeechMs: 160,
|
||||
redemptionMs: 750,
|
||||
onSpeechStart: () => {
|
||||
emitLocal('wake', AGENT)
|
||||
},
|
||||
onSpeechStart: () => emitLocal('wake', AGENT),
|
||||
onSpeechEnd: handleSpeechEnd,
|
||||
})
|
||||
vadRef.current = vad
|
||||
vad.start()
|
||||
// Не вызываем start — ждём пока wake-word триггернёт.
|
||||
console.log('[voice] VAD preloaded (paused)')
|
||||
} catch (e: any) {
|
||||
console.error('[voice] VAD init failed:', e?.name, e?.message, e)
|
||||
setState('error')
|
||||
// Не вырубаем wake — может на ручной trigger ещё попробуем
|
||||
emitLocal('error', AGENT, `VAD: ${e?.message?.slice(0, 60) || 'init'}`)
|
||||
}
|
||||
}
|
||||
@@ -168,12 +169,9 @@ export default function VoiceController() {
|
||||
try { wakeRef.current?.pause?.() } catch {}
|
||||
setState('recording')
|
||||
emitLocal('wake', AGENT)
|
||||
// Если VAD ещё не готов — создаём; иначе reset+start.
|
||||
if (!vadRef.current) {
|
||||
await startVAD()
|
||||
} else {
|
||||
try { vadRef.current.start?.() } catch {}
|
||||
}
|
||||
// VAD должен быть уже preloaded — мгновенный старт.
|
||||
if (!vadRef.current) await initVAD()
|
||||
try { vadRef.current?.start?.() } catch {}
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
@@ -215,6 +213,10 @@ export default function VoiceController() {
|
||||
await wake.start()
|
||||
wakeRef.current = wake
|
||||
setState('listening')
|
||||
|
||||
// Прелоадим VAD в фоне — после первого wake реакция будет мгновенной,
|
||||
// вместо +1-2с на загрузку Silero VAD.
|
||||
initVAD().catch((e) => console.warn('[voice] VAD preload failed', e))
|
||||
} catch (e: any) {
|
||||
console.error('[wake] init failed:', e)
|
||||
setState('error')
|
||||
|
||||
Reference in New Issue
Block a user