#![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, Channel},
surround::Surround32,
Frame, Resampler, Sink,
};
use super::{
asound, pcm_hw_params, AudioDevice, SndPcmState, SndPcmStream, SoundDevice,
DEFAULT,
};
pub(crate) struct Speakers {
device: AudioDevice,
starti: usize,
buffer: Vec<Ch32>,
resampler: ([Ch32; 6], f64),
period: u16,
pub(crate) channels: u8,
pub(crate) sample_rate: Option<f64>,
}
impl SoundDevice for Speakers {
const INPUT: bool = false;
fn pcm(&self) -> *mut c_void {
self.device.pcm
}
fn hwp(&self) -> *mut c_void {
self.device.pcm
}
}
impl Display for Speakers {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.write_str(self.device.name.as_str())
}
}
impl From<AudioDevice> for Speakers {
fn from(device: AudioDevice) -> Self {
Self {
device,
starti: 0,
buffer: Vec::new(),
sample_rate: None,
channels: 0,
resampler: ([Ch32::MID; 6], 0.0),
period: 0,
}
}
}
impl Default for Speakers {
fn default() -> Self {
let (pcm, hwp, supported) =
super::open(DEFAULT.as_ptr().cast(), SndPcmStream::Playback)
.unwrap();
Self::from(AudioDevice {
name: "Default".to_string(),
pcm,
hwp,
supported,
fds: Vec::new(),
})
}
}
impl Speakers {
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 play<F>(&mut self) -> SpeakersSink<'_, F>
where
F: Frame<Chan = Ch32>,
{
self.set_channels::<F>()
.expect("Speaker::play() called with invalid configuration");
let resampler = Resampler::<F>::new(
Surround32::from_channels(&self.resampler.0[..]).convert(),
self.resampler.1,
);
SpeakersSink(self, resampler, PhantomData)
}
pub(crate) fn channels(&self) -> u8 {
self.device.supported
}
}
impl Future for &mut Speakers {
type Output = ();
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::writei(
this.device.pcm,
this.buffer.as_ptr(),
this.period.into(),
)
};
let len = match result {
Ok(len) => len,
Err(error) => {
match error {
-11 => {
for fd in &this.device.fds {
fd.register_waker(cx.waker());
}
return Poll::Pending;
}
-32 => {
match unsafe { asound::pcm::state(this.device.pcm) } {
SndPcmState::Xrun => {
unsafe {
asound::pcm::prepare(this.device.pcm)
.unwrap();
asound::pcm::writei(
this.device.pcm,
this.buffer.as_ptr(),
this.period.into(),
)
.unwrap()
}
}
st => {
eprintln!(
"Incorrect state = {:?} (XRUN): Report Bug to \
https://github.com/libcala/wavy/issues/new",
st
);
unreachable!()
}
}
}
-77 => {
eprintln!(
"Incorrect state (-EBADFD): Report Bug to \
https://github.com/libcala/wavy/issues/new"
);
unreachable!()
}
-86 => {
eprintln!(
"Stream got suspended, trying to recover… \
(-ESTRPIPE)"
);
unsafe {
let _ = asound::pcm::resume(this.device.pcm);
asound::pcm::prepare(this.device.pcm).unwrap();
asound::pcm::writei(
this.device.pcm,
this.buffer.as_ptr(),
this.period.into(),
)
.unwrap()
}
}
_ => unreachable!(),
}
}
};
this.buffer.drain(..len * this.channels as usize);
this.starti = this.buffer.len() / this.channels as usize;
this.buffer
.resize(this.period as usize * this.channels as usize, Ch32::MID);
Poll::Ready(())
}
}
pub(crate) struct SpeakersSink<'a, F: Frame<Chan = Ch32>>(
&'a mut Speakers,
Resampler<F>,
PhantomData<F>,
);
impl<F: Frame<Chan = Ch32>> Sink<F> for SpeakersSink<'_, F> {
fn sample_rate(&self) -> f64 {
self.0.sample_rate.unwrap()
}
fn resampler(&mut self) -> &mut Resampler<F> {
&mut self.1
}
fn buffer(&mut self) -> &mut [F] {
let data = self.0.buffer.as_mut_ptr().cast();
let count = self.0.period.into();
unsafe {
&mut std::slice::from_raw_parts_mut(data, count)[self.0.starti..]
}
}
}
impl<F: Frame<Chan = Ch32>> Drop for SpeakersSink<'_, F> {
fn drop(&mut self) {
let frame: Surround32 = self.1.frame().convert();
self.0.resampler.0 = [
frame.channels()[0],
frame.channels()[1],
frame.channels()[2],
frame.channels()[3],
frame.channels()[4],
frame.channels()[5],
];
self.0.resampler.1 = self.1.index() % 1.0;
}
}