xous-core/services/codec/src/lib.rs

115 lines
4.4 KiB
Rust

#![cfg_attr(target_os = "none", no_std)]
pub mod api;
use xous::{CID, send_message, Message};
use num_traits::{ToPrimitive, FromPrimitive};
use xous_ipc::Buffer;
pub use api::*;
#[derive(Debug)]
pub struct Codec {
conn: CID,
frame_sid: Option<xous::SID>,
}
impl Codec {
pub fn new(xns: &xous_names::XousNames) -> Result<Self, xous::Error> {
REFCOUNT.store(REFCOUNT.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
let conn = xns.request_connection_blocking(api::SERVER_NAME_CODEC).expect("Can't connect to Codec server");
Ok(Codec {
conn,
frame_sid: None,
})
}
pub fn hook_frame_callback(&mut self, id: u32, cid: CID) -> Result<(), xous::Error> {
if self.frame_sid.is_none() {
let sid = xous::create_server().unwrap();
self.frame_sid = Some(sid);
let sid_tuple = sid.to_u32();
xous::create_thread_4(frame_cb_server, sid_tuple.0 as usize, sid_tuple.1 as usize, sid_tuple.2 as usize, sid_tuple.3 as usize).unwrap();
let hookdata = ScalarHook {
sid: sid_tuple,
id,
cid,
};
let buf = Buffer::into_buf(hookdata).or(Err(xous::Error::InternalError))?;
buf.lend(self.conn, Opcode::AudioStreamSubscribe.to_u32().unwrap()).map(|_|())
} else {
Err(xous::Error::MemoryInUse) // can't hook it twice
}
}
pub fn setup_8k_stream(&mut self) -> Result<(), xous::Error> {
send_message(self.conn,
Message::new_scalar(Opcode::Setup8kStereo.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
pub fn power_off(&mut self) -> Result<(), xous::Error> {
send_message(self.conn,
Message::new_scalar(Opcode::PowerOff.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
pub fn free_frames(&mut self) -> Result<(usize, usize), xous::Error> {
let response = send_message(self.conn,
Message::new_blocking_scalar(Opcode::FreeFrames.to_usize().unwrap(), 0, 0, 0, 0))?;
if let xous::Result::Scalar2(play_free, rec_avail) = response {
Ok(
(play_free, rec_avail)
)
} else {
log::error!("unexpected return value: {:#?}", response);
Err(xous::Error::InternalError)
}
}
pub fn swap_frames(&mut self, frames: &mut FrameRing) -> Result<(), xous::Error> {
let mut buf = Buffer::into_buf(*frames).or(Err(xous::Error::InternalError))?;
buf.lend_mut(self.conn, Opcode::SwapFrames.to_u32().unwrap()).or(Err(xous::Error::InternalError))?;
*frames = buf.to_original::<FrameRing, _>().unwrap();
Ok(())
}
pub fn resume(&mut self) -> Result<(), xous::Error> {
send_message(self.conn,
Message::new_scalar(Opcode::ResumeStream.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
pub fn pause(&mut self) -> Result<(), xous::Error> {
send_message(self.conn,
Message::new_scalar(Opcode::PauseStream.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
}
use core::sync::atomic::{AtomicU32, Ordering};
static REFCOUNT: AtomicU32 = AtomicU32::new(0);
impl Drop for Codec {
fn drop(&mut self) {
// de-allocate myself. It's unsafe because we are responsible to make sure nobody else is using the connection.
if REFCOUNT.load(Ordering::Relaxed) == 0 {
unsafe{xous::disconnect(self.conn).unwrap();}
}
}
}
/// handles callback messages that indicate an audio frame has been used up, in the library user's process space.
fn frame_cb_server(sid0: usize, sid1: usize, sid2: usize, sid3: usize) {
let sid = xous::SID::from_u32(sid0 as u32, sid1 as u32, sid2 as u32, sid3 as u32);
loop {
let msg = xous::receive_message(sid).unwrap();
match FromPrimitive::from_usize(msg.body.id()) {
Some(EventCallback::Event) => xous::msg_scalar_unpack!(msg, cid, id, free_play, avail_rec, {
// directly pass the scalar message onto the CID with the ID memorized in the original hook
send_message(cid as u32,
Message::new_scalar(id, free_play, avail_rec, 0, 0)
).unwrap();
}),
Some(EventCallback::Drop) => {
break; // this exits the loop and kills the thread
}
None => (),
}
}
xous::destroy_server(sid).unwrap();
}