303 lines
9.9 KiB
Rust
303 lines
9.9 KiB
Rust
//! Provides stream messages that come from a running stream
|
|
use crate::config::*;
|
|
use crate::daq::Qty;
|
|
use crate::siggen::Siggen;
|
|
use anyhow::{bail, Result};
|
|
use cpal::Sample;
|
|
use crossbeam::channel::Sender;
|
|
use reinterpret::{reinterpret_slice, reinterpret_vec};
|
|
use std::any::TypeId;
|
|
use std::sync::{Arc, RwLock};
|
|
use std::u128::MAX;
|
|
use strum_macros::Display;
|
|
|
|
use super::*;
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "python-bindings")] {
|
|
use pyo3::exceptions::PyValueError;
|
|
use pyo3::prelude::*;
|
|
use pyo3::{pymodule, pyclass, types::PyModule, PyResult};
|
|
} else {} }
|
|
|
|
/// Raw stream data coming from a stream.
|
|
#[derive(Clone, Debug)]
|
|
pub enum RawStreamData {
|
|
/// 8-bits integer
|
|
Datai8(Vec<i8>),
|
|
/// 16-bits integer
|
|
Datai16(Vec<i16>),
|
|
/// 32-bits integer
|
|
Datai32(Vec<i32>),
|
|
/// 32-bits float
|
|
Dataf32(Vec<f32>),
|
|
/// 64-bits float
|
|
Dataf64(Vec<f64>),
|
|
|
|
/// A stream error occured
|
|
StreamError(StreamError),
|
|
}
|
|
|
|
impl RawStreamData {
|
|
pub fn toFloat(&self, _nchannels: usize) -> Dmat {
|
|
// match &self {
|
|
// RawStreamData::Datai8(c) => {
|
|
// Dmat::zeros((2, 2));
|
|
// }
|
|
// RawStreamData::Datai16(c) => {
|
|
// Dmat::zeros((2, 2));
|
|
// }
|
|
// }
|
|
todo!()
|
|
}
|
|
}
|
|
impl RawStreamData {
|
|
/// Get reference to raw data buffer
|
|
pub fn getRef<T>(&self) -> &[T]
|
|
where
|
|
T: Sample + 'static,
|
|
{
|
|
let thetype: TypeId = TypeId::of::<T>();
|
|
match &self {
|
|
RawStreamData::Datai8(t) => {
|
|
let i8type: TypeId = TypeId::of::<i8>();
|
|
assert!(thetype == i8type);
|
|
let v: &[T] = unsafe { reinterpret_slice(t) };
|
|
v
|
|
}
|
|
RawStreamData::Datai16(t) => {
|
|
let i16type: TypeId = TypeId::of::<i16>();
|
|
assert!(thetype == i16type);
|
|
let v: &[T] = unsafe { reinterpret_slice(t) };
|
|
v
|
|
}
|
|
RawStreamData::Datai32(t) => {
|
|
let i32type: TypeId = TypeId::of::<i32>();
|
|
assert!(thetype == i32type);
|
|
let v: &[T] = unsafe { reinterpret_slice(t) };
|
|
v
|
|
}
|
|
RawStreamData::Dataf32(t) => {
|
|
let f32type: TypeId = TypeId::of::<f32>();
|
|
assert!(thetype == f32type);
|
|
let v: &[T] = unsafe { reinterpret_slice(t) };
|
|
v
|
|
}
|
|
RawStreamData::Dataf64(t) => {
|
|
let f64type: TypeId = TypeId::of::<f64>();
|
|
assert!(thetype == f64type);
|
|
let v: &[T] = unsafe { reinterpret_slice(t) };
|
|
v
|
|
}
|
|
_ => panic!("Cannot getRef from "),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create InStreamData object from
|
|
impl<T> From<&[T]> for RawStreamData
|
|
where
|
|
T: num::ToPrimitive + Clone + 'static,
|
|
{
|
|
fn from(input: &[T]) -> RawStreamData {
|
|
// Apparently, this code does not work with a match. I have searched around and have not found the
|
|
// reason for this. So this is a bit of stupid boilerplate.
|
|
let i8type: TypeId = TypeId::of::<i8>();
|
|
let i16type: TypeId = TypeId::of::<i16>();
|
|
let i32type: TypeId = TypeId::of::<i32>();
|
|
let f32type: TypeId = TypeId::of::<f32>();
|
|
let f64type: TypeId = TypeId::of::<f64>();
|
|
let thetype: TypeId = TypeId::of::<T>();
|
|
if i8type == thetype {
|
|
let v: Vec<i8> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Datai8(v)
|
|
} else if i16type == thetype {
|
|
let v: Vec<i16> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Datai16(v)
|
|
} else if i16type == thetype {
|
|
let v: Vec<i16> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Datai16(v)
|
|
} else if i32type == thetype {
|
|
let v: Vec<i32> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Datai32(v)
|
|
} else if f32type == thetype {
|
|
let v: Vec<f32> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Dataf32(v)
|
|
} else if f64type == thetype {
|
|
let v: Vec<f64> = unsafe { reinterpret_slice(input).to_vec() };
|
|
RawStreamData::Dataf64(v)
|
|
} else {
|
|
panic!("Not implemented sample type!")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create InStreamData object from
|
|
impl<T> From<Vec<T>> for RawStreamData
|
|
where
|
|
T: num::ToPrimitive + Clone + 'static,
|
|
{
|
|
fn from(input: Vec<T>) -> RawStreamData {
|
|
// Apparently, this code does not work with a match. I have searched around and have not found the
|
|
// reason for this. So this is a bit of stupid boilerplate.
|
|
let i8type: TypeId = TypeId::of::<i8>();
|
|
let i16type: TypeId = TypeId::of::<i16>();
|
|
let i32type: TypeId = TypeId::of::<i32>();
|
|
let f32type: TypeId = TypeId::of::<f32>();
|
|
let f64type: TypeId = TypeId::of::<f64>();
|
|
let thetype: TypeId = TypeId::of::<T>();
|
|
if i8type == thetype {
|
|
let v: Vec<i8> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Datai8(v)
|
|
} else if i16type == thetype {
|
|
let v: Vec<i16> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Datai16(v)
|
|
} else if i16type == thetype {
|
|
let v: Vec<i16> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Datai16(v)
|
|
} else if i32type == thetype {
|
|
let v: Vec<i32> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Datai32(v)
|
|
} else if f32type == thetype {
|
|
let v: Vec<f32> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Dataf32(v)
|
|
} else if f64type == thetype {
|
|
let v: Vec<f64> = unsafe { reinterpret_vec(input) };
|
|
RawStreamData::Dataf64(v)
|
|
} else {
|
|
panic!("Not implemented sample type!")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Stream metadata. All information required for properly interpreting the raw
|
|
/// data that is coming from the stream.
|
|
#[derive(Clone, Debug)]
|
|
pub struct StreamMetaData {
|
|
/// Information for each channel in the stream
|
|
pub channelInfo: Vec<DaqChannel>,
|
|
|
|
/// The data type of the device [Number / voltage / Acoustic pressure / ...]
|
|
pub rawDatatype: DataType,
|
|
|
|
/// Sample rate in [Hz]
|
|
pub samplerate: Flt,
|
|
|
|
/// The number of frames per block of data that comes in. Multiplied by
|
|
/// channelInfo.len() we get the total number of samples that come in at
|
|
/// each callback.
|
|
pub framesPerBlock: usize,
|
|
}
|
|
impl StreamMetaData {
|
|
/// Create new metadata object.
|
|
/// ///
|
|
/// # Args
|
|
///
|
|
pub fn new(
|
|
channelInfo: &[DaqChannel],
|
|
rawdtype: DataType,
|
|
sr: Flt,
|
|
framesPerBlock: usize,
|
|
) -> Result<StreamMetaData> {
|
|
Ok(StreamMetaData {
|
|
channelInfo: channelInfo.to_vec(),
|
|
rawDatatype: rawdtype,
|
|
samplerate: sr,
|
|
framesPerBlock,
|
|
})
|
|
}
|
|
|
|
/// Returns the number of channels in the stream metadata.
|
|
pub fn nchannels(&self) -> usize {
|
|
self.channelInfo.len()
|
|
}
|
|
}
|
|
|
|
/// Stream data (audio / other) coming from a stream or to be send to a stream
|
|
#[derive(Debug)]
|
|
pub struct StreamData {
|
|
/// Package counter. Should always increase monotonically.
|
|
pub ctr: usize,
|
|
|
|
/// Stream metadata. All info required for properly interpreting the raw data.
|
|
pub meta: Arc<StreamMetaData>,
|
|
|
|
/// This is typically what is stored when recording
|
|
pub raw: RawStreamData,
|
|
|
|
// Converted to floating point format. Used for further real time
|
|
// processing. Stored in an rw-lock. The first thread that acesses this data
|
|
// will perform the conversion. All threads after that will get the data.
|
|
converted: RwLock<Option<Arc<Dmat>>>,
|
|
}
|
|
impl StreamData {
|
|
/// Create new stream data object.
|
|
pub fn new(ctr: usize, meta: Arc<StreamMetaData>, raw: RawStreamData) -> StreamData {
|
|
StreamData {
|
|
ctr,
|
|
meta,
|
|
raw,
|
|
converted: RwLock::new(None),
|
|
}
|
|
}
|
|
|
|
/// Get the data in floating point format. If already converted, uses the
|
|
/// cached float data.
|
|
pub fn getFloatData(&self) -> Arc<Dmat> {
|
|
if let Some(dat) = self.converted.read().unwrap().as_ref() {
|
|
return dat.clone();
|
|
}
|
|
|
|
// In case we reach here, the data has not yet be converted to floating
|
|
// point, so we do this.
|
|
let mut o = self.converted.write().unwrap();
|
|
|
|
// It might be that another thread was 'first', and already performed
|
|
// the conversion. In that case, we still do an early return, and we
|
|
// just openend the lock twice for writing. Not a problem.
|
|
if let Some(dat) = o.as_ref() {
|
|
return dat.clone();
|
|
}
|
|
// Perform the actual conversion
|
|
let converted_data = Arc::new(self.raw.toFloat(self.meta.nchannels()));
|
|
// Replace the option with the Some
|
|
o.replace(converted_data.clone());
|
|
|
|
converted_data
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use num::traits::sign;
|
|
use cpal::Sample;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test() {
|
|
|
|
const fs: Flt = 20.;
|
|
// Number of samples per channel
|
|
const Nframes: usize = 20;
|
|
const Nch: usize = 2;
|
|
let mut signal = [0.; Nch*Nframes];
|
|
let mut siggen = Siggen::newSineWave(Nch, 1.);
|
|
|
|
siggen.reset(fs);
|
|
siggen.setMute(&[false, true]);
|
|
siggen.genSignal(&mut signal);
|
|
|
|
let raw: Vec<i16> = Vec::from_iter(signal.iter().map(
|
|
|o| o.to_sample::<i16>()));
|
|
|
|
let ms1 = raw.iter().step_by(2).map(|s1| {*s1 as f64 * *s1 as f64}).sum::<f64>() / Nframes as f64;
|
|
|
|
let i16maxsq = (i16::MAX as f64).powf(2.);
|
|
// println!("ms1: {} {}", ms1, i16maxsq/2.);
|
|
// println!("{:?}", raw.iter().cloned().step_by(2).collect::<Vec<i16>>());
|
|
// println!("{:?}", i16::EQUILIBRIUM);
|
|
assert!(f64::abs(ms1 - i16maxsq/2.)/i16maxsq < 1e-3);
|
|
|
|
}
|
|
|
|
} |