Phase 06: Speech & Audio

Traitement audio en temps réel

Les pipelines en série traitent un fichier. Les pipelines en temps réel traitent les 20 millisecondes suivantes avant l'arrivée des 20 suivantes. Chaque IA de conversation, studio de diffusion et robot téléphonique vit et meurt avec ce budget de latence.

Type: Build

Languages: Python

Prerequisites: Phase 6 · 02 (Spectrograms), Phase 6 · 04 (ASR), Phase 6 · 07 (TTS)

Time: ~75 minutes

Le problème

Vous voulez un assistant vocal qui se sent vivant. La latence de prise de tour de conversation humaine est d'environ 230 ms (silence à réponse). Tout ce qui dépasse 500 ms se sent robotique; au-dessus de 1500 ms se sent cassé. Le budget pour une pleinehear → understand → respond → speakla boucle en 2026 est:

StageBudget
Mic → buffer20 ms
VAD10 ms
ASR (streaming)150 ms
LLM (first token)100 ms
TTS (first chunk)100 ms
Render → speaker20 ms
Total~400 ms

Moshi (Kyutai, 2024) a réalisé 200 ms de double-duplex. GPT-4o en temps réel (2024) montre ~ 320 ms. Les pipelines en cascade en 2022 ont été expédiées à 2500 ms. L'amélioration de 10 fois est venue de trois techniques: (1) streaming partout, (2) pipeline asynchrone avec des résultats partiels, (3) génération interrompue.

Le concept

!Streaming audio pipeline with ring buffer, VAD gate, interruption

Frame / chunk / window.Les flux audio en temps réel sont des blocs de taille fixe.

Ring buffer.Bouffer circulaire de taille fixe. Le fil de producteur écrit de nouveaux cadres, le fil de consommation est lu. empêche l'allocation dans le chemin chaud. Taille ≈ latence maximale × taux d'échantillonnage; un anneau de 2 secondes 16 kHz = 32 000 échantillons.

VAD (Voice Activity Detection).Les portes fonctionnent en aval quand personne ne parle. Silero VAD 4.0 (2024) fonctionne < 1 ms par frame de 30 ms sur CPU. webrtcvadest l'alternative plus ancienne.

Streaming ASR.Modèles qui émettent des transcriptions partielles à l'arrivée de l'audio. Parakeet-CTC-0.6B en mode streaming (NeMo, 2024) fait 25% WER à 320 ms de latence.

Interruption.Lorsque l'utilisateur parle pendant que l'assistant parle, vous devez (a) détecter le barge-in, (b) arrêter le TTS, (c) jeter le reste de la sortie LLM. Tout cela dans un délai de 100 ms, ou l'utilisateur perçoit l'assistant sourd.

WebRTC Opus transport.20 ms de cadres, 48 kHz, bitrate adaptative 8128 kbps. Standard pour navigateur et mobile. LiveKit, Daily.co, Pion sont les piles 2026 pour la construction d'applications vocales.

Jitter buffer.Les paquets réseau arrivent en panne / en retard. Le tampon jitter réordonne et se déplace; trop petits → espaces audibles, trop grands → latence. 6080 ms typique.

Les goutches communes

  • Thread contention.Les modèles lourds GIL + de Python peuvent affamer le fil audio. Utilisez une bibliothèque audio C-callback (appareil sonore, PortAudio) et gardez Python hors du chemin chaud.
  • Sample-rate conversion latency.Le reéchantillonnage à l'intérieur du pipeline ajoute 520 ms.soxr_hq)
  • TTS priming.Même un TTS rapide comme Kokoro a un réchauffement de 100 à 200 ms sur première demande.
  • Echo cancellation.Sans AEC, la sortie TTS rentre dans le micro et déclenche ASR sur la voix du robot.

Faites-le

Étape 1: tampon à anneaux

pythonimport 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)

La capacité détermine la latence maximale du tampon. 32 000 échantillons à 16 kHz = 2 s.

Étape 2: Porte de détection

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

Remplacez par Silero VAD en production:

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

Étape 3: diffusion de l' ASR

python# 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")

Étape 4: gestionnaire d'interruption

pythonclass 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)

Le lien est en continu avec le réseau TTS.

Utilisez-le

La pile de 2026:

LayerPick
TransportLiveKit (WebRTC) or Pion (Go)
VADSilero VAD 4.0
Streaming ASRParakeet-CTC-0.6B or Whisper-Streaming
LLM first-tokenGroq, Cerebras, vLLM-streaming
Streaming TTSKokoro or ElevenLabs Turbo v2.5
Echo cancelWebRTC AEC3
End-to-end nativeOpenAI Realtime API or Moshi

Les pièges

  • Buffering 500 ms to be safe.Le tampon est votre plancher de latence.
  • Not pinning threads.Rappel d'audio sur un fil de priorité inférieure à l'interface utilisateur = défauts sous chargement.
  • TTS chunks too small.Les pièces sous 200 ms rendent audibles les objets du vocoder.
  • No jitter buffer.Les réseaux réels sont nerveux; sans lisser, on se fait des coups.
  • Single-shot error handling.Les conduites audio doivent être résistantes aux chocs.

La faire partir

  • Je ne sais pas .outputs/skill-realtime-designer.md- Conception d'un pipeline audio en temps réel avec des budgets concrets de latence par étape.

Exercices

  1. Easy.On court .code/main.pySimulation d'un tampon d'anneau + VAD d'énergie; imprime les latences de l'étape pour un faux flux de 10 secondes.
  2. Medium.En utilisant sounddevice, construire un passage à travers la boucle qui traite votre micro en 20 ms cadres et imprime l'état de VAD à chaque cadre.
  3. Hard.Construisez un test d' écho duplex complet avec aiortc: navigateur → WebRTC → Python → WebRTC → navigateur. Mesurer la latence verre à verre avec un pulsation de 1 kHz.

Les termes clés

TermWhat people sayWhat it actually means
Ring bufferThe circular queueFixed-size, lock-free (or SPSC-locked) FIFO for audio frames.
VADSilence gateModel or heuristic marking speech vs non-speech.
Streaming ASRReal-time STTEmits partial text as audio arrives; bounded lookahead.
Jitter bufferNetwork smootherQueue reordering out-of-order packets; 60–80 ms typical.
AECEcho cancellationSubtracts speaker-to-mic feedback path.
Barge-inUser interruptSystem detects user speech mid-TTS; must cancel playback.
Full duplexSimultaneous both waysUser and bot can talk at the same time; Moshi is full duplex.

Pour en savoir plus

This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.

Browse the complete course catalog or open this lesson on GitHub.