use cpal::traits::{DeviceTrait, HostTrait};
use cpal::Device;
use ringbuffer::AllocRingBuffer;
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};
pub struct AudioDevAndCfg {
dev: cpal::Device,
cfg: cpal::StreamConfig,
}
impl AudioDevAndCfg {
pub fn new(
preferred_dev: Option<cpal::Device>,
preferred_cfg: Option<cpal::StreamConfig>,
) -> Self {
let dev = preferred_dev.unwrap_or_else(|| {
let host = cpal::default_host();
host.default_input_device().unwrap_or_else(|| {
panic!(
"No default audio input device found for host {}",
host.id().name()
)
})
});
let cfg = preferred_cfg.unwrap_or_else(|| dev.default_input_config().unwrap().config());
Self { dev, cfg }
}
pub const fn dev(&self) -> &cpal::Device {
&self.dev
}
pub const fn cfg(&self) -> &cpal::StreamConfig {
&self.cfg
}
}
impl Debug for AudioDevAndCfg {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AudioDevAndCfg")
.field(
"dev",
&self
.dev
.name()
.as_ref()
.map(|x| x.as_str())
.unwrap_or("<unknown>"),
)
.field("cfg", &self.cfg)
.finish()
}
}
pub fn setup_audio_input_loop(
latest_audio_data: Arc<Mutex<AllocRingBuffer<f32>>>,
audio_dev_and_cfg: AudioDevAndCfg,
) -> cpal::Stream {
let dev = audio_dev_and_cfg.dev();
let cfg = audio_dev_and_cfg.cfg();
eprintln!(
"Using input device '{}' with config: {:?}",
dev.name()
.as_ref()
.map(|x| x.as_str())
.unwrap_or("<unknown>"),
cfg
);
assert!(
cfg.channels == 1 || cfg.channels == 2,
"only supports Mono or Stereo channels!"
);
if cfg.sample_rate.0 != 44100 && cfg.sample_rate.0 != 48000 {
eprintln!(
"WARN: sampling rate is {}, but the crate was only tested with 44,1/48khz.",
cfg.sample_rate.0
);
}
let is_mono = cfg.channels == 1;
let stream = dev
.build_input_stream(
audio_dev_and_cfg.cfg(),
move |data: &[f32], _info| {
let mut audio_buf = latest_audio_data.lock().unwrap();
if is_mono {
audio_buf.extend(data.iter().copied());
} else {
audio_buf.extend(data.chunks_exact(2).map(|vals| (vals[0] + vals[1]) / 2.0))
}
},
|err| {
eprintln!("got stream error: {:#?}", err);
},
None,
)
.unwrap();
stream
}
pub fn list_input_devs() -> Vec<(String, cpal::Device)> {
let host = cpal::default_host();
type DeviceName = String;
let mut devs: Vec<(DeviceName, Device)> = host
.input_devices()
.unwrap()
.map(|dev| {
(
dev.name().unwrap_or_else(|_| String::from("<unknown>")),
dev,
)
})
.collect();
devs.sort_by(|(n1, _), (n2, _)| n1.cmp(n2));
devs
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list_input_devs() {
dbg!(list_input_devs()
.iter()
.map(|(n, d)| (n, d.default_input_config()))
.collect::<Vec<_>>());
}
}