tauri_plugin_audio_recorder/
models.rs

1use serde::{Deserialize, Serialize};
2
3/// Audio format for recording
4#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
5#[serde(rename_all = "lowercase")]
6pub enum AudioFormat {
7    #[default]
8    Wav,
9    // Future: Mp3, Ogg, etc.
10}
11
12/// Audio quality preset
13#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
14#[serde(rename_all = "lowercase")]
15pub enum AudioQuality {
16    /// 16kHz, 16-bit mono - Good for speech
17    Low,
18    #[default]
19    /// 44.1kHz, 16-bit mono - Standard quality
20    Medium,
21    /// 48kHz, 16-bit stereo - High quality
22    High,
23}
24
25impl AudioQuality {
26    pub fn sample_rate(&self) -> u32 {
27        match self {
28            AudioQuality::Low => 16000,
29            AudioQuality::Medium => 44100,
30            AudioQuality::High => 48000,
31        }
32    }
33
34    pub fn channels(&self) -> u16 {
35        match self {
36            AudioQuality::Low | AudioQuality::Medium => 1,
37            AudioQuality::High => 2,
38        }
39    }
40}
41
42/// Recording configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44#[serde(rename_all = "camelCase")]
45pub struct RecordingConfig {
46    /// Output file path (without extension)
47    pub output_path: String,
48    /// Audio format (default: wav)
49    #[serde(default)]
50    pub format: AudioFormat,
51    /// Audio quality preset (default: medium)
52    #[serde(default)]
53    pub quality: AudioQuality,
54    /// Maximum recording duration in seconds (0 = unlimited)
55    #[serde(default)]
56    pub max_duration: u32,
57}
58
59/// Recording state
60#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
61#[serde(rename_all = "lowercase")]
62pub enum RecordingState {
63    Idle,
64    Recording,
65    Paused,
66}
67
68impl Default for RecordingState {
69    fn default() -> Self {
70        Self::Idle
71    }
72}
73
74/// Recording status response
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(rename_all = "camelCase")]
77pub struct RecordingStatus {
78    /// Current state
79    pub state: RecordingState,
80    /// Duration recorded so far (in milliseconds)
81    pub duration_ms: u64,
82    /// Output file path (if recording)
83    pub output_path: Option<String>,
84}
85
86/// Recording completed response
87#[derive(Debug, Clone, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct RecordingResult {
90    /// Full path to the recorded file
91    pub file_path: String,
92    /// Duration in milliseconds
93    pub duration_ms: u64,
94    /// File size in bytes
95    pub file_size: u64,
96    /// Sample rate used
97    pub sample_rate: u32,
98    /// Number of channels
99    pub channels: u16,
100}
101
102/// Audio input device
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct AudioDevice {
106    /// Device identifier
107    pub id: String,
108    /// Human-readable device name
109    pub name: String,
110    /// Whether this is the default device
111    pub is_default: bool,
112}
113
114/// List of available audio devices
115#[derive(Debug, Clone, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct AudioDevicesResponse {
118    pub devices: Vec<AudioDevice>,
119}
120
121/// Permission status
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(rename_all = "camelCase")]
124pub struct PermissionStatus {
125    /// Whether microphone permission is granted
126    pub granted: bool,
127    /// Whether we can ask for permission
128    pub can_request: bool,
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_audio_quality_sample_rate() {
137        assert_eq!(AudioQuality::Low.sample_rate(), 16000);
138        assert_eq!(AudioQuality::Medium.sample_rate(), 44100);
139        assert_eq!(AudioQuality::High.sample_rate(), 48000);
140    }
141
142    #[test]
143    fn test_audio_quality_channels() {
144        assert_eq!(AudioQuality::Low.channels(), 1);
145        assert_eq!(AudioQuality::Medium.channels(), 1);
146        assert_eq!(AudioQuality::High.channels(), 2);
147    }
148
149    #[test]
150    fn test_recording_state_serialization() {
151        assert_eq!(
152            serde_json::to_string(&RecordingState::Idle).unwrap(),
153            "\"idle\""
154        );
155        assert_eq!(
156            serde_json::to_string(&RecordingState::Recording).unwrap(),
157            "\"recording\""
158        );
159        assert_eq!(
160            serde_json::to_string(&RecordingState::Paused).unwrap(),
161            "\"paused\""
162        );
163    }
164
165    #[test]
166    fn test_recording_config_deserialization() {
167        let json = r#"{
168            "outputPath": "/path/to/recording",
169            "quality": "high"
170        }"#;
171
172        let config: RecordingConfig = serde_json::from_str(json).unwrap();
173        assert_eq!(config.output_path, "/path/to/recording");
174        assert!(matches!(config.quality, AudioQuality::High));
175        assert!(matches!(config.format, AudioFormat::Wav));
176        assert_eq!(config.max_duration, 0);
177    }
178
179    #[test]
180    fn test_recording_config_defaults() {
181        let json = r#"{"outputPath": "/test"}"#;
182        let config: RecordingConfig = serde_json::from_str(json).unwrap();
183
184        assert!(matches!(config.format, AudioFormat::Wav));
185        assert!(matches!(config.quality, AudioQuality::Medium));
186        assert_eq!(config.max_duration, 0);
187    }
188}