Transcrever áudio manualmente é um dos trabalhos mais tediosos que existem — e o Whisper, modelo de reconhecimento de fala da OpenAI, resolve isso com qualidade impressionante. Neste guia mostro como usar Whisper em Python para transcrever áudios e reuniões, gerar legendas com timestamps e integrar o texto a um LLM para resumir.
O melhor: você pode rodar tudo localmente, sem enviar seus áudios para terceiros.
O que é o Whisper e por que usá-lo
- Multilíngue: reconhece dezenas de idiomas, inclusive português.
- Robusto: lida bem com sotaques e ruído moderado.
- Local: roda na sua máquina, preservando privacidade.
- Open source: modelos de vários tamanhos, do tiny ao large.
Escolhendo o modelo certo
O tamanho do modelo define o equilíbrio entre velocidade e precisão:
| Modelo | Velocidade | Precisão | Uso ideal |
|---|---|---|---|
| tiny/base | Muito rápida | Baixa | Rascunho, CPU |
| small | Rápida | Boa | Uso geral |
| medium | Média | Muito boa | Reuniões |
| large | Lenta | Excelente | Máxima qualidade, GPU |
Instalando
python -m venv .venv && source .venv/bin/activate
pip install openai-whisper
# para manipular audio
pip install ffmpeg-python
Você também precisa do ffmpeg instalado no sistema — o Whisper usa para decodificar áudio.
Transcrição básica
import whisper
model = whisper.load_model("small") # ou "medium", "large"
resultado = model.transcribe("reuniao.mp3", language="pt")
print(resultado["text"])
Pronto: texto completo do áudio. O parâmetro language="pt" força o português e melhora a precisão.
Transcrição com timestamps
Para legendas ou para saber quem falou quando, use os segmentos:
resultado = model.transcribe("video.mp4", language="pt")
for seg in resultado["segments"]:
inicio = seg["start"]
fim = seg["end"]
print(f"[{inicio:6.2f} -> {fim:6.2f}] {seg['text'].strip()}")
Gerando legendas SRT
Convertendo os segmentos para o formato SRT, você cria legendas prontas para o vídeo:
def formatar_tempo(t: float) -> str:
h, m = divmod(int(t), 3600)
m, s = divmod(m, 60)
ms = int((t - int(t)) * 1000)
return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"
def gerar_srt(segmentos, caminho="saida.srt"):
with open(caminho, "w", encoding="utf-8") as f:
for i, seg in enumerate(segmentos, 1):
f.write(f"{i}\n")
f.write(f"{formatar_tempo(seg['start'])} --> {formatar_tempo(seg['end'])}\n")
f.write(seg["text"].strip() + "\n\n")
gerar_srt(resultado["segments"])
Processando áudios longos em blocos
Reuniões de horas podem estourar a memória. Divida o áudio em partes de alguns minutos e transcreva separadamente.
from pydub import AudioSegment
def dividir(caminho, minutos=10):
audio = AudioSegment.from_file(caminho)
passo = minutos * 60 * 1000
partes = []
for i in range(0, len(audio), passo):
p = f"parte_{i//passo}.mp3"
audio[i:i+passo].export(p, format="mp3")
partes.append(p)
return partes
Usando GPU
Com GPU NVIDIA e PyTorch com CUDA, a transcrição fica muito mais rápida:
model = whisper.load_model("medium", device="cuda")
Alternativa mais rápida ainda: faster-whisper, que usa CTranslate2 e costuma ser várias vezes mais veloz com precisão equivalente.
Integrando com um LLM para resumir
A transcrição bruta é longa. Um LLM transforma em resumo acionável:
from openai import OpenAI
client = OpenAI()
texto = resultado["text"][:12000]
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Resuma em topicos e liste acoes:\n{texto}"}],
)
print(resp.choices[0].message.content)
Processando em lote com fila
Quando você tem dezenas de áudios para transcrever, processar um a um é lento. Uma fila simples com um worker por vez evita estourar a memória e permite retomar do ponto onde parou.
from pathlib import Path
import json
def transcrever_pendentes(pasta="audios", saida="transcricoes"):
Path(saida).mkdir(exist_ok=True)
for audio in Path(pasta).glob("*.mp3"):
destino = Path(saida) / (audio.stem + ".json")
if destino.exists():
continue # ja processado
r = model.transcribe(str(audio), language="pt")
destino.write_text(json.dumps(r, ensure_ascii=False), encoding="utf-8")
print("ok:", audio.name)
Otimizando com faster-whisper
O faster-whisper reimplementa o modelo com CTranslate2, ficando várias vezes mais rápido e usando menos memória. A API é parecida:
from faster_whisper import WhisperModel
model = WhisperModel("medium", device="cuda", compute_type="float16")
segmentos, info = model.transcribe("reuniao.mp3", language="pt")
for seg in segmentos:
print(seg.start, seg.end, seg.text)
Para grandes volumes, essa troca é praticamente obrigatória.
Melhorando a precisão
- Use
initial_promptcom nomes e termos do seu domínio. - Reduza ruído do áudio antes de transcrever.
- Prefira taxas de amostragem padronizadas (16 kHz).
- Divida por silêncios, não por tempo fixo, quando possível.
resultado = model.transcribe(
"reuniao.mp3", language="pt",
initial_prompt="Termos: FastAPI, LangChain, RAG, embeddings.",
)
Legendas e tradução
Além de transcrever, o Whisper pode traduzir para inglês com a tarefa translate. Para outros idiomas, combine a transcrição com um tradutor:
resultado = model.transcribe("video.mp4", task="translate")
print(resultado["text"]) # texto em ingles
Quando vale usar a nuvem
Rodar localmente é ótimo para privacidade e volume, mas exige hardware. Se você transcreve pouco e não tem GPU, uma API de transcrição pode sair mais barata que manter uma máquina. A regra prática: volume baixo e esporádico favorece a nuvem; volume alto e recorrente favorece o local.
Integrando a um pipeline de conteúdo
Transcrição é o primeiro passo. Um pipeline completo transforma áudio em vários formatos de conteúdo:
def pipeline(audio_path: str):
r = model.transcribe(audio_path, language="pt")
texto = r["text"]
resumo = resumir_com_llm(texto)
return {"transcricao": texto, "resumo": resumo, "srt": gerar_srt(r["segments"])}
Erros comuns
- Usar o modelo tiny esperando precisão alta.
- Esquecer de instalar o
ffmpegno sistema. - Transcrever áudio muito longo de uma vez e estourar a memória.
- Não fixar o idioma, fazendo o modelo traduzir indevidamente.
- Enviar áudio sensível para serviços externos sem necessidade.
- Reprocessar tudo quando um único arquivo falha.
Comparando modelos de transcrição
Além do Whisper original e do faster-whisper, existem alternativas. Vale testar no seu próprio áudio antes de decidir:
| Ferramenta | Velocidade | Precisão PT | Observação |
|---|---|---|---|
| whisper | Média | Muito boa | Referência, simples de usar |
| faster-whisper | Alta | Muito boa | Mesma precisão, muito mais rápido |
| APIs de nuvem | Alta | Excelente | Custo por minuto, privacidade menor |
Pré-processando o áudio
Um pouco de limpeza melhora bastante o resultado. Normalizar volume e converter para mono 16 kHz reduz ruído e acelera o processamento.
from pydub import AudioSegment
def preparar(caminho: str) -> str:
audio = AudioSegment.from_file(caminho).set_channels(1).set_frame_rate(16000)
audio = audio.normalize()
saida = caminho.rsplit(".", 1)[0] + "_limpo.wav"
audio.export(saida, format="wav")
return saida
Extraindo falantes (diarização)
Para reuniões, saber quem falou é essencial. Bibliotecas de diarização identificam os falantes e você combina com os timestamps do Whisper para montar a ata.
Fluxo:
1) diarizacao -> trechos por falante
2) whisper -> transcricao por trecho
3) juncao -> "Falante 1: ...", "Falante 2: ..."
Transcrevendo podcasts e lives
Podcasts e lives longas são um caso ideal: geram texto pesquisável, posts e clipes. O fluxo é transcrever, dividir em blocos temáticos e derivar conteúdo.
def episodio_para_conteudo(audio: str):
r = model.transcribe(audio, language="pt")
blocos = []
for seg in r["segments"]:
if len(seg["text"].strip()) > 80:
blocos.append(seg["text"].strip())
return {"transcricao": r["text"], "blocos": blocos}
Erros de reconhecimento em português
Alguns problemas são típicos do português e têm solução simples:
- Nomes próprios: corrija com
initial_prompt. - Números: o modelo pode escrever “dois” ou “2” — normalize depois.
- Siglas: adicione-as ao prompt para evitar erros.
- Gírias regionais: revise e padronize manualmente.
def normalizar_numeros(texto: str) -> str:
import re
return re.sub(r"\b(\d+)\b", lambda m: m.group(1), texto)
Automatizando a criação de conteúdo a partir de áudio
Um uso poderoso é transformar uma reunião ou aula em vários formatos: transcrição completa, resumo executivo, lista de ações e até um rascunho de post. Tudo a partir de um único áudio.
def gerar_formatos(texto: str) -> dict:
return {
"resumo": chamar_llm(f"Resuma em 5 topicos:\n{texto}"),
"acoes": chamar_llm(f"Liste as acoes e responsaveis:\n{texto}"),
"post": chamar_llm(f"Escreva um rascunho de post:\n{texto}"),
}
Conclusão e próximos passos
Whisper é o ponto de entrada de muitas automações de conteúdo. Comece transcrevendo um único arquivo, ganhe confiança no resultado e só depois monte o pipeline completo com resumo e diarização.
Estimando custo antes de transcrever
Transcrição em nuvem cobra por minuto. Antes de processar um acervo grande, calcule o custo e compare com a opção local.
def estimar_custo(horas: float, preco_minuto: float) -> float:
return horas * 60 * preco_minuto
print(f"R$ {estimar_custo(50, 0.006):.2f} para 50 horas")
Qualidade por tipo de áudio
| Tipo de áudio | Dificuldade | Dica |
|---|---|---|
| Voz limpa (estúdio) | Baixa | Modelo small já basta |
| Reunião online | Média | Use medium e initial_prompt |
| Ambiente com ruído | Alta | Pré-processe e use large |
| Múltiplos falantes | Alta | Combine com diarização |
Transcrição em tempo real (streaming)
Para legendas ao vivo, processe em janelas curtas de áudio conforme chegam. Não é tão preciso quanto processar o arquivo completo, mas serve para acompanhar uma fala.
import sounddevice as sd, numpy as np, whisper
model = whisper.load_model("base")
def callback(indata, frames, time, status):
audio = np.squeeze(indata).astype(np.float32)
resultado = model.transcribe(audio, language="pt", fp16=False)
print(resultado["text"])
with sd.InputStream(callback=callback, samplerate=16000, channels=1):
input("Gravando... pressione Enter para parar")
Armazenando transcrições de forma pesquisável
Guardar apenas texto puro limita o uso. Salve com metadados (data, duração, participantes) em um banco ou em arquivos JSON, para depois buscar por palavra-chave.
def salvar_com_metadados(r, origem: str, duracao: float):
return {
"origem": origem,
"duracao_s": duracao,
"texto": r["text"],
"segmentos": r["segments"],
}
Resumo
Whisper transforma horas de áudio em texto pesquisável em minutos, localmente e sem custo por minuto. Comece com o modelo small, ajuste para medium ou large quando a precisão importar, e integre um LLM para resumir. É uma das automações com melhor retorno de tempo.
