extern crate anyhow;
extern crate cpal;
extern crate ringbuf;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use ringbuf::RingBuffer;
const LATENCY_MS: f32 = 150.0;
fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host();
let input_device = host
.default_input_device()
.expect("failed to get default input device");
let output_device = host
.default_output_device()
.expect("failed to get default output device");
println!("Using default input device: \"{}\"", input_device.name()?);
println!("Using default output device: \"{}\"", output_device.name()?);
let config: cpal::StreamConfig = input_device.default_input_config()?.into();
let latency_frames = (LATENCY_MS / 1_000.0) * config.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * config.channels as usize;
let ring = RingBuffer::new(latency_samples * 2);
let (mut producer, mut consumer) = ring.split();
for _ in 0..latency_samples {
producer.push(0.0).unwrap();
}
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
let mut output_fell_behind = false;
for &sample in data {
if producer.push(sample).is_err() {
output_fell_behind = true;
}
}
if output_fell_behind {
eprintln!("output stream fell behind: try increasing latency");
}
};
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
let mut input_fell_behind = None;
for sample in data {
*sample = match consumer.pop() {
Ok(s) => s,
Err(err) => {
input_fell_behind = Some(err);
0.0
}
};
}
if let Some(err) = input_fell_behind {
eprintln!(
"input stream fell behind: {:?}: try increasing latency",
err
);
}
};
println!(
"Attempting to build both streams with f32 samples and `{:?}`.",
config
);
let input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn)?;
let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn)?;
println!("Successfully built streams.");
println!(
"Starting the input and output streams with `{}` milliseconds of latency.",
LATENCY_MS
);
input_stream.play()?;
output_stream.play()?;
println!("Playing for 3 seconds... ");
std::thread::sleep(std::time::Duration::from_secs(3));
drop(input_stream);
drop(output_stream);
println!("Done!");
Ok(())
}
fn err_fn(err: cpal::StreamError) {
eprintln!("an error occurred on stream: {}", err);
}