Created
July 9, 2024 08:35
-
-
Save louis030195/013fda2fa6b373f815bbeeb39e524090 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use clap::Parser; | |
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | |
use cpal::{FromSample, Sample}; | |
use std::fs::File; | |
use std::io::BufWriter; | |
use std::sync::{Arc, Mutex}; | |
#[derive(Parser, Debug)] | |
#[command(version, about = "CPAL record_wav example", long_about = None)] | |
struct Opt { | |
/// The audio device to use | |
#[arg(short, long, default_value_t = String::from("default"))] | |
device: String, | |
} | |
fn main() -> Result<(), anyhow::Error> { | |
let opt = Opt::parse(); | |
// Use ScreenCaptureKit host | |
let host = cpal::host_from_id(cpal::HostId::ScreenCaptureKit)?; | |
// Set up the input device and stream with the default input config. | |
let device = if opt.device == "default" { | |
host.default_input_device() | |
} else { | |
host.input_devices()? | |
.find(|x| x.name().map(|y| y == opt.device).unwrap_or(false)) | |
} | |
.expect("failed to find input device"); | |
println!("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))); | |
println!("Begin recording..."); | |
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::I8 => device.build_input_stream( | |
&config.into(), | |
move |data, _: &_| write_input_data::<i8, i8>(data, &writer_2), | |
err_fn, | |
None, | |
)?, | |
cpal::SampleFormat::I16 => device.build_input_stream( | |
&config.into(), | |
move |data, _: &_| write_input_data::<i16, i16>(data, &writer_2), | |
err_fn, | |
None, | |
)?, | |
cpal::SampleFormat::I32 => device.build_input_stream( | |
&config.into(), | |
move |data, _: &_| write_input_data::<i32, i32>(data, &writer_2), | |
err_fn, | |
None, | |
)?, | |
cpal::SampleFormat::F32 => device.build_input_stream( | |
&config.into(), | |
move |data, _: &_| write_input_data::<f32, f32>(data, &writer_2), | |
err_fn, | |
None, | |
)?, | |
sample_format => { | |
return Err(anyhow::Error::msg(format!( | |
"Unsupported sample format '{sample_format}'" | |
))) | |
} | |
}; | |
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(()) | |
} | |
// ... [rest of the functions remain the same] ... | |
fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { | |
if format.is_float() { | |
hound::SampleFormat::Float | |
} else { | |
hound::SampleFormat::Int | |
} | |
} | |
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<Mutex<Option<hound::WavWriter<BufWriter<File>>>>>; | |
fn write_input_data<T, U>(input: &[T], writer: &WavWriterHandle) | |
where | |
T: Sample, | |
U: Sample + hound::Sample + FromSample<T>, | |
{ | |
if let Ok(mut guard) = writer.try_lock() { | |
if let Some(writer) = guard.as_mut() { | |
for &sample in input.iter() { | |
let sample: U = U::from_sample(sample); | |
writer.write_sample(sample).ok(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment