Desktop-Audio-Pipeline
Das adk-audio-Crate stellt plattformübergreifende Desktop-Audio-Ein-/Ausgabe hinter dem desktop-audio-Feature-Flag bereit. Drei Komponenten — AudioCapture, AudioPlayback und VadTurnManager — ermöglichen Mikrofonaufnahme, Lautsprecherwiedergabe und VAD-gesteuerten Sprecherwechsel zum Erstellen von Desktop-Sprachagenten.
Übersicht
Die Desktop-Audio-Pipeline verbindet die Audiosystemhardware mit dem bestehenden adk-audio-Pipeline-System:
Microphone → AudioCapture → AudioStream → [VAD → STT → Agent → TTS] → AudioPlayback → Speaker
Alle Komponenten verwenden das cpal-Crate für plattformübergreifendes Audio (CoreAudio unter macOS, PipeWire/ALSA/PulseAudio unter Linux, WASAPI unter Windows) und erzeugen bzw. verwenden den standardmäßigen Typ AudioFrame.
Feature-Flags
[dependencies]
# Cross-platform (macOS, Linux, Windows) via cpal
adk-audio = { version = "2.1.0", features = ["desktop-audio"] }
Das Feature desktop-audio impliziert vad (für VadProcessor) und fügt cpal als Abhängigkeit hinzu. Es ist absichtlich vom Feature all ausgeschlossen, um zu verhindern, dass plattformspezifische Audioabhängigkeiten in CI-Builds einbezogen werden.
Unterstützung für PipeWire
Unter modernen Linux-Distributionen (Fedora 34+, Ubuntu 22.10+, Arch) hat PipeWire PulseAudio und ALSA als standardmäßigen Audioserver abgelöst. Das Feature desktop-audio funktioniert über dessen ALSA-Kompatibilitätsschicht transparent mit PipeWire — es ist keine zusätzliche Konfiguration erforderlich.
Ein natives PipeWire-Backend (desktop-pipewire) unter Verwendung des Crates pipewire (v0.9) ist für eine zukünftige Version geplant. Natives PipeWire würde eine geringere Latenz und direktes Sitzungsmanagement ermöglichen. Derzeit wird dies durch einen Abhängigkeitskonflikt verhindert: redis-protocol 6.0.0 fixiert cookie-factory =0.3.2, während pipewires libspa 0.3.3 erfordert. Sobald der Upstream diese Fixierung lockert, kann das native Backend hinzugefügt werden.
Schnellstart
Audiogeräte auflisten
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());
}
Mikrofon-Audio aufnehmen
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();
Audio über den Lautsprecher wiedergeben
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();
VAD-Sprecherwechsel
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)");
}
}
});
Komponenten
AudioDevice
Deskriptor für ein System-Audiogerät (Eingabe oder Ausgabe). Enthält ein undurchsichtiges id und ein für Menschen lesbares name.
CaptureConfig
Konfiguration für die Mikrofonaufnahme:
| Feld | Typ | Standardwert | Beschreibung |
|---|---|---|---|
sample_rate | u32 | 16000 | Abtastrate in Hz |
channels | u8 | 1 | Kanalanzahl (1=Mono, 2=Stereo) |
frame_duration_ms | u32 | 20 | Dauer jedes AudioFrame |
Rufen Sie validate() vor der Verwendung auf — weist Nullwerte mit AudioError::Device zurück.
AudioCapture
Mikrofonaufnahme über cpal. Die Methode start_capture() gibt ein AudioStream zurück (begrenztes mpsc::Receiver<AudioFrame> mit einer Kapazität von 64). Frames werden in Intervallen von frame_duration_ms im PCM-16-LE-Format erzeugt.
AudioPlayback
Lautsprecherwiedergabe über cpal. Die Methode play() reiht die Samples eines AudioFrame in einen gemeinsam genutzten Puffer ein, den der cpal-Ausgabe-Callback leert. Rufen Sie stop() auf, um das Gerät freizugeben.
VadTurnManager
Verbraucht ein AudioStream, wendet VadProcessor::is_speech() auf jeden Frame an und gibt VoiceActivityEvent-Werte über einen registrierten Callback aus.
Zwei Modi:
- HandsFree — automatische Erkennung von Sprachgrenzen anhand konfigurierbarer Schwellenwerte für die Dauer von Stille und Sprache
- PushToTalk — keine automatischen Ereignisse; der Aufrufer steuert die Aktivierung extern
VadConfig
| Feld | Typ | Beschreibung |
|---|---|---|
mode | VadMode | HandsFree oder PushToTalk |
silence_threshold_ms | u32 | Aufeinanderfolgende Stille vor SpeechEnded |
speech_threshold_ms | u32 | Aufeinanderfolgende Sprache vor SpeechStarted |
Rufen Sie validate() vor der Verwendung auf — lehnt Schwellenwerte von null mit AudioError::Vad ab.
Erstellen eines Sprachagenten
Das vollständige Muster für einen konversationellen Sprachagenten:
- Audio vom Mikrofon erfassen
- Sprachgrenzen mit VAD erkennen
- Sprache mit GeminiStt (oder einem beliebigen
SttProvider) transkribieren - Das Transkript zur Verarbeitung an einen LlmAgent senden
- Die Antwort mit GeminiTts (oder einem beliebigen
TtsProvider) synthetisieren - Das synthetisierte Audio über den Lautsprecher wiedergeben
Unter examples/desktop_audio/src/voice_agent.rs finden Sie ein vollständiges, funktionierendes Beispiel mit echten Gemini-Cloud-Anbietern.
Thread-Sicherheit
Alle Desktop-Audiotypen (AudioCapture, AudioPlayback, VadTurnManager) sind Send + Sync, wodurch sie sicher über Tokio-Tasks hinweg gemeinsam verwendet werden können.
Fehlerbehandlung
Desktop-Audiofehler verwenden das vorhandene Enum AudioError:
| Komponente | Fehlervariante | Wann |
|---|---|---|
| AudioCapture | AudioError::Device | Gerät nicht gefunden, Host nicht verfügbar, Konfigurationsvalidierung |
| AudioPlayback | AudioError::Device | Gerät nicht gefunden, Host nicht verfügbar, Fehler beim Öffnen/Schreiben |
| VadTurnManager | AudioError::Vad | Konfigurationsvalidierung (Null-Schwellenwerte) |
Plattformunterstützung
| Plattform | Audio-Backend | Status |
|---|---|---|
| macOS | CoreAudio | Unterstützt |
| Linux | PipeWire (über ALSA-Kompatibilität) | Unterstützt (libasound2-dev installieren) |
| Linux | ALSA / PulseAudio (veraltet) | Unterstützt (libasound2-dev installieren) |
| Windows | WASAPI | Unterstützt |
Unter modernem Linux ist PipeWire der standardmäßige Audioserver und hat PulseAudio sowie ALSA ersetzt. Die Funktion desktop-audio funktioniert über ihre ALSA-Kompatibilitätsschicht transparent mit PipeWire. Ein natives PipeWire-Backend unter Verwendung der pipewire-Crate ist für eine zukünftige Version geplant (derzeit durch einen Konflikt bei einer Upstream-Abhängigkeit in redis-protocol blockiert).
Beispiele
Siehe examples/desktop_audio/ für 6 praktische Beispiele, darunter einen vollständigen konversationellen Sprachagenten mit echtem Gemini-STT/TTS.