use std::{ffi::{c_int, c_void}, sync::mpsc, mem::swap}; use fl2k_ampliphase::{fl2k_dev_t, fl2k_get_device_count, fl2k_open, fl2k_close, fl2k_stop_tx, fl2k_set_sample_rate, fl2k_get_sample_rate, fl2k_start_tx, FL2K_BUF_LEN}; const BUF_LEN : usize = FL2K_BUF_LEN as usize; #[derive(Debug)] pub enum FL2KError { InvalidParam, NoDevice, NotFound, Busy, Timeout, NoMem, Unknown(c_int) } fn handle_return_value(val: c_int) -> Result<(), FL2KError> { if val == fl2k_ampliphase::fl2k_error_FL2K_SUCCESS { Ok(()) } else if val == fl2k_ampliphase::fl2k_error_FL2K_TRUE { Ok(()) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_INVALID_PARAM { Err(FL2KError::InvalidParam) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_NO_DEVICE { Err(FL2KError::NoDevice) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_NOT_FOUND { Err(FL2KError::NotFound) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_BUSY { Err(FL2KError::Busy) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_TIMEOUT { Err(FL2KError::Timeout) } else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_NO_MEM { Err(FL2KError::NoMem) } else { Err(FL2KError::Unknown(val)) } } pub fn get_device_count() -> u32 { unsafe { fl2k_get_device_count() } } pub struct FL2K { device: *mut fl2k_dev_t, callback_ctx: Box, tx : mpsc::SyncSender<(Vec, Vec)>, r_buf : Vec, g_buf : Vec } pub struct CallbackCtx { // All three buffers must have same length! r_buf: Vec, g_buf: Vec, rg_bufs_channel: mpsc::Receiver<(Vec, Vec)>, underflow_count: u32, buffer_underflows: u32, abort: bool, } extern "C" fn tx_callback(data_info: *mut fl2k_ampliphase::fl2k_data_info_t) { unsafe { let ctx = (*data_info).ctx as *mut CallbackCtx; if (*data_info).device_error != 0 { (*ctx).abort = true; } else { (*ctx).underflow_count = (*data_info).underflow_cnt; match (*ctx).rg_bufs_channel.try_recv() { Ok((r, g)) => { (*data_info).sampletype_signed = 1; (*ctx).r_buf = r; (*ctx).g_buf = g; (*data_info).r_buf = (*ctx).r_buf.as_mut_ptr(); (*data_info).g_buf = (*ctx).g_buf.as_mut_ptr(); }, Err(_) => { (*ctx).buffer_underflows += 1; eprintln!("FL2K buffers not ready on callback"); }, } } } } impl FL2K { pub fn open(device_index: u32) -> Result { let (tx, rx) = mpsc::sync_channel(2); let ctx = CallbackCtx { r_buf: Vec::new(), g_buf: Vec::new(), rg_bufs_channel: rx, underflow_count: 0, buffer_underflows: 0, abort: false, }; unsafe { let r_buf = Vec::with_capacity(2 * BUF_LEN); let g_buf = Vec::with_capacity(2 * BUF_LEN); let mut fl2k = FL2K { device: std::mem::zeroed(), callback_ctx: Box::new(ctx), tx, r_buf, g_buf }; handle_return_value(fl2k_open(&mut fl2k.device, device_index))?; Ok(fl2k) } } pub fn set_sample_rate(&mut self, sample_rate: u32) -> Result<(), FL2KError> { let r = unsafe { fl2k_set_sample_rate(self.device, sample_rate) }; if r < 0 { handle_return_value(r) } else { Ok(()) } } pub fn get_sample_rate(&mut self) -> Result { let sr = unsafe { fl2k_get_sample_rate(self.device) }; if sr == 0 { Err(FL2KError::Unknown(0)) } else { Ok(sr) } } pub fn start_tx(&mut self) -> Result<(), FL2KError> { let r = unsafe { fl2k_start_tx(self.device, Some(tx_callback), self.callback_ctx.as_mut() as *mut CallbackCtx as *mut c_void, 0) }; handle_return_value(r) } pub fn stop_tx(&self) -> Result<(), FL2KError> { handle_return_value( unsafe { fl2k_stop_tx(self.device) } ) } pub fn send(&mut self, mut r_buf: Vec, mut g_buf: Vec) -> bool { if r_buf.len() != g_buf.len() { panic!("r_buf and g_buf must have same length"); } self.r_buf.append(&mut r_buf); self.g_buf.append(&mut g_buf); if self.r_buf.len() >= BUF_LEN { let mut r = self.r_buf.split_off(BUF_LEN); let mut g = self.g_buf.split_off(BUF_LEN); /* self.r_buf contains head, r contains tail. Swap them, as we want to give the head and keep the tail. */ swap(&mut r, &mut self.r_buf); swap(&mut g, &mut self.g_buf); self.tx.send((r, g)).is_ok() } else { true } } } impl Drop for FL2K { fn drop(&mut self) { match unsafe { handle_return_value(fl2k_close(self.device)) } { Ok(_) => (), Err(e) => eprintln!("Failed to close FL2K: {:?}", e), } } }