Pipeline de Áudio para Desktop
O crate adk-audio fornece E/S de áudio para desktop multiplataforma por trás da flag de recurso desktop-audio. Três componentes — AudioCapture, AudioPlayback e VadTurnManager — habilitam a captura pelo microfone, a reprodução pelos alto-falantes e o gerenciamento de turnos orientado por VAD para a criação de agentes de voz para desktop.
Visão geral
O pipeline de áudio para desktop conecta o hardware de áudio do sistema ao sistema de pipeline adk-audio existente:
Microphone → AudioCapture → AudioStream → [VAD → STT → Agent → TTS] → AudioPlayback → Speaker
Todos os componentes usam o crate cpal para áudio multiplataforma (CoreAudio no macOS, PipeWire/ALSA/PulseAudio no Linux, WASAPI no Windows) e produzem/consomem o tipo AudioFrame padrão.
Flags de recurso
[dependencies]
# Cross-platform (macOS, Linux, Windows) via cpal
adk-audio = { version = "2.1.0", features = ["desktop-audio"] }
O recurso desktop-audio implica vad (para VadProcessor) e adiciona cpal como dependência. Ele é intencionalmente excluído do recurso all para evitar incluir dependências de áudio específicas da plataforma nas compilações de CI.
Suporte a PipeWire
Nas versões modernas do Linux (Fedora 34+, Ubuntu 22.10+, Arch), PipeWire substituiu PulseAudio e ALSA como servidor de áudio padrão. O recurso desktop-audio funciona de forma transparente em PipeWire por meio de sua camada de compatibilidade com ALSA — nenhuma configuração adicional é necessária.
Um backend nativo de PipeWire (desktop-pipewire), usando o crate pipewire (v0.9), está planejado para uma versão futura. O PipeWire nativo forneceria menor latência e gerenciamento direto de sessões. Atualmente, ele está bloqueado por um conflito de dependências: redis-protocol 6.0.0 fixa cookie-factory =0.3.2, enquanto pipewire's libspa requer 0.3.3. Quando o upstream flexibilizar essa fixação, o backend nativo poderá ser adicionado.
Início rápido
Listar dispositivos de áudio
use adk_audio::{AudioCapture, AudioPlayback};
let inputs = AudioCapture::list_input_devices()?;
for device in &inputs {
println!("Mic: {} ({})", device.name(), device.id());
}
let outputs = AudioPlayback::list_output_devices()?;
for device in &outputs {
println!("Speaker: {} ({})", device.name(), device.id());
}
Capturar áudio do microfone
use adk_audio::{AudioCapture, CaptureConfig};
use std::time::{Duration, Instant};
let mut capture = AudioCapture::new();
let devices = AudioCapture::list_input_devices()?;
let device = devices.first().expect("no input device");
let config = CaptureConfig::default(); // 16kHz, mono, 20ms frames
let mut stream = capture.start_capture(device.id(), &config)?;
let start = Instant::now();
while start.elapsed() < Duration::from_secs(3) {
if let Some(frame) = stream.recv().await {
// frame.data: PCM-16 LE bytes
// frame.sample_rate: 16000
// frame.channels: 1
// frame.duration_ms: 20
}
}
capture.stop_capture();
Reproduzir áudio pelos alto-falantes
use adk_audio::{AudioFrame, AudioPlayback};
let mut playback = AudioPlayback::new();
let devices = AudioPlayback::list_output_devices()?;
let device = devices.first().expect("no output device");
let frame = AudioFrame::silence(16000, 1, 1000); // 1 second
playback.play(device.id(), &frame).await?;
playback.stop();
Gerenciamento de turnos com VAD
use std::sync::Arc;
use adk_audio::{
AudioCapture, CaptureConfig, VadConfig, VadMode,
VadTurnManager, VoiceActivityEvent, VadProcessor,
};
let vad: Arc<dyn VadProcessor> = /* your VadProcessor impl */;
let config = VadConfig {
mode: VadMode::HandsFree,
silence_threshold_ms: 500,
speech_threshold_ms: 200,
};
let mut manager = VadTurnManager::new(vad, config)?;
let mut capture = AudioCapture::new();
let stream = capture.start_capture(device_id, &CaptureConfig::default())?;
manager.start(stream, |event| {
match event {
VoiceActivityEvent::SpeechStarted => println!("Speech started"),
VoiceActivityEvent::SpeechEnded { duration_ms } => {
println!("Speech ended ({duration_ms}ms)");
}
}
});
Componentes
AudioDevice
Descritor de um dispositivo de áudio do sistema (entrada ou saída). Contém um id opaco e um name legível.
CaptureConfig
Configuração para captura de microfone:
| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
sample_rate | u32 | 16000 | Taxa de amostragem em Hz |
channels | u8 | 1 | Contagem de canais (1=mono, 2=estéreo) |
frame_duration_ms | u32 | 20 | Duração de cada AudioFrame |
Chame validate() antes de usar — rejeita valores zero com AudioError::Device.
AudioCapture
Captura do microfone via cpal. O método start_capture() retorna um AudioStream (mpsc::Receiver<AudioFrame> limitado com capacidade 64). Os frames são produzidos em intervalos de frame_duration_ms no formato PCM-16 LE.
AudioPlayback
Reprodução pelo alto-falante via cpal. O método play() enfileira as amostras de um AudioFrame em um buffer compartilhado que o callback de saída do cpal drena. Chame stop() para liberar o dispositivo.
VadTurnManager
Consome um AudioStream, aplica VadProcessor::is_speech() a cada frame e emite valores VoiceActivityEvent por meio de um callback registrado.
Dois modos:
- HandsFree — detecção automática dos limites da fala usando limiares configuráveis de duração do silêncio e da fala
- PushToTalk — nenhum evento automático; o chamador controla o gating externamente
VadConfig
| Campo | Tipo | Descrição |
|---|---|---|
mode | VadMode | HandsFree ou PushToTalk |
silence_threshold_ms | u32 | Silêncio consecutivo antes de SpeechEnded |
speech_threshold_ms | u32 | Fala consecutiva antes de SpeechStarted |
Chame validate() antes do uso — rejeita limites zero com AudioError::Vad.
Criando um agente de voz
O padrão completo de agente de voz conversacional:
- Capture áudio do microfone
- Detecte os limites da fala com VAD
- Transcreva a fala com GeminiStt (ou qualquer
SttProvider) - Envie a transcrição para um LlmAgent para raciocínio
- Sintetize a resposta com GeminiTts (ou qualquer
TtsProvider) - Reproduza o áudio sintetizado pelo alto-falante
Consulte examples/desktop_audio/src/voice_agent.rs para obter um exemplo completo e funcional usando provedores reais do Gemini na nuvem.
Segurança de threads
Todos os tipos de áudio para desktop (AudioCapture, AudioPlayback, VadTurnManager) são Send + Sync, o que os torna seguros para compartilhar entre tarefas do Tokio.
Tratamento de erros
Os erros de áudio para desktop usam o enum AudioError existente:
| Componente | Variante de erro | Quando |
|---|---|---|
| AudioCapture | AudioError::Device | Dispositivo não encontrado, host indisponível, validação da configuração |
| AudioPlayback | AudioError::Device | Dispositivo não encontrado, host indisponível, falha ao abrir/gravar |
| VadTurnManager | AudioError::Vad | Validação da configuração (limiares zero) |
Suporte à plataforma
| Plataforma | Backend de áudio | Status |
|---|---|---|
| macOS | CoreAudio | Compatível |
| Linux | PipeWire (via compatibilidade com ALSA) | Compatível (instale libasound2-dev) |
| Linux | ALSA / PulseAudio (legado) | Compatível (instale libasound2-dev) |
| Windows | WASAPI | Compatível |
No Linux moderno, PipeWire é o servidor de áudio padrão e substituiu PulseAudio e ALSA. O recurso desktop-audio funciona em PipeWire de forma transparente por meio de sua camada de compatibilidade com ALSA. Um backend PipeWire nativo usando o crate pipewire está planejado para uma versão futura (atualmente bloqueado por um conflito de dependência upstream em redis-protocol).
Exemplos
Consulte examples/desktop_audio/ para ver 6 exemplos práticos, incluindo um agente de voz conversacional completo com STT/TTS reais do Gemini.