Голосовой ввод в Claude Code CLI через микрофонный массив Kinect (Xbox 360). Говоришь — распознанный текст сам находит окно Claude Code и печатается туда, независимо от того, что было в фокусе (даже игра в fullscreen на другом мониторе).
- Слушает через Kinect (аппаратный бимформинг + эхоподавление + шумоподавление на уровне железа), режет поток на фразы по паузам (VAD), распознаёт речь.
- Два STT-движка на выбор, переключаются на лету кнопкой в GUI (выбор
запоминается между перезапусками): faster-whisper (
large-v3-turbo, CUDA) и GigaAM-v3 (Sber, Conformer, RNNT, CPU) — специализированная под русский язык модель, на общих бенчмарках обходит whisper large-v3, но не панацея: на редких топонимах/терминах в склонённой форме путается так же, как whisper (проверено вживую на "Сухой Лог" → "сухом лагу" у обеих моделей одинаково). GigaAM не отдаёт confidence — если выбран, фразы считаются уверенными всегда (VAD/speaker/face-гейты и так уже отсеяли тишину/чужой голос/не то лицо). - Сам находит окно Claude Code CLI среди всех открытых окон (по дочернему
процессу
claude.exe) — не нужно ничего кликать/фокусировать вручную. - Печатает распознанный текст прямо в консольный буфер найденного окна через
WriteConsoleInputW, без смены фокуса. Если так не получилось — резервный путь: временно перехватывает фокус, печатает, возвращает фокус обратно. - Печатает и нажимает Enter автоматически, но только если распознавание
уверенное (по
avg_logprobот whisper) — неуверенные фразы не печатаются вообще (не просто без Enter), чтобы недопечатанный текст не смешивался со следующей фразой в поле ввода. - Фильтрует типичные "галлюцинации" whisper (фразы вроде "Продолжение следует...", "Субтитры сделал DimaTorzok" и т.п.).
- Шумоподавление NVIDIA Maxine Audio Effects SDK (нейросеть, GPU, опционально) — по-настоящему потоковая обработка кадрами по 10мс, чистит сырой сигнал ещё ДО RMS-гейта (см. "Шумоподавление NVIDIA Maxine" ниже — до этого пробовали DeepFilterNet2, но та модель не даёт честного стриминга по маленьким чанкам).
- Speaker verification (нужен записанный профиль, см. ниже) — сравнивает голос каждой фразы с заранее записанным профилем пользователя (NVIDIA TitaNet-Large, cosine similarity) и полностью игнорирует чужие голоса рядом с микрофоном — фраза даже не доходит до whisper, если похожесть ниже порога.
- Face verification (нужен записанный профиль, см. ниже) — та же идея, но по видео с той же Kinect: непрерывный гейт по лицу перед камерой (InsightFace, cosine similarity). Нет лица в кадре или лицо не совпадает с профилем — фраза игнорируется целиком, даже если голос прошёл speaker verification.
- GUI-оверлей (чёрно-фиолетовая тема): график уровня сигнала в реальном времени, угол и уверенность луча (куда "смотрит" бимформинг), 4 вертикальные полосы уровня по каждому сырому микрофону массива (см. "Уровни по 4 микрофонам" ниже), ползунок ручной чувствительности (энергетический гейт поверх webrtcvad), ползунок усиления микрофона (программный gain в дБ с мягким tanh-насыщением вместо жёсткого клиппинга на пиках — меньше шумовых артефактов при перегрузе, без искажений на 0 дБ по умолчанию), кнопка MUTE, и строка индикаторов "мост / whisper / speaker ID / face ID / TTS (ElevenLabs) / Claude CLI" — видно, что реально работает, а что ещё не поднялось или не найдено.
- Мост (C#) переживает падение/перезапуск Python-стороны — не держит сенсор занятым, не требует переоткрытия Kinect при рестарте GUI/STT.
Важно: для распознавания речи (STT) используется один уже смикшанный канал
после аппаратного бимформинга Kinect SDK (KinectAudioSource) — это единственный
поток, который управляемый API SDK отдаёт для этой цели. Но физически
микрофонный массив дополнительно виден в Windows как обычное 4-канальное
WASAPI-устройство ("Набор микрофонов (Kinect USB Audio)", 16кГц, 32-bit
float) — мост открывает его отдельным, независимым захватом в обход
бимформинга SDK, только чтобы показать в GUI уровень каждого из 4 микрофонов
по отдельности (см. "Уровни по 4 микрофонам" ниже). В сам STT/VAD/whisper
пайплайн этот сырой поток не идёт.
Kinect (4 mic array + RGB-камера, физически)
-> KinectAudioSource (beamforming + AEC + noise suppression) [C#]
-> ColorStream (640x480 Bgr32) [C#]
-> WASAPI-захват "Kinect USB Audio" напрямую (4ch, 16кГц, float32,
независимо от KinectAudioSource выше — только для метрики) [C#]
-> named pipe \\.\pipe\cloudwhisper-audio (16kHz mono PCM) -> stt-service
-> named pipe \\.\pipe\cloudwhisper-meta (angle;confidence;rms;
rms-по-4-микрофонам, ~10Hz) -> gui.py
-> named pipe \\.\pipe\cloudwhisper-video (сырые Bgr32-кадры) -> stt-service
[Python]
-> face verification (опционально, фоновый поток, непрерывно): эмбеддинг
лица в кадре (InsightFace) против сохранённого профиля — лица нет или
не совпадает? гейт закрыт, ниже фразы игнорируются целиком
-> gain: программное усиление PCM (ползунок в GUI, дБ), мягкое tanh-насыщение
вместо жёсткого клиппинга при перегрузе — по умолчанию 0 дБ (не трогает сигнал)
-> денойз NVIDIA Maxine (опционально, потоково по кадрам 10мс, GPU —
см. раздел про денойз ниже)
-> VAD: webrtcvad + ручной RMS-гейт (ползунок в GUI, или MUTE — совсем глушит)
-> speaker verification (опционально): эмбеддинг фразы (NVIDIA TitaNet-Large)
против сохранённого профиля — не тот голос? фраза отброшена, дальше не идёт
-> faster-whisper (large-v3-turbo, CUDA) + фильтр галлюцинаций
-> текст, помечен "уверенный"/"неуверенный" по avg_logprob
-> найти окно Claude Code CLI (по дочернему процессу claude.exe)
-> WriteConsoleInputW напрямую в его консольный буфер, без фокуса;
если не вышло — резервно: перехват фокуса + SendInput, с возвратом
фокуса туда, где он был
-> Enter — только для "уверенных" фраз, отдельным вызовом чуть позже текста
(иначе терминал распознаёт всё как вставку и не отправляет)
- Windows — весь инжект (
WriteConsoleInputW,AttachConsole,SendInput) завязан на Windows-специфичные API, на другой ОС не работает. - Kinect for Xbox 360 (сам сенсор + блок питания/USB-адаптер, если это не версия для Xbox One/Kinect for Windows v2 — код рассчитан на SDK v1.8).
- Kinect for Windows SDK v1.8 — установлен отдельно, даёт
Microsoft.Kinect.dll(путь зашит вkinect-audio-bridge/KinectAudioBridge.csproj:C:\Program Files\Microsoft SDKs\Kinect\v1.8\Assemblies\Microsoft.Kinect.dll). - .NET SDK (для сборки моста, target
net48, платформаx86— Kinect SDK v1.8 нативно x86) и командаdotnetв PATH. - NVIDIA GPU + CUDA/cuDNN —
faster-whisperв конфиге запущен сdevice: cuda,compute_type: float16. Без GPU можно переключить наdevice: cpuвconfig/config.yaml, ноlarge-v3-turboна CPU заметно медленнее. - NVIDIA Audio Effects SDK (опционально, для шумоподавления) — GPU с
Tensor Cores (Turing и новее), ставится отдельным инсталлятором (см.
"Шумоподавление NVIDIA Maxine" ниже). Без него
denoise.enabledв конфиге просто не сработает (лог-предупреждение), остальной пайплайн не пострадает. - Conda (Anaconda/Miniconda) — Python-сервис живёт в отдельном env
cloud-whisper, Python 3.11. - Права администратора — обязательны для запуска
run.bat(см. ниже, почему).enroll.bat(запись голосового профиля) их не требует. nemo_toolkit[asr](для speaker verification, модель TitaNet-Large) — тяжёлая зависимость (torch, pytorch-lightning, transformers и т.п.), несколько GB на диск при установке; веса модели (~100МБ) кешируются в~/.cache/torch/NeMo/, не в проекте.insightface/onnxruntime/opencv-python(для face verification) — веса модели buffalo_l (~280МБ) кешируются в~/.insightface/models/, не в проекте. Использует ту же RGB-камеру Kinect, отдельного веб-кадра не нужно.
- Поставь Kinect for Windows SDK v1.8 (даёт
Microsoft.Kinect.dllи Kinect-драйверы). Подключи Kinect, убедись, что Windows его видит. - Создай conda-окружение и поставь Python-зависимости:
conda create -n cloud-whisper python=3.11 conda activate cloud-whisper pip install -r stt-service/requirements.txtfaster-whisper/ctranslate2тянут свои CUDA-зависимости через pip; отдельно устанавливать CUDA toolkit обычно не нужно, но должен быть актуальный драйвер NVIDIA. - Открой
run.batи поправь путь кconda.batпод свою систему:(обычноset CONDA_BAT=C:\Users\OLD\anaconda3\condabin\conda.bat
<путь-до-anaconda>\condabin\conda.bat). - Мост (
kinect-audio-bridge) собирать вручную не обязательно —run.batделаетdotnet buildсам при каждом запуске.
| Секция | Поле | Что значит |
|---|---|---|
pipe |
name, sample_rate, channels, sample_width_bytes |
Параметры именованного канала от моста; менять не нужно, если не менял C#-сторону |
whisper |
model |
Модель faster-whisper (large-v3-turbo проверена на русском голосе/железе) |
device |
cuda или cpu |
|
compute_type |
float16 (GPU) — при cpu обычно нужен int8 |
|
language |
Язык распознавания (ru) |
|
gigaam |
enabled |
Загружать ли GigaAM вторым движком (переключение — кнопкой в GUI, см. ниже) |
model |
Версия модели (v3_e2e_rnnt — с пунктуацией и нормализацией текста) |
|
device |
cpu (torch в этом env без CUDA, как и у nemo/TitaNet) |
|
vad |
aggressiveness |
0–3, строгость webrtcvad; 3 оказался слишком строгим для звука Kinect (не пропускал речь) |
min_speech_ms / max_silence_ms |
Минимальная длина фразы / тишина, после которой фраза считается завершённой | |
denoise |
enabled |
Включить шумоподавление NVIDIA Maxine (см. раздел ниже) — если SDK не найден/GPU не поддерживается, отключается сам с предупреждением в лог |
injector |
enabled |
Выключить весь автоввод, оставив только распознавание в лог |
window_whitelist.cli_child_process |
Имя процесса CLI, по дочернему которому ищется окно (claude.exe) |
|
press_enter_after_text |
Слать Enter автоматически после уверенных фраз | |
min_avg_logprob_for_autosend |
Порог avg_logprob, ниже которого фраза считается неуверенной (текст не печатается вообще) |
|
speaker_verification |
enabled |
Включить проверку голоса (нужен записанный профиль, см. ниже) |
similarity_threshold |
Порог cosine similarity 0..1; ниже — чужой голос, фраза отброшена ещё до whisper | |
face_verification |
enabled |
Включить проверку лица (нужен записанный профиль, см. ниже) |
video_pipe_name |
Имя видео-пайпа моста (cloudwhisper-video) |
|
similarity_threshold |
Порог cosine similarity; ниже — лицо не совпадает, фраза игнорируется | |
check_interval_s |
Как часто (в секундах) пересчитывать эмбеддинг лица в фоновом потоке | |
grace_period_s |
Сколько секунд после последнего подтверждённого кадра гейт остаётся открытым, даже если лицо сейчас не видно/не совпадает — иначе обычный поворот головы (второй монитор) резал бы голос на каждом повороте |
Чувствительность (энергетический гейт) и усиление микрофона настраиваются не
в config.yaml, а "на лету" ползунками в GUI — сохраняются в
config/runtime_sensitivity.txt / config/runtime_gain.txt (не в git).
run.bat
Одна команда собирает мост, поднимает его, GUI, STT-сервис и TTS-сервер
озвучки (каждый в своём окне/фоном), и останавливает всё при выходе. Первый
запуск скачает модель large-v3-turbo (~1.5GB, кешируется). Открой Claude
Code CLI до или после запуска — окно ищется автоматически при каждой
распознанной фразе, порядок запуска не важен.
Озвучка ответов Claude (TTS) — run.bat проверяет, поднят ли уже
TTS-сервер ElevenLabs на 127.0.0.1:4321 (тот же сервер и голос, что у
opencode-flow), и поднимает его сам, если нет — без окна, в фоне. Реально
озвучивание срабатывает через .claude/settings.json этого проекта (Stop-хук
запускает ../opencode-tts/claude-code/speak_hook.py на каждый мой ответ) —
работает только пока ты в этой директории с Claude Code CLI, не глобально.
Хук сам поднимет TTS-сервер при необходимости, даже если run.bat не
запущен — но запуск в run.bat избавляет от задержки на первом ответе.
Требует прав администратора. AttachConsole получает ACCESS_DENIED,
если CLOUD-WHISPER не elevated, а Claude Code CLI запущен от администратора —
Windows не даёт процессу с более низким уровнем целостности цепляться к
консоли процесса с более высоким. run.bat сам запрашивает UAC при старте,
если ещё не elevated.
- Индикаторы статуса (строка под углом/уверенностью): "мост" — идут ли
meta-обновления от KinectAudioBridge.exe прямо сейчас (GUI определяет это
сам, по свежести последнего пакета); "whisper (large-v3-turbo)" — загружена
ли модель в stt-сервисе; "GigaAM" — загрузился ли второй STT-движок (см.
"STT-движок" ниже); "денойз (NVIDIA Maxine)" — реально ли заработал
NvAFX (не просто
enabled: trueв конфиге — если SDK не найден или GPU не поддерживается, будет красным даже при включённом конфиге, смотриlogs/stt-service.logза причиной); "speaker ID (TitaNet-Large)" — включена ли проверка голоса; "face ID (InsightFace)" — включена ли проверка лица; "TTS (ElevenLabs)" — жив ли TTS-сервер озвучки (127.0.0.1:4321/health, GUI опрашивает его сам напрямую, раз в 2с — сервер живёт вне main.py, поднимает егоrun.batили Stop-хук Claude Code); "Claude CLI" — найдено ли сейчас окно Claude Code CLI. Названия моделей подставляются автоматически изconfig/runtime_status.json. Зелёная точка — работает, красная — не работает/не найдено, серая (пустой кружок) — данных ещё не было (сервис не запущен или только стартует). Все, кроме "моста" и "TTS", — изconfig/runtime_status.json, который пишетmain.py; если stt-сервис не запущен, они всегда серые, даже если мост работает. Обрати внимание: "face ID"/"speaker ID" показывают, что проверка включена и настроена, а не то, что лицо/голос прямо сейчас совпадают. - Строка "лицо: ..." (под углом/уверенностью) — живой результат
face-гейта: "лицо в фокусе · схожесть 0.XX" (зелёный), "лицо не совпадает"
/ "лицо: не в кадре" (красный), "недавно подтверждено (grace)" (жёлтый —
лица сейчас не видно, но гейт ещё открыт по
grace_period_s). Обновляется каждые ~0.5с вместе с фоновым потоком вmain.py. Для speaker verification такого живого индикатора нет (проверяется только в момент фразы, не непрерывно) — его результат смотри вlogs/stt-service.log. - График уровня — RMS сырого сигнала с моста (до применения gain) в реальном времени, плюс пунктирная линия текущего порога чувствительности.
- Угол / уверенность — куда сейчас "смотрит" бимформинг Kinect и с какой уверенностью (это данные моста, не whisper).
- Ползунок чувствительности — порог RMS, ниже которого кадр считается тишиной/шумом, даже если webrtcvad сказал "речь". Подними, если ловятся посторонние шумы; опусти, если тихая речь не распознаётся.
- Ползунок усиления микрофона (−12…+24 дБ) — программное усиление PCM перед VAD/whisper. На 0 дБ ничего не меняет. Полезно, если Kinect стоит далеко или голос звучит тихо для распознавания.
- MUTE — полностью глушит микрофон (STT перестаёт даже пытаться распознавать), не останавливая процессы.
- STT-движок — переключатель Whisper/GigaAM. Оба движка загружены в
память сразу при старте
main.py(переключение мгновенное, без задержки на загрузку модели), выбор сохраняется вconfig/runtime_engine.txtи переживает перезапуск. Если GigaAM не загрузился (см. индикатор GigaAM выше) — main.py тихо остаётся на whisper, даже если кнопка переключена. - Уровни по 4 микрофонам — 4 вертикальные полосы под графиком, по одной на
каждый физический микрофон массива. Это отдельный сырой WASAPI-захват
устройства "Набор микрофонов (Kinect USB Audio)" (4 канала, 16кГц, 32-bit
float), который мост открывает параллельно с
KinectAudioSource— чисто для наглядности, в распознавание речи не участвует. Полезно, чтобы увидеть, какой микрофон массива реально ловит голос (например, если Kinect повёрнут боком) — при равномерном фоновом шуме все 4 полосы примерно на одном уровне, при направленной речи ближний к источнику микрофон обычно выше остальных.
Гейт поверх VAD: эмбеддинг каждой распознанной фразы (NVIDIA TitaNet-Large,
CPU) сравнивается по cosine similarity с заранее записанным профилем. Если
похожесть ниже similarity_threshold — фраза отбрасывается целиком, даже не
доходит до whisper и лога с текстом (только строка [чужой голос, схожесть=...]).
Это защищает не только от постороннего голоса рядом с микрофоном, но и
экономит GPU — whisper вообще не вызывается на чужой речи.
Запись профиля:
- Останови
run.bat, если он запущен (пайпcloudwhisper-audio— на один клиент,enroll_speaker.pyне сможет подключиться, пока его держитmain.py). - Запусти:
Прав администратора не требует. Сам поднимет отдельный экземпляр моста и по очереди покажет 5 конкретных фраз для чтения вслух (список — в
enroll.batenroll_speaker.py,PHRASES); после каждой успешно расслышанной фразы печатает "ГОТОВО" и переходит к следующей, а если не расслышал/фраза слишком короткая — просит повторить ту же самую, не пропуская её. В конце усредняет все эмбеддинги в один профиль и сохраняет вconfig/speaker_profile.npy(файл не в git — это биометрические данные, только локально). - Включи
speaker_verification.enabled: trueвconfig/config.yaml. - Запусти
run.batкак обычно.
Если enabled: true, а профиля нет — сервис сам это заметит при старте,
напишет предупреждение в лог и продолжит работать без проверки голоса
(чтобы не заблокировать себя случайно), пока профиль не будет записан.
Порог similarity_threshold (по умолчанию 0.40, откалиброван вживую) можно
подстроить: если свой голос иногда отбрасывается как "чужой" — понизь; если
чужие голоса иногда проходят — подними. Смотри реальные значения похожести в
logs/stt-service.log — у TitaNet-Large на этом голосе/железе свой голос
(обычная речь/тихо/шёпот) обычно 0.49-0.59, фон/чужой — до 0.40.
Важно: шкала сходства своя у каждой модели эмбеддингов — при смене модели порог нужно пересчитывать заново по реальным логам, а не переносить старое число. По пути пробовали Resemblyzer (GE2E) и SpeechBrain ECAPA-TDNN — на коротких фразах (1-3 сек) через бимформинг Kinect у ECAPA-TDNN разделение получилось смазанным, у Resemblyzer — чище, но TitaNet-Large на практике показал ещё более стабильный отрыв (свой голос 0.49-0.59 против фона до 0.40) и остался финальным выбором.
Использует ту же физическую Kinect, что и микрофон — kinect-audio-bridge
теперь ещё и включает ColorStream (640x480 RGB) и стримит сырые кадры во
второй пайп (cloudwhisper-video), независимо от аудио-пайпа. В отличие от
speaker verification (проверяется только в момент фразы), это непрерывный
фоновый гейт: отдельный поток в main.py раз в check_interval_s секунд
берёт кадр, ищет лицо (InsightFace: RetinaFace-детекция + ArcFace-эмбеддинг),
сравнивает с профилем по cosine similarity и обновляет общий флаг. Основной
цикл проверяет этот флаг на каждой фразе — если лица нет в кадре или оно не
совпадает с профилем, фраза игнорируется целиком, даже если голос успешно
прошёл speaker verification.
Ограничение по ракурсу: и RetinaFace-детекция, и особенно ArcFace-эмбеддинг
рассчитаны в основном на фронтальные лица — надёжное распознавание держится
примерно до ±30-45° поворота головы, дальше схожесть проседает. Это реальная
проблема, если, например, CLI открыт на втором мониторе и голова обычно
повёрнута туда, а не в камеру. Решение — grace_period_s: если лицо только
что было подтверждено, гейт остаётся открытым ещё grace_period_s секунд,
даже если текущий кадр лицо не находит/не совпадает (см. ниже).
Запись профиля:
- Останови
run.bat, если он запущен (видео-пайп, как и аудио, — однослотовый). - Сядь перед Kinect так, чтобы лицо было видно, и запусти:
Прав администратора не требует. Сам поднимет отдельный экземпляр моста и раз в секунду проверяет кадр — печатает "ГОТОВО N/5", если лицо найдено уверенно (
enroll_face.batdet_score >= 0.5), иначе просит подвинуться/добавить света. В конце усредняет эмбеддинги в один профиль и сохраняет вconfig/face_profile.npy(файл не в git — это биометрические данные). - Включи
face_verification.enabled: trueвconfig/config.yaml. - Запусти
run.batкак обычно.
Если enabled: true, а профиля нет — сервис сам это заметит при старте,
напишет предупреждение в лог и продолжит работать без проверки лица.
Порог similarity_threshold (по умолчанию 0.35) не откалиброван на
реальном лице — в отличие от голосового порога, это отправная точка, а не
проверенное значение. Смотри реальные значения похожести в
logs/stt-service.log и подстрой так же, как делали для голоса.
grace_period_s (по умолчанию 10 секунд) — сколько времени гейт остаётся
открытым после последнего подтверждённого кадра, даже если сейчас лицо не
видно/повёрнуто. В логе это видно как [лицо в grace-периоде (недавно подтверждено), схожесть=...] вместо обычного [лицо совпадает, ...]; в GUI
— жёлтая строка "недавно подтверждено (grace)" вместо зелёной. Если работаешь
подолгу боком к камере — можно увеличить; если хочется строже — уменьшить
или поставить 0 (тогда гейт закрывается сразу же, как лицо пропадает из
кадра).
injector.py находит окно Claude Code CLI автоматически: перебирает все
видимые top-level окна и ищет то, у чьего процесса-хозяина есть дочерний
claude.exe (сам бинарник CLI, имя настраивается в config.yaml как
cli_child_process). Никакой ручной привязки PID не нужно — мост/GUI/run.bat
никогда не пройдут эту проверку, у них такого дочернего процесса нет.
Найдя окно, текст вводится в два этапа:
WriteConsoleInputWчерезAttachConsole— пишет прямо в консольный input-буфер процесса-хозяина найденного окна. Не требует фокуса вообще: работает, даже если активна игра в fullscreen на другом мониторе или окно свёрнуто. Это основной путь (подсмотрено в../opencode-tts/whisper/whisper_listener.py— там та же задача для другого CLI). Enter отправляется отдельным вызовомWriteConsoleInputWчерез ~50мс после текста — если слать всё одним пакетом, терминал/CLI распознаёт это как вставку (paste) и трактует Enter внутри неё как перенос строки, а не как отправку.- Если это не сработало — резервный путь: временный перехват фокуса
(
SetForegroundWindowс обходомForegroundLockTimeoutчерез имитацию нажатия Alt) +SendInput, с возвратом фокуса туда, где он был.
kinect-audio-bridge/— C# (.NET Framework 4.8, x86) процесс. Открывает Kinect, включаетAudioSourceс бимформингом (стримит PCM вcloudwhisper-audio, угол/уверенность/RMS — вcloudwhisper-meta) иColorStream(640x480 Bgr32, стримит сырые кадры вcloudwhisper-video— для face verification).ColorStream.Enable()вызывается доsensor.Start()— иначе Kinect SDK v1.8 его не подхватывает. Держит сенсор открытым всё время жизни процесса и переживает разрыв/переподключение любого из клиентов — не требует перезапуска, если Python-сторона упала или была перезапущена. ДополнительноStartMicArrayCaptureоткрывает то же физическое устройство ещё раз напрямую через WASAPI (пакет NuGetNAudio,NAudio.CoreAudioApi/NAudio.Wave) — сырой 4-канальный поток в обход бимформинга, RMS по каждому каналу уходит вcloudwhisper-metaвместе с углом/уверенностью/общим RMS. Если устройство не нашлось при старте — просто лог в stderr, остальной мост продолжает работать, уровни каналов останутся нулевыми.stt-service/— Python-сервис (conda envcloud-whisper, Python 3.11):pipe_client.py/meta_client.py/video_client.py— клиенты пайпов моста (аудио, meta, видео соответственно)vad.py— webrtcvad + ручной RMS-гейт (порог изsensitivity.py)sensitivity.py/mute_state.py/gain.py— общие runtime-флаги между GUI (пишет) и STT (читает), через файлыconfig/runtime_sensitivity.txt,config/runtime_mute.txtиconfig/runtime_gain.txt.gain.pyприменяет усиление к PCM перед VAD/whisper (tanh-насыщение вместо клиппинга на пиках)whisper_engine.py— faster-whisper + фильтр галлюцинаций (no_speech_prob/avg_logprob + блок-лист известных фраз-артефактов вроде "Продолжение следует...", "Субтитры сделал DimaTorzok")gigaam_engine.py— второй STT-движок (GigaAM-v3, CPU), тот же интерфейсEngine.transcribe(), что уwhisper_engine.py. Не зовётmodel.transcribe(path)(гоняет ffmpeg подпроцессом на каждый вызов) — строит тензор из уже готового PCM16 в памяти напрямуюengine_select.py— какой STT-движок сейчас выбран (GUI пишет, main.py читает), тот же файловый паттерн, что gain.py/sensitivity.py —config/runtime_engine.txtinjector.py— находит окно Claude Code CLI автоматически (по дереву процессов, ищет дочернийclaude.exe), пишет текст черезWriteConsoleInputW/AttachConsole(без фокуса), с резервным путём через перехват фокуса +SendInputspeaker_id.py— voice-эмбеддинги (NVIDIA TitaNet-Large, nemo_toolkit, CPU) и cosine similarity для speaker verification; профиль хранится вconfig/speaker_profile.npyenroll_speaker.py— автономная запись голосового профиля (см. раздел "Speaker verification" выше), запускается черезenroll.batnvafx_denoise.py— шумоподавление NVIDIA Maxine Audio Effects SDK, ctypes-обёртка надNVAudioEffects.dll(см. раздел "Шумоподавление NVIDIA Maxine" ниже); потоково, кадрами по 10мс, с буферизацией остатка между вызовами — не привязано к границам чанков из пайпаface_id.py— детекция+эмбеддинг лица (InsightFace buffalo_l, CPU) и cosine similarity для face verification; профиль хранится вconfig/face_profile.npyenroll_face.py— автономная запись профиля лица (см. раздел "Face verification" выше), запускается черезenroll_face.batstatus.py— статус для индикаторов GUI (whisper/speaker/face/Claude-окно), пишетmain.pyвconfig/runtime_status.json, читаетgui.pygui.py— оверлей на PySide6 + pyqtgraph (чёрно-фиолетовая тема): график RMS-уровня в реальном времени, угол/уверенность луча, 4 вертикальные полосы уровня по сырым каналам микрофонного массива, ползунок ручной чувствительности, ползунок усиления микрофона (дБ), кнопка MUTE. График RMS показывает сырой уровень с моста (до gain) — это чтение отдельного meta-пайпа, gain на него не влияет, только на то, что реально уходит в VAD/whispermeta_client.py— клиент meta-пайпа, парситangle;confidence;rms;ch1;ch2;ch3;ch4main.py— оркестратор (face-гейт (фоновый поток) -> pipe -> gain -> денойз -> VAD -> speaker-гейт -> whisper -> инжектор). Пишет лог в UTF-8 в../logs/stt-service.logнапрямую (в обход кодировки консоли Windows, которая иначе калечит кириллицу в выводе)
config/config.yaml— модель whisper, VAD, порог уверенности для авто-Enter, имя дочернего CLI-процесса для поиска окна.run.bat— одна команда: сборка + запуск моста, GUI и STT-сервиса вместе.enroll.bat— запись голосового профиля для speaker verification, без прав администратора, отдельно отrun.bat.enroll_face.bat— запись профиля лица для face verification, без прав администратора, отдельно отrun.bat.
Даже с фильтром галлюцинаций и порогом уверенности
(min_avg_logprob_for_autosend в config.yaml, по умолчанию -0.5) фоновый
разговор (не обращённый к Claude), сказанный достаточно чётко, может пройти
как уверенная речь и уйти в чат — фильтры отсекают тишину/шум/галлюцинации, а
не "не то адресовано". Если рядом кто-то говорит — снижай чувствительность
ползунком в GUI, жми MUTE, или отключай press_enter_after_text в
config.yaml перед тем как оставить систему без присмотра.
AttachConsole/ACCESS_DENIED, текст не вводится — CLOUD-WHISPER запущен не от администратора, а Claude Code CLI — от администратора (или наоборот несовпадение уровня целостности). Перезапустиrun.bat— он сам просит UAC.- Окно Claude Code CLI не найдено — проверь, что CLI реально запущен и
его процесс называется так же, как в
injector.window_whitelist.cli_child_process(config.yaml). Дочерний процесс должен называтьсяclaude.exe. - Текст печатается, но Enter не отправляется — маловероятно при текущей
версии (Enter шлётся отдельным вызовом), но если воспроизвелось — смотри
logs/stt-service.log, там логируетсяavg_logprobкаждой фразы и любые ошибкиWriteConsoleInputW. - Речь не распознаётся / распознаётся с ошибками — подвигай ползунок
чувствительности и усиления в GUI; проверь
logs/stt-service.logна предмет того, что фразы вообще доходят до whisper (см.> <текст> (avg_logprob=...)). - webrtcvad не пропускает речь вообще —
aggressiveness: 3слишком строгий для звука Kinect (уже подобрано2в конфиге по умолчанию). - Face verification включена, но фразы всегда игнорируются — проверь
logs/stt-service.logна[лицо не найдено/не совпадает — игнорирую]. Возможные причины: лицо реально не в кадре (Kinect направлен не туда, или слишком темно для RGB-камеры — она хуже переносит темноту, чем ИК-глубина); порогsimilarity_thresholdслишком высокий для этого освещения/ракурса (профиль писался в других условиях); либо и вовсе никто пока не тестировал модель на реальном лице (порог 0.35 — не откалиброванная отправная точка, см. раздел "Face verification").
NVIDIA Maxine Audio Effects SDK (эффект denoiser, модель
denoiser_16k.trtpkg) — нейросетевое шумоподавление на GPU, по-настоящему
потоковое: C-API (NvAFX_Run()) обрабатывает кадры ровно по 160 сэмплов
(10мс на 16кГц) за вызов, без батч-режима вообще — это единственный способ
вызова API, поэтому не может быть расхождения "поток даёт другой результат,
чем целый файл", как бывает у моделей, спроектированных для обработки
файлов целиком. Применяется к сырому потоку до RMS-гейта (vad.py) —
задержка максимум один кадр (~10мс), не мешает VAD реагировать на речь.
stt-service/nvafx_denoise.py — ctypes-обёртка над NVAudioEffects.dll
(своей Python-обвязки у SDK нет). Буферизует остаток аудио, не кратный
160 сэмплам, между вызовами process_chunk() — не привязано к тому, какого
размера чанки реально приходят из пайпа моста.
Установка: SDK не входит в репозиторий и ставится отдельно —
скачать инсталлятор под своё поколение GPU с
nvidia.com/broadcast-sdk-resources
(например ..._Ampere.exe для RTX 30xx) и запустить его — установит
NVAudioEffects.dll и модели в C:\Program Files\NVIDIA Corporation\NVIDIA Audio Effects SDK\ (путь зашит в
nvafx_denoise.DEFAULT_SDK_DIR). Нужен GPU с Tensor Cores (Turing и
новее). Если SDK не найден или GPU не поддерживается — nvafx_denoise.init()
логирует предупреждение и денойз просто отключается, весь остальной
пайплайн продолжает работать как обычно.
Почему не DeepFilterNet2 (пробовали раньше): DeepFilterNet2 —
батч-модель (enhance() рассчитан на целый файл за один вызов), с
внутренним GRU-состоянием, которое сбрасывается на каждый вызов, и
lookahead в 2 кадра — то есть честный поток по маленьким чанкам
потребовал бы патчить внутренности чужой архитектуры. После нескольких
фиксов (перенос GRU-состояния, буфер-придержка lookahead, своя
нормализация уровня — в Python-биндинге libdf.erb_norm/unit_norm
оказался баг, не переносящий state между вызовами) поток всё равно
давал приемлемое качество только при задержке ≥500мс на чанк — при такой
задержке смысл "успеть денойзить до RMS-гейта" пропадает. К тому же сама
библиотека официально не поддерживает реалтайм для v2 (Rust/tract-движок
явно bail!-ит на этой версии модели, только DeepFilterNet3). NVIDIA
Maxine изначально спроектирован под потоковую обработку (видеозвонки в
реальном времени) — того же класса проблем не возникло.
- Уже проверено на этом железе и голосе пользователя в
Open-LLM-VTuber(faster_whisper,large-v3-turbo,language: ru,device: cuda) — работает точно, минимум ошибок. - CTranslate2-бэкенд быстрее и легче по VRAM, чем оригинальный
openai-whisper(важно — GPU RTX 3080 10GB общий с другими локальными моделями). - Нативно ставится под Windows (без WSL), в отличие от Qwen3-TTS/F5-TTS, которым потребовался WSL из-за flash-attn/CUDA-нестыковок.
- Будящее слово (Porcupine/openWakeWord) вместо постоянного прослушивания.