Skip to main content

ai_agent/services/
voice.rs

1// Source: /data/home/swei/claudecode/openclaudecode/src/commands/voice/voice.ts
2//! Voice service - audio recording for push-to-talk voice input.
3//!
4////! Translates voice.ts from claude code.
5
6pub const RECORDING_SAMPLE_RATE: u32 = 16000;
7pub const RECORDING_CHANNELS: u32 = 1;
8
9pub const SILENCE_DURATION_SECS: &str = "2.0";
10pub const SILENCE_THRESHOLD: &str = "3%";
11
12#[derive(Debug, Clone)]
13pub struct RecordingAvailability {
14    pub available: bool,
15    pub reason: Option<String>,
16}
17
18impl RecordingAvailability {
19    pub fn available() -> Self {
20        Self {
21            available: true,
22            reason: None,
23        }
24    }
25
26    pub fn unavailable(reason: impl Into<String>) -> Self {
27        Self {
28            available: false,
29            reason: Some(reason.into()),
30        }
31    }
32}
33
34#[derive(Debug, Clone)]
35pub struct VoiceDependencies {
36    pub available: bool,
37    pub missing: Vec<String>,
38    pub install_command: Option<String>,
39}
40
41impl VoiceDependencies {
42    pub fn available() -> Self {
43        Self {
44            available: true,
45            missing: Vec::new(),
46            install_command: None,
47        }
48    }
49
50    pub fn missing_deps(missing: Vec<String>, install_command: Option<String>) -> Self {
51        Self {
52            available: missing.is_empty(),
53            missing,
54            install_command,
55        }
56    }
57}
58
59pub fn has_command(_cmd: &str) -> bool {
60    false
61}
62
63pub fn _reset_arecord_probe_for_testing() {}
64
65pub fn _reset_alsa_cards_for_testing() {}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_recording_availability_available() {
73        let avail = RecordingAvailability::available();
74        assert!(avail.available);
75        assert!(avail.reason.is_none());
76    }
77
78    #[test]
79    fn test_recording_availability_unavailable() {
80        let avail = RecordingAvailability::unavailable("No microphone");
81        assert!(!avail.available);
82        assert_eq!(avail.reason, Some("No microphone".to_string()));
83    }
84
85    #[test]
86    fn test_voice_dependencies_available() {
87        let deps = VoiceDependencies::available();
88        assert!(deps.available);
89        assert!(deps.missing.is_empty());
90    }
91
92    #[test]
93    fn test_voice_dependencies_missing() {
94        let deps = VoiceDependencies::missing_deps(
95            vec!["sox".to_string()],
96            Some("brew install sox".to_string()),
97        );
98        assert!(!deps.available);
99        assert_eq!(deps.missing.len(), 1);
100        assert!(deps.install_command.is_some());
101    }
102}