#![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, } impl Codec { pub fn new(xns: &xous_names::XousNames) -> Result { 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::().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(); }