Мне нужен был голосовой ввод. Не встроенная в ОС диктовка, не облачный API — а простая и главное локальная штука: зажал клавишу, сказал, отпустил, текст появился в активном окне. 

Готовых решений, которые бы устроили, сходу не нашёл. Сделал свое.  Может, кому будет полезно.

Как устроено

Микрофон → Whisper (локально) → LLM-трансформация (опционально) → вставка в активное окно

Whisper транскрибирует. Ollama (у меня gemma3:12b) обрабатывает результат — что именно делает LLM, целиком определяется промптом. Дефолтный убирает слова-паразиты, расставляет пунктуацию, поправляет регистр. Но через кастомный промпт можно переформатировать текст в email, перевести на другой язык, привести к стилю документации — короче, как настроите. Оба шага работают локально. LLM-трансформацию можно отключить, тогда вставляется сырой текст от Whisper.

Платформы

Рассчитано на GPU. Один самодостаточный скрипт на платформу.

Платформа

Whisper-бэкенд

GPU

Windows / Linux

faster-whisper

NVIDIA CUDA

macOS

mlx-whisper

Apple Silicon (Metal)

Два скрипта вместо одного — осознанное решение. Разным платформам нужны разные бэкенды Whisper, разные механизмы вставки, разные аудио-особенности. Каждый скрипт — самодостаточный, ~400–560 строк, читается за 10 минут. Идея в том чтобы, просмотрев один файл + список зависимостей, можно было валидировать код, который слушает ваш микрофон.

Несколько деталей, которые оказались важны

Пребуфер. Если начать запись по нажатию клавиши, первое слово часто теряется — человек начинает говорить раньше, чем палец дожимает кнопку, микрофон не сразу оживает и так далее. Поэтому микрофон пишет в кольцевой буфер постоянно (последние 0.5 секунды), и при старте записи этот буфер подклеивается в начало. Первое слово не обрезается.

Буфер обмена. Вставка текста через Ctrl+V / Cmd+V требует положить текст в буфер обмена. Но там может быть что-то важное. Поэтому есть три политики: restore (вернуть старое содержимое после вставки), clearpreserve. По умолчанию — restore.

Горячая клавиша. По умолчанию Alt (Windows/Linux) или Option (macOS). Дефолты удобны, но могут конфликтовать — стоит переназначить на что-то вроде Pause или комбинацию. У меня стоит Pause. Push-to-talk: зажал — говоришь — отпустил — текст пошёл на транскрипцию.

Скорость

На NVIDIA GPU 16GB (large-v3 + gemma3:12b) и Apple Silicon M3 16GB (large-v3-turbo + qwen2.5:14b) — результаты схожие:

Этап

Время

Whisper

1–3 с

LLM-трансформация

1–3 с

Итого

2–6 с

Большие пассажи подольше, но всё ещё юзабельно.

Где использую

В основном — для набора сообщений в чатах и AI-ассистентах (Cursor, ChatGPT, Slack). Фокус на текстовое поле, зажал клавишу, сказал, отпустил — текст вставился, Enter отправился. Настройка KEYS_AFTER_PASTE позволяет отправить любую клавишу после вставки (или не отправлять ничего).

Попробовать

git clone https://github.com/sancau/whisper-ptt.git
cd whisper-ptt
python -m venv venv
source venv/bin/activate
pip install -r requirements-cuda.txt           # или requirements-apple-silicon.txt
cp .env.example-cuda .env                      # или .env.example-apple-silicon
python whisper_ptt_cuda.py                     # или whisper_ptt_apple_silicon.py

Для преобладания русского языка (в эпизодами английского) у меня в .env настроено вот так:

WHISPER_PTT_WHISPER_LANGUAGE=ru
WHISPER_PTT_WHISPER_INITIAL_PROMPT=Russian speech with possible English tech terms.

Ссылки