use crate::mmal::{self, Component, Event, Mmal, Pool, PortAction, PortInfo};
use crate::timer::Timer;
use crate::vchiq::Vchiq;
const COMPONENT: &str = "ril.audio_render";
const PARAMETER_AUDIO_DESTINATION: u32 = 3 << 16;
pub const BITS_PER_SAMPLE: u32 = 16;
const BYTES_PER_SAMPLE: usize = BITS_PER_SAMPLE as usize / 8;
pub const MAX_BUFFERS: usize = mmal::POOL_CAPACITY;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Destination {
Hdmi,
Local,
}
impl Destination {
fn as_str(self) -> &'static str {
match self {
Destination::Hdmi => "hdmi",
Destination::Local => "local",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Format {
pub sample_rate: u32,
pub channels: u32,
}
impl Format {
fn samples_per_frame(&self) -> usize {
(self.channels as usize).max(1)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
Mmal(mmal::Error),
TooManyBuffers,
NoBuffers,
BufferTooSmall {
provided: usize,
required: usize,
},
WrongState,
RendererError,
}
impl From<mmal::Error> for Error {
fn from(error: mmal::Error) -> Self {
Error::Mmal(error)
}
}
pub struct AudioRenderer {
mmal: Mmal,
component: Component,
control: PortInfo,
input: PortInfo,
pool: Pool,
format: Format,
in_flight: usize,
started: bool,
}
impl AudioRenderer {
pub fn new(
vchiq: Vchiq,
destination: Destination,
format: Format,
timer: &Timer,
) -> Result<Self, Error> {
let mut mmal = Mmal::new(vchiq, timer)?;
let component = mmal.component_create(COMPONENT, timer)?;
let control = mmal.port_info_get(&component, mmal::PORT_TYPE_CONTROL, 0, timer)?;
mmal.parameter_set_string(
&control,
PARAMETER_AUDIO_DESTINATION,
destination.as_str(),
timer,
)?;
let mut input = mmal.port_info_get(&component, mmal::PORT_TYPE_INPUT, 0, timer)?;
input.es_type = mmal::ES_TYPE_AUDIO;
input.encoding = mmal::ENCODING_PCM_SIGNED_LE;
input.encoding_variant = 0;
input.audio = mmal::AudioFormat {
channels: format.channels,
sample_rate: format.sample_rate,
bits_per_sample: BITS_PER_SAMPLE,
block_align: 0,
};
Ok(Self {
mmal,
component,
control,
input,
pool: Pool::new(),
format,
in_flight: 0,
started: false,
})
}
pub fn add_buffer(&mut self, buffer: &'static mut [u8]) -> Result<(), Error> {
if self.started {
return Err(Error::WrongState);
}
if !self.pool.add(buffer) {
return Err(Error::TooManyBuffers);
}
Ok(())
}
pub fn start(&mut self, timer: &Timer) -> Result<(), Error> {
if self.started {
return Err(Error::WrongState);
}
if self.pool.total() == 0 {
return Err(Error::NoBuffers);
}
let smallest = self.pool.smallest();
if smallest < self.input.buffer_size_min as usize {
return Err(Error::BufferTooSmall {
provided: smallest,
required: self.input.buffer_size_min as usize,
});
}
self.input.buffer_num = self.pool.total().max(self.input.buffer_num_min as usize) as u32;
self.input.buffer_size = smallest as u32;
self.mmal.port_info_set(&mut self.input, timer)?;
self.mmal.component_enable(&self.component, timer)?;
self.mmal
.port_action(&self.input, PortAction::Enable, timer)?;
self.input.enabled = true;
self.started = true;
Ok(())
}
pub fn feed(&mut self, samples: &[i16], pts: i64, timer: &Timer) -> Result<usize, Error> {
if !self.started {
return Err(Error::WrongState);
}
let Some(buffer) = self.pool.take() else {
return Ok(0);
};
let frame = self.format.samples_per_frame();
let capacity = buffer.len() / BYTES_PER_SAMPLE;
let taken = (samples.len().min(capacity) / frame) * frame;
if taken == 0 {
self.pool.put(buffer);
return Ok(0);
}
let (words, _) = buffer.as_chunks_mut::<BYTES_PER_SAMPLE>();
for (bytes, sample) in words.iter_mut().zip(&samples[..taken]) {
*bytes = sample.to_le_bytes();
}
self.mmal
.send_buffer(&self.input, buffer, taken * BYTES_PER_SAMPLE, 0, pts, timer)?;
self.in_flight += 1;
Ok(taken)
}
pub fn poll(&mut self) -> Result<(), Error> {
while let Some(event) = self.mmal.poll()? {
match event {
Event::Buffer { buffer, .. } => {
self.in_flight = self.in_flight.saturating_sub(1);
self.pool.put(buffer);
}
Event::PortEvent { cmd, .. } => {
if cmd == mmal::EVENT_ERROR {
return Err(Error::RendererError);
}
}
}
}
Ok(())
}
pub fn buffers_in_flight(&self) -> usize {
self.in_flight
}
pub fn format(&self) -> Format {
self.format
}
pub fn mmal(&mut self) -> &mut Mmal {
&mut self.mmal
}
pub fn input_port(&self) -> &PortInfo {
&self.input
}
pub fn control_port(&self) -> &PortInfo {
&self.control
}
}