Whisper e TTS na stack: adicionando voz a aplicações sem APIs externas

1. Por que rodar ASR e TTS localmente?

A crescente preocupação com privacidade de dados e conformidade regulatória (LGPD/GDPR) torna a execução local de reconhecimento de fala (ASR) e síntese de voz (TTS) uma alternativa estratégica. Ao processar áudio diretamente no dispositivo, eliminamos o envio de dados sensíveis para servidores de terceiros, reduzimos custos com APIs pagas e garantimos operação em ambientes off-line.

A stack 100% open source formada pelo Whisper (OpenAI) para transcrição e pelo TTS (Coqui, Piper ou Tortoise) para síntese oferece uma solução completa e auditável. Com a maturidade dos modelos otimizados, é possível obter resultados comparáveis a serviços cloud sem depender de conexão com internet.

2. Whisper: transcrição de áudio local com modelos otimizados

O Whisper, desenvolvido pela OpenAI, suporta múltiplos idiomas com detecção automática. Para ambientes de produção, recomenda-se o uso do whisper.cpp, que oferece inferência otimizada para CPU e GPU com quantização.

Instalação do whisper.cpp:

git clone https://github.com/ggerganov/whisper.cpp
cd whisper.cpp
make
bash models/download-ggml-model.sh base.en

Exemplo de transcrição com parâmetros ajustados:

./main -m models/ggml-base.en.bin -f audio.wav -otxt -t 4 -p 2

Parâmetros importantes:
- -t 4: número de threads para processamento paralelo
- -p 2: beam size (2-5 para melhor precisão)
- --prompt "transcrição": contexto inicial para melhorar acurácia

3. Integração do Whisper em aplicações Node.js/Python

Exemplo em Python com whisper.cpp via subprocesso:

import subprocess
import json

def transcribe_audio(file_path):
    result = subprocess.run(
        ['./whisper.cpp/main', 
         '-m', 'models/ggml-base.en.bin',
         '-f', file_path,
         '-oj',  # saída JSON
         '--no-prints'],
        capture_output=True,
        text=True
    )
    return json.loads(result.stdout)

# Uso assíncrono com buffers
audio_buffer = b''
while True:
    chunk = microphone.read(1024)
    audio_buffer += chunk
    if len(audio_buffer) >= 16000 * 5:  # 5 segundos
        with open('temp.wav', 'wb') as f:
            f.write(audio_buffer)
        transcription = transcribe_audio('temp.wav')
        print(transcription['text'])
        audio_buffer = b''

Estratégia de fallback com cache:

cache = {}
def transcribe_with_fallback(file_path):
    if file_path in cache:
        return cache[file_path]

    try:
        result = transcribe_audio(file_path)
        cache[file_path] = result
        return result
    except:
        # Fallback para modelo maior
        result = subprocess.run(
            ['./whisper.cpp/main', '-m', 'models/ggml-small.en.bin', '-f', file_path],
            capture_output=True
        )
        return result

4. TTS local: gerando voz com Coqui AI e Piper

Coqui TTS (modelos VITS): oferece vozes naturais com controle de emoção e velocidade.

# Instalação
pip install TTS

# Exemplo de síntese
from TTS.api import TTS

tts = TTS(model_name="tts_models/en/ljspeech/tacotron2-DDC", progress_bar=False)
tts.tts_to_file(text="Hello, this is a local TTS system.", file_path="output.wav")

# Controle de velocidade e entonação
tts.tts_to_file(
    text="Welcome to our application.",
    file_path="output_slow.wav",
    speed=0.8
)

Piper (modelos compactos): ideal para dispositivos com recursos limitados.

# Download do modelo Piper
wget https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx

# Síntese via linha de comando
echo "Hello world" | piper --model en_US-lessac-medium.onnx --output_file output.wav

5. Orquestração Whisper → TTS em um pipeline de voz

Arquitetura com fila Redis para evitar sobrecarga:

import redis
import subprocess
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def process_audio_pipeline():
    while True:
        # Aguarda novo áudio na fila
        audio_data = r.blpop('audio_queue', timeout=0)

        if audio_data:
            file_path = audio_data[1].decode()

            # Passo 1: Transcrição
            transcription = subprocess.run(
                ['./whisper.cpp/main', '-m', 'models/ggml-base.en.bin', '-f', file_path, '-oj'],
                capture_output=True, text=True
            )
            text = json.loads(transcription.stdout)['text']

            # Passo 2: Síntese
            subprocess.run([
                'python3', '-c', 
                f"from TTS.api import TTS; TTS().tts_to_file('{text}', 'response.wav')"
            ])

            # Passo 3: Reprodução
            subprocess.run(['aplay', 'response.wav'])

# Iniciar pipeline em thread separada
import threading
threading.Thread(target=process_audio_pipeline, daemon=True).start()

6. Otimizações de performance e hardware

Uso de GPU com CUDA para aceleração:

# Whisper.cpp com suporte CUDA
cmake -B build -DWHISPER_CUDA=ON
cmake --build build -j

# Coqui TTS com GPU
tts = TTS(model_name="tts_models/en/ljspeech/tacotron2-DDC", gpu=True)

Quantização para dispositivos edge (Raspberry Pi):

# Modelo Whisper quantizado int8
./models/download-ggml-model.sh base.en
# Já vem com quantização int8 por padrão

# Piper com modelo tiny para Raspberry Pi
wget https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/tiny/en_US-lessac-tiny.onnx

Pré-carregamento de modelos para reduzir latência:

class VoicePipeline:
    def __init__(self):
        # Pré-carrega modelos na inicialização
        self.whisper_model = self.load_whisper('models/ggml-base.en.bin')
        self.tts_model = TTS(model_name="tts_models/en/ljspeech/tacotron2-DDC")

    def load_whisper(self, model_path):
        # Simula carregamento do modelo
        return subprocess.Popen(['./whisper.cpp/main', '-m', model_path, '--no-prints'])

7. Casos de uso práticos e limitações

Casos de uso:
- Assistentes de voz off-line para sistemas embarcados
- Legendagem automática de vídeos com privacidade garantida
- Acessibilidade para pessoas com deficiência visual
- Sistemas de call center com processamento local

Limitações conhecidas:
- Modelos pequenos (tiny/base) têm precisão reduzida em ambientes ruidosos
- Sotaques não representados nos dados de treinamento podem gerar erros
- Vozes sintéticas ainda soam robóticas em comparação com soluções cloud
- Consumo de memória: modelos grandes exigem ~2GB RAM para inferência

Trade-offs recomendados:

# Configuração para baixa latência (uso em tempo real)
Modelo: Whisper tiny.en + Piper tiny
Latência: ~500ms
Precisão: 85-90%

# Configuração para alta qualidade (processamento batch)
Modelo: Whisper small + Coqui VITS
Latência: ~3s
Precisão: 95-98%

Referências