#![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::*; /// This is a keyword reserved for the "arg4" slot of a scalar callback, where args are numbered 1-4. pub const AUDIO_CB_ROUTING_ID: usize = 0; #[derive(Debug)] pub struct Codec { conn: CID, frame_sid: Option, } impl Codec { pub fn new(xns: &xous_names::XousNames) -> Result { REFCOUNT.fetch_add(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(|_| ()) } pub fn abort(&mut self) -> Result<(), xous::Error> { send_message(self.conn, Message::new_scalar(Opcode::AbortStream.to_usize().unwrap(), 0, 0, 0, 0) ).map(|_| ()) } /// gain goes from 0dB down to -80dB pub fn set_speaker_volume(&self, op: VolumeOps, gain: Option) -> Result<(), xous::Error> { let code = if let Some(g) = gain { if g > 0.0 { 0 as usize } else { (-g * 10.0) as usize } } else { 800 // -80.0 dB => mute }; send_message(self.conn, Message::new_scalar(Opcode::SetSpeakerVolume.to_usize().unwrap(), op.to_usize().unwrap(), code, // gain as -dB * 10 as usize 0, 0 ) ).map(|_| ()) } /// gain goes from 0dB down to -80dB pub fn set_headphone_volume(&self, op: VolumeOps, gain: Option) -> Result<(), xous::Error> { let code = if let Some(g) = gain { if g > 0.0 { 0 as usize } else { (-g * 10.0) as usize } } else { 800 // -80.0 dB => mute }; send_message(self.conn, Message::new_scalar(Opcode::SetHeadphoneVolume.to_usize().unwrap(), op.to_usize().unwrap(), code, // gain as -dB * 10 as usize 0, 0 ) ).map(|_| ()) } pub fn is_running(&self) -> Result { match send_message(self.conn, Message::new_blocking_scalar(Opcode::IsLive.to_usize().unwrap(), 0, 0, 0, 0) ) { Ok(xous::Result::Scalar1(is_live)) => { if is_live != 0 { Ok(true) } else { Ok(false) } } _ => Err(xous::Error::InternalError) } } } 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.fetch_sub(1, Ordering::Relaxed) == 1 { 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, AUDIO_CB_ROUTING_ID) ).unwrap(); }), Some(EventCallback::Drop) => { break; // this exits the loop and kills the thread } None => (), } } xous::destroy_server(sid).unwrap(); }