| 
 | 1 | +//! Slightly less efficient microphone that multiple sources can draw from  | 
 | 2 | +//! think of it as an inverse mixer.  | 
 | 3 | +
  | 
 | 4 | +use std::{  | 
 | 5 | +    sync::{  | 
 | 6 | +        atomic::{AtomicBool, Ordering},  | 
 | 7 | +        mpsc, Arc,  | 
 | 8 | +    },  | 
 | 9 | +    thread::{self, JoinHandle},  | 
 | 10 | +    time::Duration,  | 
 | 11 | +};  | 
 | 12 | + | 
 | 13 | +use cpal::Device;  | 
 | 14 | +use rtrb::RingBuffer;  | 
 | 15 | + | 
 | 16 | +use crate::{microphone::open_input_stream, Source};  | 
 | 17 | +use crate::{  | 
 | 18 | +    microphone::{InputConfig, OpenError},  | 
 | 19 | +    Sample,  | 
 | 20 | +};  | 
 | 21 | + | 
 | 22 | +/// Send on all platforms  | 
 | 23 | +pub struct Microphone {  | 
 | 24 | +    _stream_thread: JoinHandle<()>,  | 
 | 25 | +    buffer: rtrb::Consumer<Sample>,  | 
 | 26 | +    config: InputConfig,  | 
 | 27 | +    poll_interval: Duration,  | 
 | 28 | +    error_occurred: Arc<AtomicBool>,  | 
 | 29 | +    _drop_tx: mpsc::Sender<()>,  | 
 | 30 | +}  | 
 | 31 | + | 
 | 32 | +impl Microphone {  | 
 | 33 | +    pub(crate) fn open(  | 
 | 34 | +        device: Device,  | 
 | 35 | +        config: InputConfig,  | 
 | 36 | +        mut error_callback: impl FnMut(cpal::StreamError) + Send + 'static,  | 
 | 37 | +    ) -> Result<Self, OpenError> {  | 
 | 38 | +        let hundred_ms_of_samples =  | 
 | 39 | +            config.channel_count.get() as u32 * config.sample_rate.get() / 10;  | 
 | 40 | +        let (tx, rx) = RingBuffer::new(hundred_ms_of_samples as usize);  | 
 | 41 | +        let error_occurred = Arc::new(AtomicBool::new(false));  | 
 | 42 | +        let error_callback = {  | 
 | 43 | +            let error_occurred = error_occurred.clone();  | 
 | 44 | +            move |source| {  | 
 | 45 | +                error_occurred.store(true, Ordering::Relaxed);  | 
 | 46 | +                error_callback(source);  | 
 | 47 | +            }  | 
 | 48 | +        };  | 
 | 49 | + | 
 | 50 | +        let (res_tx, res_rx) = mpsc::channel();  | 
 | 51 | +        let (_drop_tx, drop_rx) = mpsc::channel::<()>();  | 
 | 52 | +        let _stream_thread = thread::Builder::new()  | 
 | 53 | +            .name("Rodio cloneable microphone".to_string())  | 
 | 54 | +            .spawn(move || {  | 
 | 55 | +                if let Err(e) = open_input_stream(device, config, tx, error_callback) {  | 
 | 56 | +                    let _ = res_tx.send(Err(e));  | 
 | 57 | +                } else {  | 
 | 58 | +                    let _ = res_tx.send(Ok(()));  | 
 | 59 | +                };  | 
 | 60 | + | 
 | 61 | +                let _should_drop = drop_rx.recv();  | 
 | 62 | +            })  | 
 | 63 | +            .expect("Should be able to spawn threads");  | 
 | 64 | + | 
 | 65 | +        res_rx  | 
 | 66 | +            .recv()  | 
 | 67 | +            .expect("input stream thread should never panic")?;  | 
 | 68 | + | 
 | 69 | +        Ok(Microphone {  | 
 | 70 | +            _stream_thread,  | 
 | 71 | +            _drop_tx,  | 
 | 72 | +            buffer: rx,  | 
 | 73 | +            config,  | 
 | 74 | +            poll_interval: Duration::from_millis(5),  | 
 | 75 | +            error_occurred,  | 
 | 76 | +        })  | 
 | 77 | +    }  | 
 | 78 | + | 
 | 79 | +    /// Get the configuration.  | 
 | 80 | +    ///  | 
 | 81 | +    /// # Example  | 
 | 82 | +    /// Print the sample rate and channel count.  | 
 | 83 | +    /// ```no_run  | 
 | 84 | +    /// # use rodio::microphone::MicrophoneBuilder;  | 
 | 85 | +    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {  | 
 | 86 | +    /// let mic = MicrophoneBuilder::new()  | 
 | 87 | +    ///     .default_device()?  | 
 | 88 | +    ///     .default_config()?  | 
 | 89 | +    ///     .open_stream()?;  | 
 | 90 | +    /// let config = mic.config();  | 
 | 91 | +    /// println!("Sample rate: {}", config.sample_rate.get());  | 
 | 92 | +    /// println!("Channel count: {}", config.channel_count.get());  | 
 | 93 | +    /// # Ok(())  | 
 | 94 | +    /// # }  | 
 | 95 | +    /// ```  | 
 | 96 | +    pub fn config(&self) -> &InputConfig {  | 
 | 97 | +        &self.config  | 
 | 98 | +    }  | 
 | 99 | +}  | 
 | 100 | + | 
 | 101 | +impl Source for Microphone {  | 
 | 102 | +    fn current_span_len(&self) -> Option<usize> {  | 
 | 103 | +        None  | 
 | 104 | +    }  | 
 | 105 | + | 
 | 106 | +    fn channels(&self) -> crate::ChannelCount {  | 
 | 107 | +        self.config.channel_count  | 
 | 108 | +    }  | 
 | 109 | + | 
 | 110 | +    fn sample_rate(&self) -> crate::SampleRate {  | 
 | 111 | +        self.config.sample_rate  | 
 | 112 | +    }  | 
 | 113 | + | 
 | 114 | +    fn total_duration(&self) -> Option<std::time::Duration> {  | 
 | 115 | +        None  | 
 | 116 | +    }  | 
 | 117 | +}  | 
 | 118 | + | 
 | 119 | +impl Iterator for Microphone {  | 
 | 120 | +    type Item = f32;  | 
 | 121 | + | 
 | 122 | +    fn next(&mut self) -> Option<Self::Item> {  | 
 | 123 | +        loop {  | 
 | 124 | +            if let Ok(sample) = self.buffer.pop() {  | 
 | 125 | +                return Some(sample);  | 
 | 126 | +            } else if self.error_occurred.load(Ordering::Relaxed) {  | 
 | 127 | +                return None;  | 
 | 128 | +            } else {  | 
 | 129 | +                thread::sleep(self.poll_interval)  | 
 | 130 | +            }  | 
 | 131 | +        }  | 
 | 132 | +    }  | 
 | 133 | + | 
 | 134 | +    fn size_hint(&self) -> (usize, Option<usize>) {  | 
 | 135 | +        (self.buffer.slots(), None)  | 
 | 136 | +    }  | 
 | 137 | +}  | 
0 commit comments