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:

FeldTypStandardwertBeschreibung
sample_rateu3216000Abtastrate in Hz
channelsu81Kanalanzahl (1=Mono, 2=Stereo)
frame_duration_msu3220Dauer 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

FeldTypBeschreibung
modeVadModeHandsFree oder PushToTalk
silence_threshold_msu32Aufeinanderfolgende Stille vor SpeechEnded
speech_threshold_msu32Aufeinanderfolgende 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:

  1. Audio vom Mikrofon erfassen
  2. Sprachgrenzen mit VAD erkennen
  3. Sprache mit GeminiStt (oder einem beliebigen SttProvider) transkribieren
  4. Das Transkript zur Verarbeitung an einen LlmAgent senden
  5. Die Antwort mit GeminiTts (oder einem beliebigen TtsProvider) synthetisieren
  6. 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:

KomponenteFehlervarianteWann
AudioCaptureAudioError::DeviceGerät nicht gefunden, Host nicht verfügbar, Konfigurationsvalidierung
AudioPlaybackAudioError::DeviceGerät nicht gefunden, Host nicht verfügbar, Fehler beim Öffnen/Schreiben
VadTurnManagerAudioError::VadKonfigurationsvalidierung (Null-Schwellenwerte)

Plattformunterstützung

PlattformAudio-BackendStatus
macOSCoreAudioUnterstützt
LinuxPipeWire (über ALSA-Kompatibilität)Unterstützt (libasound2-dev installieren)
LinuxALSA / PulseAudio (veraltet)Unterstützt (libasound2-dev installieren)
WindowsWASAPIUnterstützt

Unter modernem Linux ist PipeWire der standardmäßige Audi​​oserver 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.

Desktop-Audio-Pipeline - ADK-Rust Dokumentation | ADK-Rust