Skip to content

Latest commit

 

History

34 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CLOUD-WHISPER

Голосовой ввод в 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/cuDNNfaster-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, отдельного веб-кадра не нужно.

Установка

  1. Поставь Kinect for Windows SDK v1.8 (даёт Microsoft.Kinect.dll и Kinect-драйверы). Подключи Kinect, убедись, что Windows его видит.
  2. Создай conda-окружение и поставь Python-зависимости:
    conda create -n cloud-whisper python=3.11
    conda activate cloud-whisper
    pip install -r stt-service/requirements.txt
    
    faster-whisper/ctranslate2 тянут свои CUDA-зависимости через pip; отдельно устанавливать CUDA toolkit обычно не нужно, но должен быть актуальный драйвер NVIDIA.
  3. Открой run.bat и поправь путь к conda.bat под свою систему:
    set CONDA_BAT=C:\Users\OLD\anaconda3\condabin\conda.bat
    (обычно <путь-до-anaconda>\condabin\conda.bat).
  4. Мост (kinect-audio-bridge) собирать вручную не обязательно — run.bat делает dotnet build сам при каждом запуске.

Настройка (config/config.yaml)

Секция Поле Что значит
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.

Использование GUI

  • Индикаторы статуса (строка под углом/уверенностью): "мост" — идут ли 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 полосы примерно на одном уровне, при направленной речи ближний к источнику микрофон обычно выше остальных.

Speaker verification (реагировать только на свой голос)

Гейт поверх VAD: эмбеддинг каждой распознанной фразы (NVIDIA TitaNet-Large, CPU) сравнивается по cosine similarity с заранее записанным профилем. Если похожесть ниже similarity_threshold — фраза отбрасывается целиком, даже не доходит до whisper и лога с текстом (только строка [чужой голос, схожесть=...]). Это защищает не только от постороннего голоса рядом с микрофоном, но и экономит GPU — whisper вообще не вызывается на чужой речи.

Запись профиля:

  1. Останови run.bat, если он запущен (пайп cloudwhisper-audio — на один клиент, enroll_speaker.py не сможет подключиться, пока его держит main.py).
  2. Запусти:
    enroll.bat
    
    Прав администратора не требует. Сам поднимет отдельный экземпляр моста и по очереди покажет 5 конкретных фраз для чтения вслух (список — в enroll_speaker.py, PHRASES); после каждой успешно расслышанной фразы печатает "ГОТОВО" и переходит к следующей, а если не расслышал/фраза слишком короткая — просит повторить ту же самую, не пропуская её. В конце усредняет все эмбеддинги в один профиль и сохраняет в config/speaker_profile.npy (файл не в git — это биометрические данные, только локально).
  3. Включи speaker_verification.enabled: true в config/config.yaml.
  4. Запусти 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) и остался финальным выбором.

Face verification (реагировать только когда твоё лицо перед камерой)

Использует ту же физическую 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 секунд, даже если текущий кадр лицо не находит/не совпадает (см. ниже).

Запись профиля:

  1. Останови run.bat, если он запущен (видео-пайп, как и аудио, — однослотовый).
  2. Сядь перед Kinect так, чтобы лицо было видно, и запусти:
    enroll_face.bat
    
    Прав администратора не требует. Сам поднимет отдельный экземпляр моста и раз в секунду проверяет кадр — печатает "ГОТОВО N/5", если лицо найдено уверенно (det_score >= 0.5), иначе просит подвинуться/добавить света. В конце усредняет эмбеддинги в один профиль и сохраняет в config/face_profile.npy (файл не в git — это биометрические данные).
  3. Включи face_verification.enabled: true в config/config.yaml.
  4. Запусти 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 никогда не пройдут эту проверку, у них такого дочернего процесса нет.

Найдя окно, текст вводится в два этапа:

  1. WriteConsoleInputW через AttachConsole — пишет прямо в консольный input-буфер процесса-хозяина найденного окна. Не требует фокуса вообще: работает, даже если активна игра в fullscreen на другом мониторе или окно свёрнуто. Это основной путь (подсмотрено в ../opencode-tts/whisper/whisper_listener.py — там та же задача для другого CLI). Enter отправляется отдельным вызовом WriteConsoleInputW через ~50мс после текста — если слать всё одним пакетом, терминал/CLI распознаёт это как вставку (paste) и трактует Enter внутри неё как перенос строки, а не как отправку.
  2. Если это не сработало — резервный путь: временный перехват фокуса (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 (пакет NuGet NAudio, NAudio.CoreAudioApi/NAudio.Wave) — сырой 4-канальный поток в обход бимформинга, RMS по каждому каналу уходит в cloudwhisper-meta вместе с углом/уверенностью/общим RMS. Если устройство не нашлось при старте — просто лог в stderr, остальной мост продолжает работать, уровни каналов останутся нулевыми.
  • stt-service/ — Python-сервис (conda env cloud-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.txt
    • injector.py — находит окно Claude Code CLI автоматически (по дереву процессов, ищет дочерний claude.exe), пишет текст через WriteConsoleInputW/AttachConsole (без фокуса), с резервным путём через перехват фокуса + SendInput
    • speaker_id.py — voice-эмбеддинги (NVIDIA TitaNet-Large, nemo_toolkit, CPU) и cosine similarity для speaker verification; профиль хранится в config/speaker_profile.npy
    • enroll_speaker.py — автономная запись голосового профиля (см. раздел "Speaker verification" выше), запускается через enroll.bat
    • nvafx_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.npy
    • enroll_face.py — автономная запись профиля лица (см. раздел "Face verification" выше), запускается через enroll_face.bat
    • status.py — статус для индикаторов GUI (whisper/speaker/face/Claude-окно), пишет main.py в config/runtime_status.json, читает gui.py
    • gui.py — оверлей на PySide6 + pyqtgraph (чёрно-фиолетовая тема): график RMS-уровня в реальном времени, угол/уверенность луча, 4 вертикальные полосы уровня по сырым каналам микрофонного массива, ползунок ручной чувствительности, ползунок усиления микрофона (дБ), кнопка MUTE. График RMS показывает сырой уровень с моста (до gain) — это чтение отдельного meta-пайпа, gain на него не влияет, только на то, что реально уходит в VAD/whisper
    • meta_client.py — клиент meta-пайпа, парсит angle;confidence;rms;ch1;ch2;ch3;ch4
    • main.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

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 изначально спроектирован под потоковую обработку (видеозвонки в реальном времени) — того же класса проблем не возникло.

Почему faster-whisper (large-v3-turbo)

  • Уже проверено на этом железе и голосе пользователя в 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) вместо постоянного прослушивания.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages