Artigo

Transcrição de Áudio com Whisper: do Áudio ao Texto em Python

Transcrição de Áudio com Whisper: do Áudio ao Texto em Python
Tempo de leitura9 minutos

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_prompt com 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 ffmpeg no 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.

Anterior e próximo

Leituras recentes

Temas parecidos para continuar navegando no blog.

Conteúdo criado para leitura limpa, boa hierarquia visual e navegação clara.