use core::{cell::Cell, convert::Infallible, fmt};
use esp_hal::{
Async,
dma::DmaRxStreamBuf,
dma_rx_stream_buffer,
i2s::master::{I2sRx, I2sRxDmaTransfer},
};
use crate::Error;
const MICROPHONE_BUFFER: usize = 4096;
const MICROPHONE_CHUNK: usize = 512;
const SETTLING_FRAMES: usize = 8;
type MicrophoneTransfer = I2sRxDmaTransfer<'static, Async, DmaRxStreamBuf>;
pub struct Microphone {
transfer: Option<MicrophoneTransfer>,
settling: usize,
}
impl Microphone {
pub fn start(rx: I2sRx<'static, Async>) -> Result<Self, Error> {
let buffer = dma_rx_stream_buffer!(MICROPHONE_BUFFER, MICROPHONE_CHUNK);
let transfer = rx.read(buffer).map_err(|_| Error::Microphone)?;
Ok(Self {
transfer: Some(transfer),
settling: SETTLING_FRAMES,
})
}
pub async fn run(
&mut self,
resettle: &Cell<bool>,
mut take: impl FnMut(&[i16]),
) -> Result<Infallible, Error> {
let mut mono = [0i16; 256];
loop {
let transfer = self.transfer.as_mut().ok_or(Error::Microphone)?;
if transfer.wait_for_available_async().await.is_err() {
self.restart()?;
continue;
}
if resettle.take() {
self.settling = SETTLING_FRAMES;
}
loop {
let (frames, _) = transfer.peek().as_chunks::<4>();
if frames.is_empty() {
break;
}
let dropped = frames.len().min(self.settling);
if dropped > 0 {
self.settling -= dropped;
transfer.consume(dropped * 4);
continue;
}
let count = frames.len().min(mono.len());
for (sample, &[lo, hi, _, _]) in mono.iter_mut().zip(frames) {
*sample = i16::from_le_bytes([lo, hi]);
}
transfer.consume(count * 4);
take(&mono[..count]);
}
}
}
fn restart(&mut self) -> Result<(), Error> {
let transfer = self.transfer.take().ok_or(Error::Microphone)?;
let (rx, buffer) = transfer.stop();
let (descriptors, bytes) = buffer.split();
let buffer = DmaRxStreamBuf::new(descriptors, bytes).map_err(|_| Error::Microphone)?;
self.transfer = Some(rx.read(buffer).map_err(|_| Error::Microphone)?);
self.settling = SETTLING_FRAMES;
Ok(())
}
}
impl fmt::Debug for Microphone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Microphone")
.field("running", &self.transfer.is_some())
.finish_non_exhaustive()
}
}