데스크톱 오디오 파이프라인
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.0는 cookie-factory =0.3.2를 고정하는 반면 pipewire의 libspa는 0.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_rate | u32 | 16000 | Hz 단위 샘플 속도 |
channels | u8 | 1 | 채널 수 (1=모노, 2=스테레오) |
frame_duration_ms | u32 | 20 | 각 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
| 필드 | 타입 | 설명 |
|---|---|---|
mode | VadMode | HandsFree 또는 PushToTalk |
silence_threshold_ms | u32 | SpeechEnded 전의 연속적인 침묵 |
speech_threshold_ms | u32 | SpeechStarted 전의 연속적인 발화 |
사용하기 전에 validate()를 호출하세요 — AudioError::Vad로 0 임계값을 거부합니다.
음성 에이전트 구축
완전한 대화형 음성 에이전트 패턴:
- 마이크에서 오디오 캡처
- VAD로 음성 경계 감지
- GeminiStt (또는 모든
SttProvider)로 음성을 전사 - 추론을 위해 전사본을 LlmAgent로 전송
- GeminiTts (또는 모든
TtsProvider)로 응답 합성 - 합성된 오디오를 스피커로 재생
실제 Gemini 클라우드 공급자를 사용하는 완전한 작동 예시를 보려면 examples/desktop_audio/src/voice_agent.rs를 참조하세요.
스레드 안전성
모든 데스크톱 오디오 유형 (AudioCapture, AudioPlayback, VadTurnManager)은(는) Send + Sync이며, Tokio 작업 간에 안전하게 공유할 수 있습니다.
오류 처리
데스크톱 오디오 오류는 기존 AudioError enum을 사용합니다:
| 구성 요소 | 오류 변형 | 시기 |
|---|---|---|
| AudioCapture | AudioError::Device | 장치를 찾을 수 없음, 호스트 사용 불가, 구성 유효성 검사 |
| AudioPlayback | AudioError::Device | 장치를 찾을 수 없음, 호스트 사용 불가, 열기/쓰기 실패 |
| VadTurnManager | AudioError::Vad | 구성 유효성 검사 (임계값 0) |
플랫폼 지원
| 플랫폼 | 오디오 백엔드 | 상태 |
|---|---|---|
| macOS | CoreAudio | 지원됨 |
| Linux | PipeWire (ALSA 호환을 통해) | 지원됨 (libasound2-dev 설치) |
| Linux | ALSA / PulseAudio (레거시) | 지원됨 (libasound2-dev 설치) |
| Windows | WASAPI | 지원됨 |
최신 Linux에서, PipeWire는 기본 오디오 서버이며, PulseAudio 및 ALSA를 대체했습니다. 이 desktop-audio 기능은 PipeWire에서 ALSA 호환성 계층을 통해 투명하게 작동합니다. 네이티브 PipeWire 백엔드는 pipewire 크레이트를 사용하여 향후 릴리스를 위해 계획되어 있습니다 (현재 redis-protocol의 업스트림 종속성 충돌로 인해 차단됨).
예시
실제 Gemini STT/TTS를 사용하는 완전한 대화형 음성 에이전트를 포함한 6가지 실용적인 예시는 examples/desktop_audio/을(를) 참조하십시오.