feat(voice): wake-word «Космо» в браузере (Шаг 3)
All checks were successful
Deploy / deploy (push) Successful in 6m33s

openWakeWord pipeline на onnxruntime-web прямо на планшете. Цепочка:
mic (16kHz, AudioWorklet) → melspectrogram.onnx → embedding_model.onnx
(sliding 76-frame window, stride 8) → cosmo.onnx → score 0..1.

Триггер при score≥0.5 → запускается тот же VAD-flow что и push-to-talk.

- public/wake/ — cosmo.onnx (custom-trained на голос Даниила) +
  melspectrogram.onnx + embedding_model.onnx (~2.9MB вместе).
- lib/wake-word.ts — WakeWordDetector class. ort грузится через
  <script src=/vad/ort.wasm.min.js> на клиенте — обход проблемы next-swc
  с парсингом import.meta.url в onnxruntime-web .mjs билдах.
- VoiceController: тап = активация (нужен для AudioContext user-gesture),
  далее непрерывное слушание wake-word; на детект → MicVAD флоу.
  Долгий тап = выкл. Ручной тап остаётся как fallback.

После деплоя Python-агент на .103 не нужен — можно архивировать
home-voice-assistant. На .103 остаётся только ElevenLabs прокси :8888.
This commit is contained in:
Cosmo
2026-04-27 09:43:53 +00:00
parent 96bd846a08
commit 522d36d1a2
10 changed files with 1567 additions and 53 deletions

View File

@@ -0,0 +1,23 @@
// AudioWorklet, который буферизует входной поток и шлёт чанки по 1280 семплов
// (80 мс при 16kHz) в main thread. Любая входная длина допустима — буферизуется.
class WakeCaptureProcessor extends AudioWorkletProcessor {
constructor() {
super()
this.buf = new Float32Array(0)
this.target = 1280
}
process(inputs) {
const ch = inputs[0] && inputs[0][0]
if (!ch || ch.length === 0) return true
const merged = new Float32Array(this.buf.length + ch.length)
merged.set(this.buf)
merged.set(ch, this.buf.length)
this.buf = merged
while (this.buf.length >= this.target) {
this.port.postMessage(this.buf.slice(0, this.target))
this.buf = this.buf.slice(this.target)
}
return true
}
}
registerProcessor('wake-capture', WakeCaptureProcessor)