//! Records a WAV file (roughly 3 seconds long) using the default input device and config. //! //! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav". extern crate anyhow; extern crate cpal; extern crate hound; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use std::fs::File; use std::io::BufWriter; use std::sync::{Arc, Mutex}; fn main() -> Result<(), anyhow::Error> { // Use the default host for working with audio devices. let host = cpal::default_host(); // Setup the default input device and stream with the default input config. let device = host .default_input_device() .expect("Failed to get default input device"); println!("Default input device: {}", device.name()?); let config = device .default_input_config() .expect("Failed to get default input config"); println!("Default input config: {:?}", config); // The WAV file we're recording to. const PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); let spec = wav_spec_from_config(&config); let writer = hound::WavWriter::create(PATH, spec)?; let writer = Arc::new(Mutex::new(Some(writer))); // A flag to indicate that recording is in progress. println!("Begin recording..."); // Run the input stream on a separate thread. let writer_2 = writer.clone(); let err_fn = move |err| { eprintln!("an error occurred on stream: {}", err); }; let stream = match config.sample_format() { cpal::SampleFormat::F32 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, cpal::SampleFormat::I16 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, cpal::SampleFormat::U16 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, }; stream.play()?; // Let recording go for roughly three seconds. std::thread::sleep(std::time::Duration::from_secs(3)); drop(stream); writer.lock().unwrap().take().unwrap().finalize()?; println!("Recording {} complete!", PATH); Ok(()) } fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { match format { cpal::SampleFormat::U16 => hound::SampleFormat::Int, cpal::SampleFormat::I16 => hound::SampleFormat::Int, cpal::SampleFormat::F32 => hound::SampleFormat::Float, } } fn wav_spec_from_config(config: &cpal::SupportedStreamConfig) -> hound::WavSpec { hound::WavSpec { channels: config.channels() as _, sample_rate: config.sample_rate().0 as _, bits_per_sample: (config.sample_format().sample_size() * 8) as _, sample_format: sample_format(config.sample_format()), } } type WavWriterHandle = Arc>>>>; fn write_input_data(input: &[T], writer: &WavWriterHandle) where T: cpal::Sample, U: cpal::Sample + hound::Sample, { if let Ok(mut guard) = writer.try_lock() { if let Some(writer) = guard.as_mut() { for &sample in input.iter() { let sample: U = cpal::Sample::from(&sample); writer.write_sample(sample).ok(); } } } }