use super::*;
use crate::daq::streamerror::{OutputUnderrunSnafu, StreamError, SystemSnafu};
use crate::tools::find_unused_buf;
use core::time;
use parking_lot::{Condvar, Mutex, RwLock};
use snafu::prelude::*;
use std::collections::VecDeque;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};
use std::{any::Any, fmt::Debug, sync::Arc};
type Result<T> = std::result::Result<T, StreamMgrError>;
const SAMPLERATE: Flt = 48000.;
const NCHANNELS: usize = 2;
const RUNINTIME: Flt = 2.;
#[derive(Debug, Clone)]
struct StreamChannels {
output_thread_sender: Sender<Arc<Vec<f32>>>,
instream_running: Arc<AtomicBool>,
outstream_running: Arc<AtomicBool>,
input_thread_receiver: Receiver<Arc<Vec<f32>>>,
}
#[derive(Debug)]
pub struct LoopbackApi {
channels: StreamChannels,
}
impl LoopbackApi {
pub fn new() -> Self {
let (tx, rx) = bounded(1);
Self {
channels: StreamChannels {
instream_running: Arc::new(AtomicBool::new(false)),
outstream_running: Arc::new(AtomicBool::new(false)),
output_thread_sender: tx,
input_thread_receiver: rx,
},
}
}
fn generalChecks(
&self,
dev: &DeviceInfo,
cfg: &DaqConfig,
) -> Result<(usize, StrictlyPositive)> {
ensure!(
*dev == self.getDeviceInfo().expect("Should not give errors")[0],
DeviceNotAvailableSnafu {
device_name: dev.device_name.clone()
}
);
for in_ch_unavailable in cfg.inchannel_config.iter().skip(dev.iChannelCount as usize) {
ensure!(
!in_ch_unavailable.enabled,
DAQConfigSnafu {
msg: "Too high enabled input channels",
}
);
}
for out_ch_unavailable in cfg
.outchannel_config
.iter()
.skip(dev.oChannelCount as usize)
{
ensure!(
!out_ch_unavailable.enabled,
DAQConfigSnafu {
msg: "Too high enabled output channels",
}
);
}
let framesPerBlock =
*dev.avFramesPerBlock
.get(cfg.framesPerBlockIndex)
.context(DAQConfigSnafu {
msg: "Frames per block index out of range",
})?;
let sampleRate = *dev
.avSampleRates
.get(cfg.sampleRateIndex)
.context(DAQConfigSnafu {
msg: "Samplerate index out of range",
})?;
ensure!(
dev.avDataTypes.contains(&cfg.dtype),
DAQConfigSnafu {
msg: "Datatype not available for device",
}
);
Ok((
framesPerBlock,
sampleRate.try_into().context(DAQConfigValidationSnafu)?,
))
}
}
impl DaqApiMethods for LoopbackApi {
fn getDeviceInfo(&self) -> Result<Vec<DeviceInfo>> {
Ok(vec![DeviceInfo {
api: DaqApiDescriptor::Loopback,
device_name: String::from("Loopback"),
avDataTypes: vec![DataType::F32],
prefDataType: DataType::F32,
avFramesPerBlock: vec![512],
prefFramesPerBlock: 512,
avSampleRates: vec![SAMPLERATE],
prefSampleRate: SAMPLERATE,
iChannelCount: 2,
oChannelCount: 2,
hasInputIEPE: false,
hasInputACCouplingSwitch: false,
hasInputTrigger: false,
avInputRanges: vec![(-1.0, 1.0)],
avOutputRanges: vec![(-1.0, 1.0)],
physicalIOQty: Qty::Number,
hasDuplexMode: false,
duplexModeForced: false,
hasInternalOutputMonitor: false,
}])
}
fn startInputOrDuplexStream(
&self,
stype: StreamType,
devinfo: &DeviceInfo,
conf: &DaqConfig,
sender: Sender<InStreamMsg>,
receiver: Option<Receiver<Arc<RawStreamData>>>,
) -> Result<Box<dyn Stream>> {
let (framesPerBlock, sampleRate) = self.generalChecks(devinfo, conf)?;
ensure!(
matches!(stype, StreamType::Input),
DAQConfigSnafu {
msg: "Duplex stream not supported for loopback API",
}
);
ensure!(
receiver.is_none(),
DAQConfigSnafu {
msg: "No receiver should be applied",
}
);
ensure!(
conf.inchannel_config.len() >= NCHANNELS,
DAQConfigSnafu {
msg: format!(
"Too few input channels configurations specified. Device
requires at least {NCHANNELS} of input channels."
)
}
);
ensure!(
conf.inchannel_config
.iter()
.skip(NCHANNELS)
.filter(|ch| ch.enabled)
.count()
== 0,
DAQConfigSnafu {
msg: format!(
"Too high input channels enabled. Device only supports {NCHANNELS} of input channels."
)
}
);
ensure!(
conf.inchannel_config
.iter()
.take(NCHANNELS)
.filter(|ch| ch.enabled)
.count()
> 0,
DAQConfigSnafu {
msg: "No input channels enabled"
}
);
let stopThread = Arc::new(AtomicBool::new(false));
let stopThread2 = stopThread.clone();
let status = Arc::new(RwLock::new(StreamStatus::NotRunning {}));
let status2 = status.clone();
let channelInfo = conf
.inchannel_config
.iter()
.take(NCHANNELS)
.filter(|ch| ch.enabled)
.cloned()
.collect::<Vec<_>>();
let meta = Arc::new(StreamMetaData {
channelInfo,
rawDatatype: conf.dtype,
samplerate: sampleRate,
framesPerBlock,
physicalIOQty: devinfo.physicalIOQty,
});
let ch2 = self.channels.clone();
let devinfo2 = devinfo.clone();
let conf2 = conf.clone();
let meta2 = meta.clone();
spawn(
move || {
loopbackInputThreadFcn(devinfo2, conf2, stopThread2, status2, ch2, sender, meta2);
},
ThreadPriority::High,
);
Ok(Box::new(InputLoopbackStream {
stopThread,
meta,
status,
}))
}
fn startOutputStream(
&self,
dev: &DeviceInfo,
cfg: &DaqConfig,
receiver: Receiver<Arc<RawStreamData>>,
) -> Result<Box<dyn Stream>> {
let (framesPerBlock, samplerate) = self.generalChecks(dev, cfg)?;
ensure!(
cfg.outchannel_config.len() >= NCHANNELS,
DAQConfigSnafu {
msg: "Too few out channel configurations provided"
}
);
let out_cfg = &cfg.outchannel_config[..NCHANNELS];
ensure!(
out_cfg.iter().filter(|ch| ch.enabled).count() != 0,
DAQConfigSnafu {
msg: "No output channels enabled in stream"
}
);
let channelInfo = cfg
.outchannel_config
.iter()
.take(NCHANNELS)
.filter(|ch| ch.enabled)
.cloned()
.collect::<Vec<_>>();
let stopThread = Arc::new(AtomicBool::new(false));
let stopThread2 = stopThread.clone();
let status = Arc::new(RwLock::new(StreamStatus::NotRunning {}));
let status2 = status.clone();
let meta = Arc::new(StreamMetaData {
channelInfo,
rawDatatype: cfg.dtype,
samplerate,
framesPerBlock,
physicalIOQty: dev.physicalIOQty,
});
let dev_clone = dev.clone();
let cfg_clone = cfg.clone();
let channels_clone = self.channels.clone();
spawn(
move || {
loopbackOutputThreadFcn(
dev_clone,
cfg_clone,
stopThread2,
status2,
channels_clone,
receiver,
);
},
ThreadPriority::High,
);
Ok(Box::new(OutputLoopbackStream {
stopThread,
meta,
status,
}))
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
struct OutputLoopbackStream {
stopThread: Arc<AtomicBool>,
meta: Arc<StreamMetaData>,
status: Arc<RwLock<StreamStatus>>,
}
impl Drop for OutputLoopbackStream {
fn drop(&mut self) {
self.stopThread.store(true, Ordering::Relaxed);
}
}
impl Stream for OutputLoopbackStream {
fn inMetaData(&self) -> Option<Arc<StreamMetaData>> {
None
}
fn outMetaData(&self) -> Option<Arc<StreamMetaData>> {
Some(self.meta.clone())
}
fn status(&self, dir: StreamDirection) -> StreamStatus {
match dir {
StreamDirection::Input => StreamStatus::NotRunning {},
StreamDirection::Output => self.status.read().clone(),
}
}
}
#[derive(Debug)]
struct InputLoopbackStream {
stopThread: Arc<AtomicBool>,
meta: Arc<StreamMetaData>,
status: Arc<RwLock<StreamStatus>>,
}
impl Drop for InputLoopbackStream {
fn drop(&mut self) {
self.stopThread.store(true, Ordering::Relaxed);
}
}
impl Stream for InputLoopbackStream {
fn inMetaData(&self) -> Option<Arc<StreamMetaData>> {
Some(self.meta.clone())
}
fn outMetaData(&self) -> Option<Arc<StreamMetaData>> {
None
}
fn status(&self, dir: StreamDirection) -> StreamStatus {
match dir {
StreamDirection::Input => self.status.read().clone(),
StreamDirection::Output => StreamStatus::NotRunning {},
}
}
}
fn loopbackOutputThreadFcn(
dev: DeviceInfo,
cfg: DaqConfig,
stopThread: Arc<AtomicBool>,
status: Arc<RwLock<StreamStatus>>,
ch: StreamChannels,
siggenchannel: Receiver<Arc<RawStreamData>>,
) {
let framesPerBlock = cfg.framesPerBlock(&dev);
let fs = cfg.sampleRate(&dev);
let StreamChannels {
output_thread_sender,
instream_running,
outstream_running,
..
} = ch;
outstream_running.store(true, Ordering::Relaxed);
let run_in_blocks = (RUNINTIME * SAMPLERATE / (framesPerBlock as Flt)) as u64;
let outch_enabled = [
cfg.outchannel_config[0].enabled,
cfg.outchannel_config[1].enabled,
];
let noutch_enabled = outch_enabled.iter().copied().filter(|val| *val).count();
let mut ctr: u64 = 0;
let mut curtime = Instant::now();
{
let mut s = status.write();
*s = StreamStatus::newRunning();
}
let mut bufs: VecDeque<Arc<Vec<f32>>> = VecDeque::with_capacity(10);
let interval: Duration = Duration::from_micros((1e6 * framesPerBlock as Flt / fs) as u64);
let long_sleep = 9 * interval / 10;
'threadloop: while !stopThread.load(Ordering::Relaxed) {
if curtime.elapsed() < interval - long_sleep {
std::thread::sleep(long_sleep);
}
while curtime.elapsed() <= interval {
std::thread::sleep(interval / 100);
}
curtime += interval;
match siggenchannel.try_recv() {
Ok(dat) => match dat.as_ref() {
RawStreamData::Dataf32(items) => {
if items.len() != framesPerBlock * noutch_enabled {
let mut w = status.write();
*w = StreamStatus::Error {
e: StreamError::FramesMismatchError {},
};
outstream_running.store(true, Ordering::Relaxed);
return;
}
if instream_running.load(Ordering::Relaxed) {
let mut buf = find_unused_buf(&mut bufs)
.unwrap_or_else(|| Arc::new(vec![0.; framesPerBlock * NCHANNELS]));
let bufmut = Arc::get_mut(&mut buf).expect("Buffer in use?");
assert!(noutch_enabled > 0);
if noutch_enabled == 2 {
bufmut.copy_from_slice(items);
} else {
const {
assert!(NCHANNELS == 2);
}
assert!(noutch_enabled == 1);
bufmut
.iter_mut()
.skip(outch_enabled[1] as usize)
.step_by(2)
.zip(items)
.for_each(|(b, i)| *b = *i);
}
bufs.push_back(buf.clone());
if let Err(e) = output_thread_sender.try_send(buf) {
match e {
TrySendError::Full(_) => {
if instream_running.load(Ordering::Relaxed) {
let mut w = status.write();
*w = StreamStatus::Error {
e: StreamError::InputOverrun {},
};
outstream_running.store(false, Ordering::Relaxed);
return;
} else {
continue 'threadloop;
}
}
TrySendError::Disconnected(_) => {
unreachable!()
}
}
}
}
}
_ => {
let mut w = status.write();
*w = StreamStatus::Error {
e: StreamError::DTypeMismatchError {},
};
outstream_running.store(false, Ordering::Relaxed);
return;
}
},
Err(e) => {
match e {
TryRecvError::Empty => {
if ctr > run_in_blocks {
let mut w = status.write();
*w = StreamStatus::Error {
e: OutputUnderrunSnafu {}.build(),
};
outstream_running.store(false, Ordering::Relaxed);
return;
}
}
TryRecvError::Disconnected => {
let mut w = status.write();
*w = StreamStatus::Error {
e: SystemSnafu {
msg: "signal generator channel closed",
}
.build(),
};
outstream_running.store(false, Ordering::Relaxed);
return;
}
}
}
};
ctr += 1;
}
let mut w = status.write();
*w = StreamStatus::NotRunning {};
outstream_running.store(false, Ordering::Relaxed);
}
fn loopbackInputThreadFcn(
dev: DeviceInfo,
cfg: DaqConfig,
stopThread: Arc<AtomicBool>,
status: Arc<RwLock<StreamStatus>>,
ch: StreamChannels,
sender: Sender<InStreamMsg>,
meta: Arc<StreamMetaData>,
) {
let StreamChannels {
instream_running,
outstream_running,
input_thread_receiver,
..
} = ch;
let ninch_enabled = cfg.numberEnabledInChannels();
let framesPerBlock = cfg.framesPerBlock(&dev);
let fs = cfg.sampleRate(&dev);
let inch_enabled: [bool; 2] = array_init::array_init(|i| cfg.inchannel_config[i].enabled);
assert!(ninch_enabled > 0 && ninch_enabled <= 2);
instream_running.store(true, Ordering::Relaxed);
while !input_thread_receiver.is_empty() {
let _ = input_thread_receiver.recv();
}
let mut ctr = 0;
let mut bufs: VecDeque<Arc<RawStreamData>> = VecDeque::with_capacity(10);
{
let mut s = status.write();
*s = StreamStatus::newRunning();
}
while !stopThread.load(Ordering::Relaxed) {
let mut rawstreamdata = find_unused_buf(&mut bufs).unwrap_or_else(|| {
Arc::new(RawStreamData::Dataf32(vec![
0.;
framesPerBlock * ninch_enabled
]))
});
let RawStreamData::Dataf32(bufmut) =
Arc::get_mut(&mut rawstreamdata).expect("Should be mutable")
else {
unreachable!()
};
let interval: Duration = Duration::from_nanos((1e9 / fs * framesPerBlock as Flt) as u64);
let receiver_timeout = 2 * interval;
let long_sleep = 8 * interval / 10;
if outstream_running.load(Ordering::Relaxed) {
match input_thread_receiver.recv_timeout(receiver_timeout) {
Ok(buf) => {
if ninch_enabled == 2 {
bufmut.copy_from_slice(&buf);
} else {
debug_assert!(ninch_enabled == 1);
bufmut
.iter_mut()
.zip(buf.iter().skip(inch_enabled[1] as usize).step_by(2))
.for_each(|(out, inn)| *out = *inn);
}
}
Err(e) => match e {
RecvTimeoutError::Timeout => {
if outstream_running.load(Ordering::Relaxed) {
let mut s = status.write();
*s = StreamStatus::Error {
e: StreamError::SystemError {
msg: "output thread timeout".into(),
},
};
instream_running.store(false, Ordering::Relaxed);
return;
}
}
RecvTimeoutError::Disconnected => unreachable!(),
},
}
} else {
let curtime = Instant::now();
while curtime.elapsed() < interval {
if curtime.elapsed() < interval - long_sleep {
std::thread::sleep(long_sleep);
}
std::thread::sleep(interval / 100);
}
bufmut.fill(0.);
}
if sender
.send(InStreamMsg::InStreamData(Arc::new(
InStreamData::newFromRaw(ctr, meta.clone(), rawstreamdata),
)))
.is_err()
{
let mut w = status.write();
*w = StreamStatus::Error {
e: SystemSnafu {
msg: "sending message failure: system error",
}
.build(),
};
instream_running.store(false, Ordering::Relaxed);
return;
}
ctr += 1;
} let mut w = status.write();
*w = StreamStatus::NotRunning {};
instream_running.store(false, Ordering::Relaxed);
}