Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых. API.. API. voicemail.. API. voicemail. Машинное обучение.. API. voicemail. Машинное обучение. обработка звука.. API. voicemail. Машинное обучение. обработка звука. разработка open source.

Каждый из нас хоть раз получал голосовое сообщение на 5 минут, которое проще было бы прочитать за 30 секунд. А если таких сообщений – целая папка? Я написал open-source инструмент voice-to-text, который умеет массово расшифровывать голосовые из Telegram, WhatsApp и других мессенджеров. В статье расскажу про архитектуру, подводные камни Whisper и сравнение четырех бэкендов транскрипции.

Зачем это нужно

У проекта было три причины:

1. Практическая потребность. У моей супруги танцевальная студия, и клиенты с тренерами постоянно присылают голосовые – вопросы по расписанию, обсуждение мероприятий, организационные моменты. Слушать их неудобно: нельзя быстро пробежаться глазами, найти нужный момент или процитировать. Telegram Desktop позволяет экспортировать данные — и вот у тебя папка с сотнями .ogg файлов. Штатных средств для массовой расшифровки нет.

2. Развитие портфолио. Я развиваю свои проекты на GitHub для работы со звуком (распознавание музыки, очистка аудиокниг, конвертация). Voice-to-text — логичное дополнение этой линейки.

3. Расширение экспертизы. Хотелось глубже погрузиться в речевые технологии, сравнить разные подходы к транскрипции (локальные модели vs API), разобраться с оптимизацией inference через CTranslate2 и понять, как правильно работать с аудиоформатами на низком уровне.

Задачи, которые я хотел решить:

  • Расшифровать папку с 200+ голосовыми за одну команду

  • Поддержать форматы всех популярных мессенджеров (OGG Opus, M4A, AMR)

  • Дать выбор между скоростью и качеством

  • Получить чистое Python API для интеграции в свои проекты

Архитектура

Проект состоит из трёх основных модулей:

voice_to_text/
├── transcriber.py      # Ядро: 4 бэкенда транскрипции
├── formats.py          # Детекция форматов, конвертация
├── cli.py              # CLI интерфейс (Click + Rich)
└── utils.py            # Утилиты (хеши, форматирование)
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 1

Ключевое архитектурное решение — абстрактный базовый класс BaseTranscriber с единым интерфейсом:

class BaseTranscriber(ABC):
    @abstractmethod
    def transcribe(self, audio_path: Path, language: str | None = None) -> TranscriptionResult:
        ...

    @abstractmethod
    def transcribe_batch(self, audio_paths: list[Path], language: str | None = None) -> Iterator[tuple[Path, TranscriptionResult]]:
        ...
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 2

Это позволяет безболезненно переключаться между бэкендами — фабрика create_transcriber() создаёт нужную реализацию:

transcriber = create_transcriber(
    backend=Backend.FASTER_WHISPER,
    model_size=ModelSize.SMALL
)
# Или API:
transcriber = create_transcriber(backend=Backend.GROQ, api_key="...")
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 3

Четыре бэкенда: зачем так много

1. OpenAI Whisper (локальный)

Оригинальная модель от OpenAI. Работает честно, но медленно на CPU. На GPU — нормально, но требует CUDA и приличный VRAM.

class WhisperTranscriber(BaseTranscriber):
    def __init__(self, model_size=ModelSize.BASE, device="auto"):
        import whisper
        import torch
        if device == "auto":
            device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = whisper.load_model(model_size.value, device=device)
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 4

Основной подводный камень: Whisper нативно не работает с OGG Opus (формат Telegram). Нужно конвертировать через pydub/ffmpeg. Я добавил автоматическую конвертацию несовместимых форматов:

def _ensure_compatible(self, path: Path) -> Path:
    suffix = path.suffix.lower()
    if suffix in {".wav", ".mp3", ".m4a", ".flac"}:
        return path  # Whisper справится сам
    if suffix in {".ogg", ".opus", ".webm", ".amr"}:
        return self._convert_to_wav(path)
    return path
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 5

2. faster-whisper (рекомендуемый)

Использует CTranslate2 — оптимизированный runtime для трансформеров. Даёт 4x ускорение на CPU при идентичном качестве. Поддерживает VAD-фильтрацию (Voice Activity Detection), что убирает тишину и ускоряет обработку:

segments, info = self.model.transcribe(
    str(audio_path),
    language=language,
    beam_size=5,
    vad_filter=True,  # Пропускает тишину
)
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 6

На моём тесте (100 голосовых, ~45 минут суммарно):

Бэкенд

Модель

Время

CPU

whisper

base

12 мин

i7-12700

faster-whisper

base

3 мин

i7-12700

faster-whisper

small

7 мин

i7-12700

3. OpenAI API

Для тех, у кого нет GPU и нет желания ждать. $0.006 за минуту аудио. Удобно для разовых задач.

4. Groq API

Относительно новый игрок. Предоставляет бесплатный tier с щедрыми лимитами (~2000 запросов/день). Скорость транскрипции — впечатляющая, примерно 10x от реалтайма. Отличный вариант для тестирования и небольших проектов.

Есть нюанс — лимит на размер файла 25 МБ:

class GroqTranscriber(BaseTranscriber):
    MAX_FILE_SIZE = 25 * 1024 * 1024

    def transcribe(self, audio_path, language=None):
        file_size = audio_path.stat().st_size
        if file_size > self.MAX_FILE_SIZE:
            raise ValueError(f"File too large: {file_size / 1024 / 1024:.1f} MB")
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 7

Детекция форматов: не доверяй расширениям

Один из неочевидных моментов — файлы из мессенджеров не всегда имеют правильное расширение. Telegram экспортирует .ogg, WhatsApp — .opus или .m4a, но внутри может быть что угодно.

Я реализовал детекцию по magic bytes с фоллбеком на расширение:

MAGIC_BYTES = {
    b"OggS": AudioFormat.OGG_OPUS,
    b"ID3":  AudioFormat.MP3,
    b"RIFF": AudioFormat.WAV,
    b"fLaC": AudioFormat.FLAC,
    b"x1aExdfxa3": AudioFormat.WEBM,
    b"#!AMR": AudioFormat.AMR,
}

def detect_format(file_path: Path) -> AudioFormat:
    with open(file_path, "rb") as f:
        header = f.read(32)

    for magic, fmt in MAGIC_BYTES.items():
        if header.startswith(magic):
            if fmt == AudioFormat.OGG_OPUS:
                return _detect_ogg_codec(header)  # Opus vs Vorbis
            return fmt

    # M4A/AAC — особый случай (ftyp box)
    if b"ftyp" in header[:12]:
        if b"M4A" in header or b"mp42" in header:
            return AudioFormat.M4A
        return AudioFormat.AAC

    return _format_from_extension(file_path)  # Фоллбек
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 8

Отдельно различаю OGG Opus и OGG Vorbis — это разные кодеки в одном контейнере, и для конвертации это важно.

CLI с прогресс-баром

Для CLI использую связку Click + Rich. Получается информативный вывод с прогрессом:

$ vtt transcribe ./telegram_voices --batch -l ru -m small -o result.json -f json
Found 47 voice files

Transcribing... ━━━━━━━━━━━━━━━━━━━━ 100% voice_047.ogg

┌─────────────────┬──────────┬──────┬──────────────────────────────┐
│ File            │ Duration │ Lang │ Preview                      │
├─────────────────┼──────────┼──────┼──────────────────────────────┤
│ voice_001.ogg   │ 12.3s    │ ru   │ Привет, я хотел сказать...   │
│ voice_002.ogg   │ 45.1s    │ ru   │ Слушай, по поводу встречи... │
│ ...             │          │      │                              │
└─────────────────┴──────────┴──────┴──────────────────────────────┘

Total duration: 1847.3s (30.8 min)
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 9

SRT-экспорт для подкастов

Неочевидное применение — генерация субтитров. Whisper возвращает сегменты с таймкодами, и из них легко собрать SRT:

def to_srt(self) -> str:
    if not self.segments:
        return ""
    lines = []
    for i, seg in enumerate(self.segments, 1):
        start = self._format_timestamp(seg["start"])
        end = self._format_timestamp(seg["end"])
        lines.append(f"{i}")
        lines.append(f"{start} --> {end}")
        lines.append(seg["text"].strip())
        lines.append("")
    return "n".join(lines)
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 10
vtt transcribe podcast.mp3 -m medium -f srt -o podcast.srt
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 11

На выходе — готовый .srt, который можно загрузить в YouTube, VLC или любой видеоредактор.

Что пошло не так: уроки

1. Temp-файлы — тихий убийца диска. При конвертации OGG → WAV создаются временные файлы. Если транскрипция упала с exception — файл остаётся. При батч-обработке 200 файлов это может съесть гигабайты. Решение — try/finally с unlink() и опция cleanup=True.

2. Модели Whisper кэшируются в ~/.cache/whisper. При первом запуске модель скачивается — это может быть 1.5 ГБ для medium. В Docker-образе нужно предзагружать модель в build-time, иначе каждый запуск контейнера — это скачивание заново.

3. GPU vs CPU — не всегда очевидный выбор. Для коротких голосовых (< 30 сек) overhead на загрузку данных в GPU может быть больше, чем выигрыш. faster-whisper на CPU с int8 квантизацией часто быстрее, чем оригинальный Whisper на GPU для коротких аудио.

4. Определение языка стоит дорого. Если вы знаете язык заранее — всегда указывайте его явно. Автодетекция языка добавляет ~30% времени к транскрипции и иногда ошибается (особенно на коротких фрагментах с числами или именами).

Выбор модели: практические рекомендации

Задача

Модель

Бэкенд

Голосовые < 1 мин

base

faster-whisper

Голосовые на русском

small

faster-whisper

Подкасты, лекции

medium

faster-whisper + GPU

Много файлов, не критично качество

base

Groq API

Нужен best quality

large-v3

faster-whisper + GPU

Планы

Проект в активной разработке. Ближайшие планы:

  • Docker-образ с предзагруженной моделью

  • Telegram-бот для real-time расшифровки

Как попробовать

git clone https://github.com/raxod/voice-to-text
cd voice-to-text
pip install -r requirements.txt
pip install faster-whisper

# Расшифровать один файл
python -m voice_to_text.cli transcribe voice.ogg -l ru

# Расшифровать папку
python -m voice_to_text.cli transcribe ./voices --batch -o result.json -f json

# Через Groq (бесплатно)
export GROQ_API_KEY=gsk_...
python -m voice_to_text.cli transcribe voice.ogg -b groq
Пишем свой voice-to-text на Python: 4 бэкенда и батч-обработка голосовых - 12

Проект на GitHub: voice-to-text
Экосистема: audiotools.dev

Автор: formeo

Источник

Rambler's Top100