데스크톱 오디오 파이프라인

adk-audio 크레이트는 desktop-audio 기능 플래그를 통해 크로스 플랫폼 데스크톱 오디오 I/O를 제공합니다. AudioCapture, AudioPlayback, VadTurnManager 세 가지 구성 요소는 마이크 캡처, 스피커 재생 및 VAD 기반 턴테이킹을 가능하게 하여 데스크톱 음성 에이전트를 구축합니다.

개요

데스크톱 오디오 파이프라인은 시스템 오디오 하드웨어를 기존 adk-audio 파이프라인 시스템에 연결합니다:

Microphone → AudioCapture → AudioStream → [VAD → STT → Agent → TTS] → AudioPlayback → Speaker

모든 구성 요소는 크로스 플랫폼 오디오(macOS에서는 CoreAudio, Linux에서는 PipeWire/ALSA/PulseAudio, Windows에서는 WASAPI)를 위해 cpal 크레이트를 사용하며 표준 AudioFrame 타입을 생성/소비합니다.

기능 플래그

[dependencies]
# Cross-platform (macOS, Linux, Windows) via cpal
adk-audio = { version = "2.0.0", features = ["desktop-audio"] }

desktop-audio 기능은 vad(VadProcessor용)를 포함하며 cpal를 의존성으로 추가합니다. 이는 플랫폼별 오디오 의존성이 CI 빌드에 포함되는 것을 방지하기 위해 의도적으로 all 기능에서 제외됩니다.

PipeWire 지원

최신 Linux(Fedora 34+, Ubuntu 22.10+, Arch)에서는 PipeWire가 PulseAudio 및 ALSA를 기본 오디오 서버로 대체했습니다. desktop-audio 기능은 ALSA 호환성 계층을 통해 PipeWire에서 투명하게 작동하며, 추가 구성이 필요하지 않습니다.

pipewire 크레이트(v0.9)를 사용하는 네이티브 PipeWire 백엔드(desktop-pipewire)는 향후 릴리스를 위해 계획되어 있습니다. 네이티브 PipeWire는 더 낮은 지연 시간과 직접적인 세션 관리를 제공할 것입니다. 현재는 의존성 충돌로 인해 차단되어 있습니다: redis-protocol 6.0.0cookie-factory =0.3.2를 고정하는 반면 pipewirelibspa0.3.3를 필요로 합니다. 업스트림에서 이 고정을 완화하면 네이티브 백엔드를 추가할 수 있습니다.

빠른 시작

오디오 장치 목록

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());
}

마이크 오디오 캡처

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();

스피커를 통해 오디오 재생

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 턴테이킹

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)");
        }
    }
});

구성 요소

AudioDevice

시스템 오디오 장치(입력 또는 출력)에 대한 설명자입니다. 불투명한 id와 사람이 읽을 수 있는 name를 포함합니다.

CaptureConfig

마이크 캡처 구성:

필드타입기본값설명
sample_rateu3216000Hz 단위 샘플 속도
channelsu81채널 수 (1=모노, 2=스테레오)
frame_duration_msu3220각 AudioFrame의 지속 시간

사용하기 전에 validate()를 호출하세요 — AudioError::Device로 0 값을 거부합니다.

AudioCapture

cpal를 통한 마이크 캡처. start_capture() 메서드는 AudioStream(용량 64의 제한된 mpsc::Receiver<AudioFrame>)를 반환합니다. 프레임은 PCM-16 LE 형식으로 frame_duration_ms 간격으로 생성됩니다.

AudioPlayback

cpal를 통한 스피커 재생. play() 메서드는 AudioFrame의 샘플을 cpal 출력 콜백이 비우는 공유 버퍼에 대기시킵니다. 장치를 해제하려면 stop()를 호출하세요.

VadTurnManager

AudioStream를 소비하고, 각 프레임에 VadProcessor::is_speech()를 적용하며, 등록된 콜백을 통해 VoiceActivityEvent 값을 내보냅니다.

두 가지 모드:

  • HandsFree — 구성 가능한 무음 및 음성 지속 시간 임계값을 사용하여 자동 음성 경계 감지
  • PushToTalk — 자동 이벤트 없음; 호출자가 외부에서 게이팅을 제어합니다

VadConfig

필드타입설명
modeVadModeHandsFree 또는 PushToTalk
silence_threshold_msu32SpeechEnded 전의 연속적인 침묵
speech_threshold_msu32SpeechStarted 전의 연속적인 발화

사용하기 전에 validate()를 호출하세요 — AudioError::Vad로 0 임계값을 거부합니다.

음성 에이전트 구축

완전한 대화형 음성 에이전트 패턴:

  1. 마이크에서 오디오 캡처
  2. VAD로 음성 경계 감지
  3. GeminiStt (또는 모든 SttProvider)로 음성을 전사
  4. 추론을 위해 전사본을 LlmAgent로 전송
  5. GeminiTts (또는 모든 TtsProvider)로 응답 합성
  6. 합성된 오디오를 스피커로 재생

실제 Gemini 클라우드 공급자를 사용하는 완전한 작동 예시를 보려면 examples/desktop_audio/src/voice_agent.rs를 참조하세요.

스레드 안전성

모든 데스크톱 오디오 유형 (AudioCapture, AudioPlayback, VadTurnManager)은(는) Send + Sync이며, Tokio 작업 간에 안전하게 공유할 수 있습니다.

오류 처리

데스크톱 오디오 오류는 기존 AudioError enum을 사용합니다:

구성 요소오류 변형시기
AudioCaptureAudioError::Device장치를 찾을 수 없음, 호스트 사용 불가, 구성 유효성 검사
AudioPlaybackAudioError::Device장치를 찾을 수 없음, 호스트 사용 불가, 열기/쓰기 실패
VadTurnManagerAudioError::Vad구성 유효성 검사 (임계값 0)

플랫폼 지원

플랫폼오디오 백엔드상태
macOSCoreAudio지원됨
LinuxPipeWire (ALSA 호환을 통해)지원됨 (libasound2-dev 설치)
LinuxALSA / PulseAudio (레거시)지원됨 (libasound2-dev 설치)
WindowsWASAPI지원됨

최신 Linux에서, PipeWire는 기본 오디오 서버이며, PulseAudio 및 ALSA를 대체했습니다. 이 desktop-audio 기능은 PipeWire에서 ALSA 호환성 계층을 통해 투명하게 작동합니다. 네이티브 PipeWire 백엔드는 pipewire 크레이트를 사용하여 향후 릴리스를 위해 계획되어 있습니다 (현재 redis-protocol의 업스트림 종속성 충돌로 인해 차단됨).

예시

실제 Gemini STT/TTS를 사용하는 완전한 대화형 음성 에이전트를 포함한 6가지 실용적인 예시는 examples/desktop_audio/을(를) 참조하십시오.