Skip to main content

rust_audio_api/nodes/
microphone.rs

1use crate::nodes::resampler::{ResamplerState, RingIter};
2use crate::types::{AUDIO_UNIT_SIZE, AudioUnit};
3use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
4use cpal::{Stream, StreamConfig};
5use dasp::signal::Signal;
6use ringbuf::HeapRb;
7use ringbuf::traits::{Observer, Producer, Split};
8
9/// An audio source that captures sound from the system's default microphone.
10///
11/// It automatically handles sample rate conversion if the microphone's native
12/// sample rate differs from the target sample rate.
13pub struct MicrophoneNode {
14    resampler: ResamplerState,
15    _stream: Stream,
16    gain: f32,
17}
18
19impl MicrophoneNode {
20    /// Creates a new `MicrophoneNode` targeting the specified sample rate.
21    ///
22    /// # Parameters
23    /// - `target_sample_rate`: The sample rate requested by the `AudioContext`.
24    pub fn new(target_sample_rate: u32) -> Result<Self, anyhow::Error> {
25        let host = cpal::default_host();
26        let device = host
27            .default_input_device()
28            .expect("Microphone device not found");
29        let supported_config = device.default_input_config()?;
30        let input_rate = supported_config.sample_rate();
31        let config: StreamConfig = supported_config.into();
32        let channels = config.channels as usize;
33
34        println!("Microphone sample rate: {:?}", input_rate);
35
36        // Approx 1 second of buffer
37        let capacity = input_rate as usize * channels;
38        let ringbuf = HeapRb::<f32>::new(capacity);
39        let (mut producer, consumer) = ringbuf.split();
40
41        let stream = device.build_input_stream(
42            &config,
43            move |data: &[f32], _: &cpal::InputCallbackInfo| {
44                for &sample in data {
45                    if !producer.is_full() {
46                        let _ = producer.try_push(sample);
47                    }
48                }
49            },
50            |err| eprintln!("Microphone capture error: {}", err),
51            None,
52        )?;
53
54        stream.play()?;
55
56        let ring_iter = RingIter { consumer, channels };
57
58        let resampler = if input_rate != target_sample_rate {
59            let ring_buffer = dasp::ring_buffer::Fixed::from([[0.0; 2]; 100]);
60            let sinc = dasp::interpolate::sinc::Sinc::new(ring_buffer);
61            let converter =
62                ring_iter.from_hz_to_hz(sinc, input_rate as f64, target_sample_rate as f64);
63            ResamplerState::Resampling(Box::new(converter))
64        } else {
65            ResamplerState::Passthrough(ring_iter)
66        };
67
68        Ok(Self {
69            resampler,
70            _stream: stream,
71            gain: 1.0,
72        })
73    }
74
75    /// Sets the input gain for the microphone capture.
76    pub fn set_gain(&mut self, gain: f32) {
77        self.gain = gain;
78    }
79
80    #[inline(always)]
81    pub fn process(&mut self, _input: Option<&AudioUnit>, output: &mut AudioUnit) {
82        match &mut self.resampler {
83            ResamplerState::Passthrough(iter) => {
84                for out in output.iter_mut().take(AUDIO_UNIT_SIZE) {
85                    *out = iter.next();
86                }
87            }
88            ResamplerState::Resampling(converter) => {
89                for out in output.iter_mut().take(AUDIO_UNIT_SIZE) {
90                    *out = converter.next();
91                }
92            }
93        }
94
95        // Apply gain safely using dasp slice operations
96        dasp::slice::map_in_place(&mut output[..], |frame| {
97            [frame[0] * self.gain, frame[1] * self.gain]
98        });
99    }
100}