lasprs 0.4.1

Library for Acoustic Signal Processing (Rust edition, with optional Python bindings via pyo3)
Documentation
//! 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::*;

/// 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::newSine(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);

    }

}