1use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
7use std::collections::VecDeque;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::{Arc, Mutex};
10
11use crate::error::{Error, Result};
12use crate::resample::{f32_to_i16, i16_to_f32, resample_linear};
13
14pub struct AudioCapture {
16 _stream: cpal::Stream,
17 running: Arc<AtomicBool>,
18 buffer: Arc<Mutex<Vec<f32>>>,
19 device_rate: u32,
20}
21
22impl AudioCapture {
23 pub fn start() -> Result<Self> {
27 let host = cpal::default_host();
28 let device = host
29 .default_input_device()
30 .ok_or_else(|| Error::device("No input audio device"))?;
31
32 let config = device
33 .default_input_config()
34 .map_err(|e| Error::device(format!("No default input config: {}", e)))?;
35
36 let device_rate = config.sample_rate();
37 log::info!("Audio capture: device rate = {} Hz", device_rate);
38
39 let stream_config = cpal::StreamConfig {
40 channels: 1,
41 sample_rate: config.sample_rate(),
42 buffer_size: cpal::BufferSize::Default,
43 };
44
45 let running = Arc::new(AtomicBool::new(true));
46 let buffer: Arc<Mutex<Vec<f32>>> = Arc::new(Mutex::new(Vec::with_capacity(8192)));
47
48 let cb_running = running.clone();
49 let cb_buffer = buffer.clone();
50
51 let stream = device
52 .build_input_stream(
53 &stream_config,
54 move |data: &[f32], _: &cpal::InputCallbackInfo| {
55 if !cb_running.load(Ordering::Relaxed) {
56 return;
57 }
58 if let Ok(mut buf) = cb_buffer.lock() {
59 buf.extend_from_slice(data);
60 while buf.len() > device_rate as usize {
62 buf.drain(..device_rate as usize / 10);
63 }
64 }
65 },
66 |err| log::error!("Audio capture error: {}", err),
67 None,
68 )
69 .map_err(|e| Error::device(format!("Failed to build input stream: {}", e)))?;
70
71 stream
72 .play()
73 .map_err(|e| Error::device(format!("Failed to start capture: {}", e)))?;
74
75 Ok(Self {
76 _stream: stream,
77 running,
78 buffer,
79 device_rate,
80 })
81 }
82
83 pub fn read_samples(&self, target_rate: u32, max_samples: usize) -> Vec<i16> {
87 let mut result = Vec::new();
88
89 if let Ok(mut buf) = self.buffer.lock() {
90 if buf.is_empty() {
91 return result;
92 }
93
94 let device_samples_needed = ((max_samples as f64)
96 * (self.device_rate as f64 / target_rate as f64))
97 .ceil() as usize;
98 let available = buf.len().min(device_samples_needed);
99
100 if available > 0 {
101 let samples: Vec<f32> = buf.drain(..available).collect();
102 let resampled = resample_linear(&samples, self.device_rate, target_rate);
103 result = f32_to_i16(&resampled);
104 }
105 }
106
107 result
108 }
109
110 pub fn device_rate(&self) -> u32 {
112 self.device_rate
113 }
114
115 pub fn stop(&self) {
117 self.running.store(false, Ordering::Relaxed);
118 }
119}
120
121impl Drop for AudioCapture {
122 fn drop(&mut self) {
123 self.stop();
124 }
125}
126
127pub struct AudioPlayback {
129 _stream: cpal::Stream,
130 running: Arc<AtomicBool>,
131 buffer: Arc<Mutex<VecDeque<f32>>>,
132 device_rate: u32,
133}
134
135impl AudioPlayback {
136 pub fn start() -> Result<Self> {
140 let host = cpal::default_host();
141 let device = host
142 .default_output_device()
143 .ok_or_else(|| Error::device("No output audio device"))?;
144
145 let config = device
146 .default_output_config()
147 .map_err(|e| Error::device(format!("No default output config: {}", e)))?;
148
149 let device_rate = config.sample_rate();
150 log::info!("Audio playback: device rate = {} Hz", device_rate);
151
152 let stream_config = cpal::StreamConfig {
153 channels: 1,
154 sample_rate: config.sample_rate(),
155 buffer_size: cpal::BufferSize::Default,
156 };
157
158 let running = Arc::new(AtomicBool::new(true));
159 let buffer: Arc<Mutex<VecDeque<f32>>> =
160 Arc::new(Mutex::new(VecDeque::with_capacity(device_rate as usize)));
161
162 let cb_buffer = buffer.clone();
163
164 let stream = device
165 .build_output_stream(
166 &stream_config,
167 move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
168 if let Ok(mut buf) = cb_buffer.lock() {
169 for sample in data.iter_mut() {
170 *sample = buf.pop_front().unwrap_or(0.0);
171 }
172 } else {
173 for sample in data.iter_mut() {
174 *sample = 0.0;
175 }
176 }
177 },
178 |err| log::error!("Audio playback error: {}", err),
179 None,
180 )
181 .map_err(|e| Error::device(format!("Failed to build output stream: {}", e)))?;
182
183 stream
184 .play()
185 .map_err(|e| Error::device(format!("Failed to start playback: {}", e)))?;
186
187 Ok(Self {
188 _stream: stream,
189 running,
190 buffer,
191 device_rate,
192 })
193 }
194
195 pub fn write_samples(&self, samples: &[i16], source_rate: u32) {
199 let f32_samples = i16_to_f32(samples);
200 let resampled = resample_linear(&f32_samples, source_rate, self.device_rate);
201
202 if let Ok(mut buf) = self.buffer.lock() {
203 for s in resampled {
204 buf.push_back(s);
205 }
206 while buf.len() > self.device_rate as usize {
208 buf.pop_front();
209 }
210 }
211 }
212
213 pub fn device_rate(&self) -> u32 {
215 self.device_rate
216 }
217
218 pub fn stop(&self) {
220 self.running.store(false, Ordering::Relaxed);
221 }
222}
223
224impl Drop for AudioPlayback {
225 fn drop(&mut self) {
226 self.stop();
227 }
228}
229
230pub fn list_devices() -> Result<Vec<String>> {
232 let host = cpal::default_host();
233 let mut devices = Vec::new();
234
235 if let Ok(input_devices) = host.input_devices() {
236 for device in input_devices {
237 if let Ok(desc) = device.description() {
238 devices.push(format!("Input: {}", desc.name()));
239 }
240 }
241 }
242
243 if let Ok(output_devices) = host.output_devices() {
244 for device in output_devices {
245 if let Ok(desc) = device.description() {
246 devices.push(format!("Output: {}", desc.name()));
247 }
248 }
249 }
250
251 Ok(devices)
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_list_devices() {
260 let result = list_devices();
262 assert!(result.is_ok());
263 }
264}