#![allow(unsafe_code)]
use std::{
fmt::{Display, Error, Formatter},
future::Future,
marker::PhantomData,
os::raw::c_void,
pin::Pin,
task::{Context, Poll},
};
use fon::{chan::Ch32, Frame, Stream};
use super::{
asound, pcm_hw_params, AudioDevice, SndPcmState, SndPcmStream, SoundDevice,
DEFAULT,
};
pub(crate) struct Microphone {
device: AudioDevice,
buffer: Vec<Ch32>,
period: u16,
endi: usize,
pub(crate) channels: u8,
pub(crate) sample_rate: Option<f64>,
}
impl SoundDevice for Microphone {
const INPUT: bool = true;
fn pcm(&self) -> *mut c_void {
self.device.pcm
}
fn hwp(&self) -> *mut c_void {
self.device.pcm
}
}
impl From<AudioDevice> for Microphone {
fn from(device: AudioDevice) -> Self {
Self {
device,
buffer: Vec::new(),
period: 0,
channels: 0,
endi: 0,
sample_rate: None,
}
}
}
impl Display for Microphone {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str(self.device.name.as_str())
}
}
impl Default for Microphone {
fn default() -> Self {
let (pcm, hwp, supported) =
super::open(DEFAULT.as_ptr().cast(), SndPcmStream::Capture)
.unwrap();
Self::from(AudioDevice {
name: "Default".to_string(),
pcm,
hwp,
supported,
fds: Vec::new(),
})
}
}
impl Microphone {
fn set_channels<F>(&mut self) -> Option<bool>
where
F: Frame<Chan = Ch32>,
{
if F::CHAN_COUNT != self.channels.into() {
if !matches!(F::CHAN_COUNT, 1 | 2 | 6) {
panic!("Unknown speaker configuration")
}
self.channels = F::CHAN_COUNT as u8;
pcm_hw_params(
&self.device,
self.channels,
&mut self.buffer,
&mut self.sample_rate,
&mut self.period,
)?;
Some(true)
} else {
Some(false)
}
}
pub(crate) fn record<F: Frame<Chan = Ch32>>(
&mut self,
) -> MicrophoneStream<'_, F> {
self.set_channels::<F>()
.expect("Microphone::record() called with invalid configuration");
MicrophoneStream(self, 0, PhantomData)
}
pub(crate) fn channels(&self) -> u8 {
self.device.supported
}
}
impl Future for Microphone {
type Output = ();
#[allow(unsafe_code)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if this.channels == 0 {
let _ = this.device.start();
return Poll::Ready(());
}
let mut pending = true;
for fd in &this.device.fds {
if !fd.should_yield() {
pending = false;
break;
}
}
if pending {
return Poll::Pending;
}
let result = unsafe {
asound::pcm::readi(
this.device.pcm,
this.buffer.as_mut_slice().as_mut_ptr(),
this.period,
)
};
match result {
Err(error) => {
match error {
-11 => { }
-77 => {
eprintln!(
"Incorrect state (-EBADFD): Report Bug to \
https://github.com/libcala/wavy/issues/new"
);
unreachable!()
}
-32 => {
match unsafe { asound::pcm::state(this.device.pcm) } {
SndPcmState::Xrun => {
eprintln!("Microphone XRUN: Latency cause?");
unsafe {
asound::pcm::prepare(this.device.pcm)
.unwrap();
}
}
st => {
eprintln!(
"Incorrect state = {:?} (XRUN): Report Bug \
to https://github.com/libcala/wavy/issues/new",
st
);
unreachable!()
}
}
}
-86 => {
eprintln!(
"Stream got suspended, trying to recover… (-ESTRPIPE)"
);
unsafe {
if asound::pcm::resume(this.device.pcm).is_ok() {
asound::pcm::prepare(this.device.pcm).unwrap();
}
}
}
_ => unreachable!(),
}
for fd in &this.device.fds {
fd.register_waker(cx.waker());
}
Poll::Pending
}
Ok(len) => {
this.endi = len;
Poll::Ready(())
}
}
}
}
pub(crate) struct MicrophoneStream<'a, F: Frame<Chan = Ch32>>(
&'a mut Microphone,
usize,
PhantomData<F>,
);
impl<F: Frame<Chan = Ch32>> Iterator for MicrophoneStream<'_, F> {
type Item = F;
fn next(&mut self) -> Option<Self::Item> {
if self.1 >= self.0.endi {
return None;
}
let frame = F::from_channels(
&self.0.buffer[self.1 * self.0.channels as usize..],
);
self.1 += 1;
Some(frame)
}
}
impl<F: Frame<Chan = Ch32>> Stream<F> for MicrophoneStream<'_, F> {
fn sample_rate(&self) -> Option<f64> {
self.0.sample_rate
}
fn len(&self) -> Option<usize> {
Some(self.0.endi)
}
}