1use crate::mic_sim::MicrophoneSimConfig;
7use serde::Serialize;
8use serde::{Deserialize, Serialize as SerdeSerialize};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::path::PathBuf;
12
13#[derive(Debug, Clone, Copy)]
15pub struct AudioSpec {
16 pub sample_rate: u32,
18 pub channels: u16,
20}
21
22#[derive(Debug, Clone)]
24pub struct AudioFrame {
25 pub samples: Vec<f32>,
27 #[allow(dead_code)]
28 pub spec: AudioSpec,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
36pub enum AudioProfile {
37 #[default]
39 VoiceClean,
40 Raw,
42 VoiceNoisyRoom,
44 VoiceHvac,
46}
47
48pub const SUPPORTED_AUDIO_PROFILES: [AudioProfile; 4] = [
49 AudioProfile::Raw,
50 AudioProfile::VoiceClean,
51 AudioProfile::VoiceNoisyRoom,
52 AudioProfile::VoiceHvac,
53];
54
55impl AudioProfile {
56 pub fn from_str(value: &str) -> Self {
57 match value {
58 "raw" => Self::Raw,
59 "voice_hvac" => Self::VoiceHvac,
60 "voice_noisy_room" => Self::VoiceNoisyRoom,
61 _ => Self::VoiceClean,
62 }
63 }
64
65 pub fn as_str(&self) -> &'static str {
66 match self {
67 Self::Raw => "raw",
68 Self::VoiceClean => "voice_clean",
69 Self::VoiceNoisyRoom => "voice_noisy_room",
70 Self::VoiceHvac => "voice_hvac",
71 }
72 }
73
74 pub fn label(&self) -> &'static str {
75 match self {
76 Self::Raw => "Raw",
77 Self::VoiceClean => "Voice Clean",
78 Self::VoiceNoisyRoom => "Voice Noisy Room",
79 Self::VoiceHvac => "Voice HVAC",
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum DelayEffectPreset {
86 VoiceDoubler,
87 Thickener,
88 Slapback,
89 Bloom,
90}
91
92pub const SUPPORTED_DELAY_EFFECT_PRESETS: [DelayEffectPreset; 4] = [
93 DelayEffectPreset::VoiceDoubler,
94 DelayEffectPreset::Thickener,
95 DelayEffectPreset::Slapback,
96 DelayEffectPreset::Bloom,
97];
98
99impl DelayEffectPreset {
100 pub fn from_str(value: &str) -> Self {
101 match value {
102 "thickener" => Self::Thickener,
103 "slapback" => Self::Slapback,
104 "bloom" => Self::Bloom,
105 _ => Self::VoiceDoubler,
106 }
107 }
108
109 pub fn as_str(&self) -> &'static str {
110 match self {
111 Self::VoiceDoubler => "voice_doubler",
112 Self::Thickener => "thickener",
113 Self::Slapback => "slapback",
114 Self::Bloom => "bloom",
115 }
116 }
117
118 pub fn label(&self) -> &'static str {
119 match self {
120 Self::VoiceDoubler => "Voice Doubler",
121 Self::Thickener => "Thickener",
122 Self::Slapback => "Slapback",
123 Self::Bloom => "Bloom",
124 }
125 }
126}
127
128#[derive(Debug, Clone, Copy)]
129pub struct DelayEffectConfig {
130 pub preset: DelayEffectPreset,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, SerdeSerialize, Default)]
134#[serde(rename_all = "snake_case")]
135pub enum ProcessorOverrideMode {
136 #[default]
137 Inherit,
138 Enabled,
139 Disabled,
140}
141
142impl ProcessorOverrideMode {
143 pub fn as_str(&self) -> &'static str {
144 match self {
145 Self::Inherit => "inherit",
146 Self::Enabled => "enabled",
147 Self::Disabled => "disabled",
148 }
149 }
150
151 pub fn label(&self) -> &'static str {
152 match self {
153 Self::Inherit => "Inherit",
154 Self::Enabled => "Force On",
155 Self::Disabled => "Force Off",
156 }
157 }
158}
159
160#[derive(Debug, Clone, Default)]
161pub struct NativeCaptureConfig {
162 pub output_path: PathBuf,
163 pub target_sample_rate: u32,
164 pub target_channels: u16,
165 pub preferred_input_device: Option<String>,
166 pub profile: AudioProfile,
167 pub input_gain_db: f32,
168 pub limiter_threshold: f32,
169 pub high_pass_hz: f32,
170 pub noise_suppression_amount: f32,
171 pub noise_calibration_ms: u32,
172 pub delay_effect: Option<DelayEffectConfig>,
173 pub stage_overrides: BTreeMap<String, ProcessorOverrideMode>,
174 pub microphone_sim: MicrophoneSimConfig,
175}
176
177impl NativeCaptureConfig {
178 pub fn new(output_path: PathBuf, profile: AudioProfile) -> Self {
179 Self {
180 output_path,
181 profile,
182 target_sample_rate: 44_100,
183 target_channels: 1,
184 preferred_input_device: None,
185 input_gain_db: 0.0,
186 limiter_threshold: 0.95,
187 high_pass_hz: 80.0,
188 noise_suppression_amount: 0.5,
189 noise_calibration_ms: 300,
190 delay_effect: None,
191 stage_overrides: BTreeMap::new(),
192 microphone_sim: MicrophoneSimConfig::default(),
193 }
194 }
195}
196
197#[derive(Debug, Clone)]
198pub struct InputDeviceInfo {
199 pub id: String,
200 pub name: String,
201 pub is_default: bool,
202}
203
204#[derive(Debug, Clone, Serialize, Default)]
205pub struct CaptureDiagnostics {
206 pub backend: String,
207 pub profile: String,
208 pub profile_base: Option<String>,
209 pub device_name: Option<String>,
210 pub sample_rate: Option<u32>,
211 pub channels: Option<u16>,
212 pub duration_ms: Option<i64>,
213 pub frames_processed: u64,
214 pub analyzed_frames: u64,
215 pub noise_only_frames: u64,
216 pub transitional_frames: u64,
217 pub speech_like_frames: u64,
218 pub rms_level: f32,
219 pub peak_level: f32,
220 pub clipping_events: u64,
221 pub processor_names: Vec<String>,
222 pub processor_stage_overrides: Vec<String>,
223 pub resolved_delay_preset: Option<String>,
224 pub microphone_sim_model: Option<String>,
225 pub microphone_sim_processor_names: Vec<String>,
226 pub notes: Vec<String>,
227}
228
229#[derive(Debug, Clone)]
230pub struct AudioError {
231 message: String,
232}
233
234impl AudioError {
235 pub fn new(message: impl Into<String>) -> Self {
236 Self {
237 message: message.into(),
238 }
239 }
240}
241
242impl fmt::Display for AudioError {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 f.write_str(&self.message)
245 }
246}
247
248impl std::error::Error for AudioError {}
249
250pub type AudioResult<T> = Result<T, AudioError>;
251
252pub trait ActiveRecording: Send {
253 fn stop(&mut self) -> AudioResult<CaptureDiagnostics>;
254}
255
256pub trait ActiveListening: Send {
257 fn stop(&mut self) -> AudioResult<CaptureDiagnostics>;
258 fn update_config(&mut self, config: NativeCaptureConfig) -> AudioResult<()>;
259}