Phase 06 - Lesson 11

Processamento de Audio em Tempo Real

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Pipelines em lote processam um arquivo. Pipelines em tempo real processam os proximos 20 milissegundos antes que os proximos 20 cheguem. Toda IA conversacional, estudio de transmissao e bot de telefonia vive ou morre por esse orcamento de latencia.

Tipo: Build Linguagens: Python, Rust Pre-requisitos: Fase 6 · 02 (Espectrogramas), Fase 6 · 04 (ASR), Fase 6 · 07 (TTS) Tempo: ~75 minutos

O Problema

Voce quer um assistente de voz que pareca vivo. A latencia humana de revezamento de turnos em uma conversa e de ~230 ms (do silencio a resposta). Qualquer coisa acima de 500 ms soa robotica; acima de 1500 ms parece quebrada. O orcamento para um loop completo de ouvir → entender → responder → falar em 2026 e:

Estagio Orcamento
Microfone → buffer 20 ms
VAD 10 ms
ASR (streaming) 150 ms
LLM (primeiro token) 100 ms
TTS (primeiro chunk) 100 ms
Renderizar → alto-falante 20 ms
Total ~400 ms

O Moshi (Kyutai, 2024) cravou 200 ms em full-duplex. O GPT-4o-realtime (2024) crava ~320 ms. Pipelines em cascata em 2022 entregavam a 2500 ms. A melhoria de 10x veio de tres tecnicas: (1) streaming em todos os pontos, (2) pipelining assincrono com resultados parciais, (3) geracao interrompivel.

O Conceito

Pipeline de audio em streaming com ring buffer, gate de VAD e interrupcao

Frame / chunk / janela. O audio em tempo real flui como blocos de tamanho fixo. Escolha comum: 20 ms (320 amostras a 16 kHz). Tudo a jusante precisa acompanhar essa cadencia.

Ring buffer. Buffer circular de tamanho fixo. A thread produtora escreve novos frames, a thread consumidora le. Evita alocacoes no caminho critico. Tamanho ≈ latencia-maxima × taxa-de-amostragem; um ring de 2 segundos a 16 kHz = 32.000 amostras.

VAD (Deteccao de Atividade de Voz). Bloqueia o trabalho a jusante quando ninguem esta falando. O Silero VAD 4.0 (2024) roda em <1 ms por frame de 30 ms na CPU. O webrtcvad e a alternativa mais antiga.

ASR em streaming. Modelos que emitem transcricoes parciais a medida que o audio chega. O Parakeet-CTC-0.6B em modo streaming (NeMo, 2024) atinge 2-5% de WER com 320 ms de latencia. O Whisper-Streaming (Macháček et al., 2023) fatia o Whisper para quase-streaming com latencia de ~2 s.

Interrupcao. Quando o usuario fala enquanto o assistente esta falando, voce precisa (a) detectar o barge-in, (b) parar o TTS, (c) descartar o restante da saida do LLM. Tudo dentro de 100 ms, ou o usuario percebe um assistente surdo.

Transporte WebRTC Opus. Frames de 20 ms, 48 kHz, bitrate adaptativo de 8-128 kbps. Padrao para navegador e mobile. LiveKit, Daily.co e Pion sao as stacks de 2026 para construir apps de voz.

Jitter buffer. Pacotes de rede chegam fora de ordem / atrasados. O jitter buffer reordena e suaviza; pequeno demais → falhas audiveis, grande demais → latencia. 60-80 ms e o tipico.

Pegadinhas comuns

  • Contencao de threads. O GIL do Python + modelos pesados podem deixar a thread de audio sem recursos. Use uma biblioteca de audio com callback em C (sounddevice, PortAudio) e mantenha o Python fora do caminho critico.
  • Latencia de conversao de taxa de amostragem. Reamostrar dentro do pipeline adiciona 5-20 ms. Reamostre antecipadamente ou use um reamostrador de latencia zero (PolyPhase, soxr_hq).
  • Priming do TTS. Ate TTS rapidos como o Kokoro tem um aquecimento de 100-200 ms na primeira requisicao. Faca cache do modelo e aqueca-o com uma execucao fictícia antes do primeiro turno real.
  • Cancelamento de eco. Sem AEC, a saida do TTS reentra no microfone e dispara o ASR na propria voz do bot. O WebRTC AEC3 e o padrao open-source.

Construa

Passo 1: ring buffer

import collections

class RingBuffer:
    def __init__(self, capacity):
        self.buf = collections.deque(maxlen=capacity)
    def write(self, frame):
        self.buf.extend(frame)
    def read(self, n):
        return [self.buf.popleft() for _ in range(min(n, len(self.buf)))]
    def level(self):
        return len(self.buf)

A capacidade determina a latencia maxima de buffering. 32.000 amostras a 16 kHz = 2 s.

Passo 2: gate de VAD

def simple_energy_vad(frame, threshold=0.01):
    return sum(x * x for x in frame) / len(frame) > threshold ** 2

Substitua pelo Silero VAD em producao:

import torch
vad, _ = torch.hub.load("snakers4/silero-vad", "silero_vad")
is_speech = vad(torch.tensor(frame), 16000).item() > 0.5

Passo 3: ASR em streaming

# Parakeet-CTC-0.6B streaming via NeMo
from nemo.collections.asr.models import EncDecCTCModelBPE
asr = EncDecCTCModelBPE.from_pretrained("nvidia/parakeet-ctc-0.6b")
# chunk_ms=320 ms, look_ahead_ms=80 ms
for chunk in audio_stream():
    partial_text = asr.transcribe_streaming(chunk)
    print(partial_text, end="\r")

Passo 4: handler de interrupcao

class Dialog:
    def __init__(self):
        self.tts_task = None

    def on_user_speech(self, frame):
        if self.tts_task and not self.tts_task.done():
            self.tts_task.cancel()   # barge-in
        # then feed to streaming ASR

    def on_final_user_utterance(self, text):
        self.tts_task = asyncio.create_task(self.reply(text))

    async def reply(self, text):
        async for tts_chunk in llm_then_tts(text):
            speaker.write(tts_chunk)

Depende de I/O assincrono e de streaming de TTS cancelavel. O peerconnection.stop() do WebRTC na trilha de audio e a forma canonica.

Use

A stack de 2026:

Camada Escolha
Transporte LiveKit (WebRTC) ou Pion (Go)
VAD Silero VAD 4.0
ASR em streaming Parakeet-CTC-0.6B ou Whisper-Streaming
LLM primeiro token Groq, Cerebras, vLLM-streaming
TTS em streaming Kokoro ou ElevenLabs Turbo v2.5
Cancelamento de eco WebRTC AEC3
Nativo de ponta a ponta OpenAI Realtime API ou Moshi

Armadilhas

  • Bufferizar 500 ms por seguranca. O buffer e o seu piso de latencia. Encolha-o.
  • Nao fixar threads. Callback de audio em uma thread de prioridade menor que a da UI = glitches sob carga.
  • Chunks de TTS pequenos demais. Chunks abaixo de 200 ms tornam os artefatos do vocoder audiveis. Chunks de 320 ms sao o ponto ideal.
  • Sem jitter buffer. Redes reais tem jitter; sem suavizacao voce tem estalos.
  • Tratamento de erro de tiro unico. Pipelines de audio precisam ser a prova de falhas. Uma excecao mata a sessao.

Entregue

Salve como outputs/skill-realtime-designer.md. Projete um pipeline de audio em tempo real com orcamentos de latencia concretos por estagio.

Exercicios

  1. Facil. Rode code/main.py. Simula um ring buffer + VAD de energia; imprime as latencias por estagio para um stream falso de 10 segundos.
  2. Medio. Usando sounddevice, construa um loop de passthrough que processe seu microfone em frames de 20 ms e imprima o estado do VAD em cada frame.
  3. Dificil. Construa um teste de eco full duplex com aiortc: navegador → WebRTC → Python → WebRTC → navegador. Meca a latencia glass-to-glass com um pulso de 1 kHz.

Termos-Chave

Termo O que as pessoas dizem O que realmente significa
Ring buffer A fila circular FIFO de tamanho fixo, lock-free (ou com lock SPSC) para frames de audio.
VAD Gate de silencio Modelo ou heuristica que marca fala vs nao-fala.
ASR em streaming STT em tempo real Emite texto parcial a medida que o audio chega; lookahead limitado.
Jitter buffer Suavizador de rede Fila que reordena pacotes fora de ordem; 60-80 ms tipico.
AEC Cancelamento de eco Subtrai o caminho de feedback do alto-falante para o microfone.
Barge-in Interrupcao do usuario O sistema detecta a fala do usuario no meio do TTS; precisa cancelar a reproducao.
Full duplex Simultaneo nos dois sentidos Usuario e bot podem falar ao mesmo tempo; o Moshi e full duplex.

Leitura Complementar

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).