codewandler_audio/
capture.rs1use crate::channel::new_audio_channel;
2use crate::{AudioSource, AudioSourceFanOut, IntoAudioSource};
3use anyhow::anyhow;
4use crossbeam_channel::{Receiver, Sender};
5use rodio::cpal::StreamConfig;
6use rodio::cpal::traits::{HostTrait, StreamTrait};
7use rodio::{DeviceTrait, cpal};
8use rubato::{FftFixedInOut, Resampler};
9use std::collections::VecDeque;
10use std::sync::{Arc, Mutex};
11use tracing::debug;
12
13pub struct AudioCapture {
14 _input_stream: cpal::Stream,
15 _tx: Sender<f32>,
16 rx: Option<Receiver<f32>>,
17}
18
19impl IntoAudioSource for AudioCapture {
20 type Format = f32;
21 fn into_audio_source(&mut self) -> anyhow::Result<Box<dyn AudioSource<Format = f32>>> {
22 match self.rx.take() {
23 Some(x) => Ok(Box::new(x)),
24 None => Err(anyhow::anyhow!("Audio capture already taken")),
25 }
26 }
27}
28
29pub fn audio_capture(
31 output_sample_rate: u32,
32) -> anyhow::Result<AudioSourceFanOut<f32, AudioCapture>> {
33 let host = cpal::default_host();
35 let device = host
36 .default_input_device()
37 .ok_or_else(|| anyhow::anyhow!("No input device available"))?;
38
39 let config = device.default_input_config()?;
40
41 if config.sample_format() != cpal::SampleFormat::F32 {
43 return Err(anyhow!(
44 "Expected f32 input, got {:?}",
45 config.sample_format()
46 ));
47 }
48
49 let input_config: StreamConfig = config.clone().into();
50 let input_sample_rate = input_config.sample_rate.0;
51 let channels = input_config.channels;
52
53 debug!(
54 "Input device: {}, Sample rate: {}, Channels: {}, Output rate: {}",
55 device.name()?,
56 input_sample_rate,
57 channels,
58 output_sample_rate,
59 );
60
61 let resampler = Arc::new(Mutex::new(FftFixedInOut::<f32>::new(
63 input_sample_rate as usize,
64 output_sample_rate as usize,
65 1024,
66 1,
67 )?));
68
69 let buf_size = resampler.lock().unwrap().input_frames_next();
71 let sample_buffer = Arc::new(Mutex::new(VecDeque::<f32>::new()));
72 let resampler_clone = resampler.clone();
73 let buffer_clone = sample_buffer.clone();
74
75 let (tx, rx) = new_audio_channel::<f32>();
78 let tx_cb = tx.clone();
79 let input_stream = device.build_input_stream(
80 &config.into(),
81 move |data: &[f32], _: &cpal::InputCallbackInfo| {
82 let mono = if channels == 1 {
84 data.to_vec()
85 } else if channels == 2 {
86 stereo_to_mono(data)
87 } else {
88 eprintln!("Unsupported channel count: {}", channels);
89 return;
90 };
91
92 let mut buf = buffer_clone.lock().unwrap();
93 buf.extend(mono);
94
95 while buf.len() >= buf_size {
96 let input_block: Vec<f32> = buf.drain(..buf_size).collect();
97 let mut resampler = resampler_clone.lock().unwrap();
98 match resampler.process(&[input_block], None) {
99 Ok(output) => {
100 let resampled = &output[0];
101 for sample in resampled {
102 tx_cb.send(*sample).unwrap();
103 }
104 }
105 Err(e) => {
106 eprintln!("Resample error: {:?}", e);
107 }
108 }
109 }
110 },
111 |err| eprintln!("Input stream error: {}", err),
112 None,
113 )?;
114
115 input_stream.play()?;
116
117 let cap = AudioCapture {
118 _input_stream: input_stream,
119 _tx: tx,
120 rx: Some(rx),
121 };
122
123 Ok(AudioSourceFanOut::new(cap))
124}
125
126fn stereo_to_mono(input: &[f32]) -> Vec<f32> {
128 input
129 .chunks_exact(2)
130 .map(|chunk| 0.5 * (chunk[0] + chunk[1]))
131 .collect()
132}