From 22a9c9f850383bc4ce519d5b7aa12596ee7184c5 Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Wed, 1 May 2024 15:25:26 +0200 Subject: [PATCH] Code cleanup with more use statements. Better Python wrapper code. --- Cargo.toml | 1 + python/lasprs/__init__.py | 2 +- src/bin/lasp_outputdefault.rs | 6 +-- src/config.rs | 26 ++++++++++--- src/daq/api/api_cpal.rs | 22 +++++------ src/daq/api/mod.rs | 12 ++++-- src/daq/daqconfig.rs | 5 +-- src/daq/datatype.rs | 2 + src/daq/deviceinfo.rs | 15 +++++++- src/daq/mod.rs | 21 ++++++++++- src/daq/qty.rs | 2 + src/daq/record.rs | 1 - src/daq/streamcmd.rs | 9 ----- src/daq/streamdata.rs | 6 --- src/daq/streammgr.rs | 69 ++++++++++++++++------------------- src/daq/streamstatus.rs | 18 ++++----- src/filter/biquad.rs | 12 +++--- src/filter/seriesbiquad.rs | 24 +++++++----- src/lib.rs | 22 ++++------- src/siggen.rs | 3 -- 20 files changed, 153 insertions(+), 125 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f44279d..329b26e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,3 +88,4 @@ record = ["dep:hdf5-sys", "dep:hdf5", "dep:chrono", "dep:uuid"] f64 = [] f32 = [] python-bindings = ["dep:pyo3", "dep:numpy"] +extension-module = [] diff --git a/python/lasprs/__init__.py b/python/lasprs/__init__.py index 8b13789..1894438 100644 --- a/python/lasprs/__init__.py +++ b/python/lasprs/__init__.py @@ -1 +1 @@ - +from ._lasprs import * diff --git a/src/bin/lasp_outputdefault.rs b/src/bin/lasp_outputdefault.rs index f044ea1..a376d1f 100644 --- a/src/bin/lasp_outputdefault.rs +++ b/src/bin/lasp_outputdefault.rs @@ -51,12 +51,12 @@ fn main() -> Result<()> { } sleep(100); match smgr.getStatus(StreamType::Output) { - StreamStatus::NotRunning => { + StreamStatus::NotRunning{} => { println!("Stream is not running?"); break 'infy; } - StreamStatus::Running => {} - StreamStatus::Error(e) => { + StreamStatus::Running{} => {} + StreamStatus::Error{e} => { println!("Stream error: {}", e); break 'infy; } diff --git a/src/config.rs b/src/config.rs index 97b0fde..fe3dad4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,19 +24,21 @@ cfg_if::cfg_if! { if #[cfg(feature = "python-bindings")] { pub use numpy::ndarray::{ArrayD, ArrayViewD, ArrayViewMutD}; pub use numpy::ndarray::prelude::*; - pub use numpy::{IntoPyArray, PyArray1, PyArrayDyn, PyArrayLike1, PyReadonlyArrayDyn}; - pub use pyo3::exceptions::PyValueError; + pub use numpy::{IntoPyArray,PyArray, PyArray1, PyArrayDyn, PyArrayLike1, PyReadonlyArrayDyn}; pub use pyo3::prelude::*; + pub use pyo3::exceptions::PyValueError; pub use pyo3::{pymodule, types::PyModule, PyResult}; + pub use pyo3::anyhow::*; + pub use pyo3; + } else { pub use ndarray::prelude::*; pub use ndarray::{Array1, Array2, ArrayView1}; } } -// pub use num::complex::i; -use num::complex::*; - +use ndarray::OwnedRepr; +use num::complex::Complex; /// View into 1D array of floats pub type VdView<'a> = ArrayView1<'a, Flt>; @@ -66,3 +68,17 @@ pub type Ccol = Array1; pub type Dmat = Array2; /// 2D array of complex floats pub type Cmat = Array2; + +cfg_if::cfg_if! { +if #[cfg(feature = "python-bindings")] { + + /// 1D array of T as returned from Rust to Numpy + pub type PyArr1<'py, T> = Bound<'py, PyArray>>; + + /// 1D array Floats returned from Rust to Numpy + pub type PyArr1Flt<'py> = PyArr1<'py, Flt>; + + /// 1D array of Complex returned from Rust to Numpy + pub type PyArr1Cflt<'py> = PyArr1<'py, Cflt>; + +}} \ No newline at end of file diff --git a/src/daq/api/api_cpal.rs b/src/daq/api/api_cpal.rs index 5da67bd..bc02125 100644 --- a/src/daq/api/api_cpal.rs +++ b/src/daq/api/api_cpal.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use super::Stream; use super::StreamMetaData; -use crate::daq::streamdata::*; +use crate::daq::{streamdata::*, StreamApiDescr}; use crate::config::{self, *}; use crate::daq::{self, *}; use anyhow::{bail, Result}; @@ -147,7 +147,7 @@ impl CpalApi { }; let prefSampleRate = *avSampleRates.last().unwrap_or(&48000.); devs.push(DeviceInfo { - api: super::StreamApiDescr::Cpal, + api: StreamApiDescr::Cpal, device_name: dev.name()?, avDataTypes: dtypes, prefDataType, @@ -186,7 +186,7 @@ impl CpalApi { if let Some(sender) = &send_ch { sender.send(RawStreamData::StreamError(serr)).unwrap(); } - status.store(StreamStatus::Error(serr)); + status.store(StreamStatus::Error{e: serr}); } } @@ -251,7 +251,7 @@ impl CpalApi { en_inchannels: Vec, framesPerBlock: usize, ) -> Result<(cpal::Stream, Arc>)> { - let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning)); + let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning{})); let errfcn = CpalApi::create_errfcn(Some(sender.clone()), status.clone()); @@ -301,7 +301,7 @@ impl CpalApi { let mut setToEquilibrium = || data.iter_mut().for_each(|v| *v = Sample::EQUILIBRIUM); match status { - StreamStatus::NotRunning | StreamStatus::Error(_) => { + StreamStatus::NotRunning{} | StreamStatus::Error{..} => { setToEquilibrium(); return; } @@ -312,7 +312,7 @@ impl CpalApi { // Obtain new samples from the generator for dat in receiver.try_iter() { let slice = dat.getRef::(); - if let StreamStatus::Running = status { + if let StreamStatus::Running{} = status { q.extend(slice); } } @@ -330,7 +330,7 @@ impl CpalApi { setToEquilibrium(); } else { // Output buffer underrun - streamstatus.store(StreamStatus::Error(StreamError::OutputUnderrunError)); + streamstatus.store(StreamStatus::Error{e:StreamError::OutputUnderrunError}); setToEquilibrium(); } } @@ -345,7 +345,7 @@ impl CpalApi { ) -> Result<(cpal::Stream, Arc>)> { // let tot_ch = config.channels as usize; - let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning)); + let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning{})); let err_cb = CpalApi::create_errfcn(None, status.clone()); macro_rules! build_stream{ @@ -472,7 +472,7 @@ impl CpalApi { )?; stream.play()?; - status.store(StreamStatus::Running); + status.store(StreamStatus::Running{}); return Ok(Box::new(CpalStream { stream, @@ -514,7 +514,7 @@ impl CpalApi { framesPerBlock, )?; stream.play()?; - status.store(StreamStatus::Running); + status.store(StreamStatus::Running{}); // Daq: default channel config let daqchannels = Vec::from_iter( @@ -574,7 +574,7 @@ impl CpalApi { )?; stream.play()?; - status.store(StreamStatus::Running); + status.store(StreamStatus::Running{}); // Daq: default channel config let daqchannels = diff --git a/src/daq/api/mod.rs b/src/daq/api/mod.rs index f481d7f..2526205 100644 --- a/src/daq/api/mod.rs +++ b/src/daq/api/mod.rs @@ -1,13 +1,14 @@ -use serde::{Deserialize, Serialize}; /// Daq apis that are optionally compiled in. Examples: /// /// - CPAL (Cross-Platform Audio Library) /// - ... +use serde::{Deserialize, Serialize}; +use std::sync::Arc; use strum::EnumMessage; use strum_macros; -use std::sync::Arc; +use crate::config::*; -use super::{streamstatus::StreamStatus, streamdata::StreamMetaData}; +use super::{streamdata::StreamMetaData, streamstatus::StreamStatus}; #[cfg(feature = "cpal-api")] pub mod api_cpal; @@ -26,9 +27,12 @@ pub trait Stream { /// Number of output channels in stream fn noutchannels(&self) -> usize; + /// Obtain stream status fn status(&self) -> StreamStatus; } +/// Stream API descriptor: type and corresponding text +#[cfg_attr(feature = "python-bindings", pyclass)] #[derive(strum_macros::EnumMessage, Debug, Clone, PartialEq, Serialize, Deserialize)] #[allow(dead_code)] pub enum StreamApiDescr { @@ -38,4 +42,4 @@ pub enum StreamApiDescr { /// PulseAudio api #[strum(message = "pulse", detailed_message = "Pulseaudio")] Pulse = 1, -} +} \ No newline at end of file diff --git a/src/daq/daqconfig.rs b/src/daq/daqconfig.rs index f21ff6b..f71b517 100644 --- a/src/daq/daqconfig.rs +++ b/src/daq/daqconfig.rs @@ -1,9 +1,6 @@ use std::{ops::Index, path::PathBuf}; -use super::api::StreamApiDescr; -use super::datatype::DataType; -use super::deviceinfo::DeviceInfo; -use super::qty::Qty; +use super::*; use crate::config::*; use anyhow::Result; use serde::{Deserialize, Serialize}; diff --git a/src/daq/datatype.rs b/src/daq/datatype.rs index ef0745b..d79fb66 100644 --- a/src/daq/datatype.rs +++ b/src/daq/datatype.rs @@ -3,10 +3,12 @@ use strum::EnumMessage; use strum_macros; use serde::{Serialize, Deserialize}; +use crate::config::*; /// Data type description for samples coming from a stream #[derive(strum_macros::EnumMessage, PartialEq, Copy, Debug, Clone, Serialize, Deserialize)] #[allow(dead_code)] +#[cfg_attr(feature = "python-bindings", pyclass)] pub enum DataType { /// 32-bit floats #[strum(message = "F32", detailed_message = "32-bits floating points")] diff --git a/src/daq/deviceinfo.rs b/src/daq/deviceinfo.rs index 3731854..883bda6 100644 --- a/src/daq/deviceinfo.rs +++ b/src/daq/deviceinfo.rs @@ -1,14 +1,16 @@ //! Data acquisition model. Provides abstract layers around DAQ devices. #![allow(non_snake_case)] -use super::api::StreamApiDescr; +use super::StreamApiDescr; use super::*; -use crate::config::*; +use crate::config::*; /// Device info structure. Gives all information regarding a device, i.e. the number of input and /// output channels, its name and available sample rates and types. #[derive(Clone, Debug)] #[allow(dead_code)] +#[cfg_attr(feature = "python-bindings", pyclass(get_all))] + pub struct DeviceInfo { /// The api in use for this device pub api: StreamApiDescr, @@ -17,9 +19,11 @@ pub struct DeviceInfo { pub device_name: String, /// Available data types for the sample + // #[pyo3(get)] pub avDataTypes: Vec, /// Preferred data type for device + // #[pyo3(get)] pub prefDataType: DataType, /// Available frames per block @@ -65,5 +69,12 @@ pub struct DeviceInfo { /// devices, this is typically a 'number' between +/- full scale. For some /// devices however, the output quantity corresponds to a physical signal, /// such a Volts. + // #[pyo3(get)] pub physicalIOQty: Qty, } +#[cfg_attr(feature = "python-bindings", pymethods)] +impl DeviceInfo { + fn __repr__(&self) -> String { + format!("{:?}", self) + } +} diff --git a/src/daq/mod.rs b/src/daq/mod.rs index c7c1305..f6853fc 100644 --- a/src/daq/mod.rs +++ b/src/daq/mod.rs @@ -22,9 +22,10 @@ pub use datatype::DataType; pub use deviceinfo::DeviceInfo; pub use qty::Qty; pub use streamhandler::StreamHandler; -pub use streammgr::StreamMgr; +pub use streammgr::*; pub use streammsg::InStreamMsg; pub use streamstatus::StreamStatus; +use api::*; #[cfg(feature = "record")] pub use record::*; @@ -46,8 +47,26 @@ pub enum StreamType { Duplex, } +#[cfg(feature = "python-bindings")] +/// Add Python classes from stream manager +pub fn add_py_classses(m: &Bound<'_, PyModule>) -> PyResult<()> { + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + Ok(()) +} + /// Errors that happen in a stream #[derive(strum_macros::EnumMessage, Debug, Clone, Display, Copy)] +#[cfg_attr(feature = "python-bindings", pyclass)] pub enum StreamError { /// Input overrun #[strum( diff --git a/src/daq/qty.rs b/src/daq/qty.rs index 3e5d7ff..ec17140 100644 --- a/src/daq/qty.rs +++ b/src/daq/qty.rs @@ -1,12 +1,14 @@ //! Physical quantities that are input / output of a daq device. Provides an enumeration for these. //! +use crate::config::*; use strum::EnumMessage; use strum_macros; use serde::{Serialize, Deserialize}; /// Physical quantities that are I/O of a Daq device. #[derive(PartialEq, Serialize, Deserialize, strum_macros::EnumMessage, Debug, Clone, Copy)] +#[cfg_attr(feature = "python-bindings", pyclass)] #[allow(dead_code)] pub enum Qty { /// Number diff --git a/src/daq/record.rs b/src/daq/record.rs index 29d6ee4..9cafaba 100644 --- a/src/daq/record.rs +++ b/src/daq/record.rs @@ -5,7 +5,6 @@ use clap::builder::OsStr; use crossbeam::atomic::AtomicCell; use hdf5::types::{VarLenArray, VarLenUnicode}; use hdf5::{dataset, datatype, Dataset, File, H5Type}; -use ndarray::ArrayView2; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; diff --git a/src/daq/streamcmd.rs b/src/daq/streamcmd.rs index 74dfbb1..f9f3ef2 100644 --- a/src/daq/streamcmd.rs +++ b/src/daq/streamcmd.rs @@ -1,11 +1,5 @@ use crate::siggen::*; use super::streammgr::SharedInQueue; -cfg_if::cfg_if! { -if #[cfg(feature = "python-bindings")] { - use pyo3::exceptions::PyValueError; - use pyo3::prelude::*; - use pyo3::{pymodule, pyclass, types::PyModule, PyResult}; -} else {} } /// Commands that can be sent to a running stream @@ -13,9 +7,6 @@ pub enum StreamCommand { /// Add a new queue to a running INPUT stream AddInQueue(SharedInQueue), - /// Remove a queue to a running INPUT stream - RemoveInQueue(SharedInQueue), - /// New signal generator config to be used in OUTPUT stream NewSiggen(Siggen), diff --git a/src/daq/streamdata.rs b/src/daq/streamdata.rs index fb7907e..5e73e2f 100644 --- a/src/daq/streamdata.rs +++ b/src/daq/streamdata.rs @@ -12,12 +12,6 @@ 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)] diff --git a/src/daq/streammgr.rs b/src/daq/streammgr.rs index 66d907d..4e71488 100644 --- a/src/daq/streammgr.rs +++ b/src/daq/streammgr.rs @@ -1,6 +1,6 @@ //! Data acquisition model. Provides abstract layers around DAQ devices. -use super::api::*; use super::*; +use super::config::*; use crate::{ config::*, siggen::{self, Siggen}, @@ -15,19 +15,14 @@ use crossbeam::{ }; use std::sync::{atomic::AtomicBool, Arc, Mutex}; use std::thread::{JoinHandle, Thread}; -use streamcmd::StreamCommand; use streamdata::*; +use streamcmd::StreamCommand; use streammsg::*; +use api::StreamApiDescr; + #[cfg(feature = "cpal-api")] -use super::api::api_cpal::CpalApi; - -cfg_if::cfg_if! { -if #[cfg(feature = "python-bindings")] { - use pyo3::exceptions::PyValueError; - use pyo3::prelude::*; - use pyo3::{pymodule, types::PyModule, PyResult}; -} else {} } +use super::api::{api_cpal::CpalApi, Stream}; /// Store a queue in a shared pointer, to share sending /// and receiving part of the queue. @@ -45,9 +40,9 @@ struct StreamInfo { /// Keep track of whether the stream has been created. To ensure singleton behaviour. static smgr_created: AtomicBool = AtomicBool::new(false); -#[cfg_attr(feature = "python-bindings", pyclass(unsendable))] /// Configure and manage input / output streams. /// +#[cfg_attr(feature = "python-bindings", pyclass(unsendable))] pub struct StreamMgr { // List of available devices devs: Vec, @@ -82,13 +77,23 @@ impl StreamMgr { StreamMgr::new() } - // #[pyo3(name = "unit")] - // #[staticmethod] - // /// See: [Biquad::unit()] - // pub fn unit_py() -> Biquad { - // Biquad::unit() - // } - // #[pyo3(name = "firstOrderHighPass")] + #[pyo3(name = "startDefaultInputStream")] + fn startDefaultInputStream_py(&mut self) -> PyResult<()> { + Ok(self.startDefaultInputStream()?) + } + #[pyo3(name = "startDefaultOutputStream")] + fn startDefaultOutputStream_py(&mut self) -> PyResult<()> { + Ok(self.startDefaultOutputStream()?) + } + #[pyo3(name = "getDeviceInfo")] + fn getDeviceInfo_py(&mut self) -> PyResult> { + Ok(self.getDeviceInfo()) + } + #[pyo3(name = "getStatus")] + fn getStatus_py(&self, st: StreamType) -> StreamStatus { + self.getStatus(st) + } + } impl Default for StreamMgr { fn default() -> Self { @@ -130,14 +135,14 @@ impl StreamMgr { if let Some(s) = &self.input_stream { s.stream.status() } else { - StreamStatus::NotRunning + StreamStatus::NotRunning{} } } StreamType::Output => { if let Some(s) = &self.output_stream { s.stream.status() } else { - StreamStatus::NotRunning + StreamStatus::NotRunning{} } } } @@ -171,8 +176,8 @@ impl StreamMgr { } /// Obtain a list of devices that are available for each available API - pub fn getDeviceInfo(&mut self) -> &Vec { - &self.devs + pub fn getDeviceInfo(&mut self) -> Vec { + self.devs.clone() } fn scanDeviceInfo(&self) -> Vec { @@ -227,14 +232,9 @@ impl StreamMgr { } } - // Remove queue from list - StreamCommand::RemoveInQueue(queue) => { - iqueues.retain(|q| !q.same_channel(&queue)) - } - // Stop this thread. Returns the queue StreamCommand::StopThread => { - sendMsgToAllQueues(&mut iqueues, InStreamMsg::StreamStopped); + sendMsgToAllQueuesRemoveUnused(&mut iqueues, InStreamMsg::StreamStopped); break 'infy; } StreamCommand::NewSiggen(_) => { @@ -249,7 +249,7 @@ impl StreamMgr { let streamdata = Arc::new(streamdata); let msg = InStreamMsg::StreamData(streamdata); - sendMsgToAllQueues(&mut iqueues, msg); + sendMsgToAllQueuesRemoveUnused(&mut iqueues, msg); ctr += 1; } } @@ -295,11 +295,6 @@ impl StreamMgr { panic!("Invalid message send to output thread: AddInQueue"); } - // Remove queue from list - StreamCommand::RemoveInQueue(_) => { - panic!("Invalid message send to output thread: RemoveInQueue"); - } - // Stop this thread. Returns the queue StreamCommand::StopThread => { break 'infy; @@ -422,7 +417,7 @@ impl StreamMgr { let meta = stream.metadata(); - sendMsgToAllQueues(iqueues, InStreamMsg::StreamStarted(meta.clone())); + sendMsgToAllQueuesRemoveUnused(iqueues, InStreamMsg::StreamStarted(meta.clone())); let (threadhandle, commtx) = self.startInputStreamThread(meta, rx); @@ -453,7 +448,7 @@ impl StreamMgr { let iqueues = self.instreamqueues.as_mut().unwrap(); let meta = stream.metadata(); - sendMsgToAllQueues(iqueues, InStreamMsg::StreamStarted(meta.clone())); + sendMsgToAllQueuesRemoveUnused(iqueues, InStreamMsg::StreamStarted(meta.clone())); let (threadhandle, commtx) = self.startInputStreamThread(meta, rx); @@ -582,7 +577,7 @@ impl Drop for StreamMgr { // Send to all queues, remove queues that are disconnected when found out // on the way. -fn sendMsgToAllQueues(iqueues: &mut InQueues, msg: InStreamMsg) { +fn sendMsgToAllQueuesRemoveUnused(iqueues: &mut InQueues, msg: InStreamMsg) { // Loop over queues. Remove queues that error when we try to send // to them iqueues.retain(|q| match q.try_send(msg.clone()) { diff --git a/src/daq/streamstatus.rs b/src/daq/streamstatus.rs index a93cf25..4554e94 100644 --- a/src/daq/streamstatus.rs +++ b/src/daq/streamstatus.rs @@ -2,24 +2,22 @@ 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 {} } -/// Gives the stream status of a stream, either input / output or duplex. +/// Gives the stream status of a (possible) stream, either input / output or duplex. #[derive(strum_macros::EnumMessage, Debug, Clone, Copy, Display)] +#[cfg_attr(feature = "python-bindings", pyclass)] pub enum StreamStatus { /// Stream is not running #[strum(message = "NotRunning", detailed_message = "Stream is not running")] - NotRunning, + NotRunning{}, /// Stream is running properly #[strum(message = "Running", detailed_message = "Stream is running")] - Running, + Running{}, /// An error occured in the stream. #[strum(message = "Error", detailed_message = "An error occured with the stream")] - Error(StreamError) + Error{ + /// In case the stream has an error: e is the field name + e: StreamError + } } diff --git a/src/filter/biquad.rs b/src/filter/biquad.rs index d7dc638..610a4e7 100644 --- a/src/filter/biquad.rs +++ b/src/filter/biquad.rs @@ -1,5 +1,5 @@ use super::*; -use ndarray::prelude::*; +use crate::config::*; use anyhow::{Result, bail}; use num::Complex; @@ -21,13 +21,12 @@ pub struct Biquad { a1: Flt, a2: Flt, } -#[cfg(feature = "python-bindings")] #[cfg_attr(feature = "python-bindings", pymethods)] impl Biquad { #[new] /// Create new biquad filter. See [Biquad::new()] /// - pub fn new_py<'py>(coefs: PyReadonlyArrayDyn) -> PyResult { + pub fn new_py<'py>(coefs: PyArrayLike1) -> PyResult { Ok(Biquad::new(coefs.as_slice()?)?) } #[pyo3(name = "unit")] @@ -48,8 +47,8 @@ impl Biquad { &mut self, py: Python<'py>, input: PyArrayLike1, - ) -> PyResult<&'py PyArray1> { - Ok(self.filter(input.as_slice()?).into_pyarray(py)) + ) -> PyResult> { + Ok(PyArray1::from_vec_bound(py, self.filter(input.as_slice()?))) } } impl Biquad { @@ -112,6 +111,8 @@ impl Biquad { Ok(Biquad::new(&coefs).unwrap()) } + + /// Filter input signal, output by overwriting input slice. pub fn filter_inout(&mut self, inout: &mut [Flt]) { for sample in inout.iter_mut() { let w0 = *sample - self.a1 * self.w1 - self.a2 * self.w2; @@ -123,6 +124,7 @@ impl Biquad { // println!("{:?}", inout); } } + impl Filter for Biquad { fn filter(&mut self, input: &[Flt]) -> Vec { let mut out = input.to_vec(); diff --git a/src/filter/seriesbiquad.rs b/src/filter/seriesbiquad.rs index e6ab7ff..2197527 100644 --- a/src/filter/seriesbiquad.rs +++ b/src/filter/seriesbiquad.rs @@ -1,17 +1,20 @@ +use super::*; + +use super::biquad::Biquad; +use anyhow::{bail, Result}; + + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "python-bindings", pyclass)] + + /// Series of biquads that filter sequentially on an input signal /// /// # Examples /// /// See (tests) /// -use super::*; -use super::biquad::Biquad; -use anyhow::{bail, Result}; - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "python-bindings", pyclass)] - pub struct SeriesBiquad { biqs: Vec, } @@ -19,6 +22,8 @@ pub struct SeriesBiquad { #[cfg(feature = "python-bindings")] #[cfg_attr(feature = "python-bindings", pymethods)] impl SeriesBiquad { + + #[new] /// Create new series filter set. See [SeriesBiquad::new()] /// @@ -37,8 +42,9 @@ impl SeriesBiquad { &mut self, py: Python<'py>, input: PyArrayLike1, - ) -> PyResult<&'py PyArray1> { - Ok(self.filter(input.as_slice()?).into_pyarray(py)) + ) -> Result> { + let res = self.filter(input.as_slice()?); + Ok(PyArray1::from_vec_bound(py, res)) } #[pyo3(name = "reset")] /// See: [SeriesBiquad::reset()] diff --git a/src/lib.rs b/src/lib.rs index 0e9ab29..cf53442 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,28 +20,22 @@ pub mod daq; pub mod siggen; use filter::*; +use daq::*; /// A Python module implemented in Rust. #[cfg(feature = "python-bindings")] #[pymodule] #[pyo3(name="_lasprs")] -fn lasprs(py: Python, m: &PyModule) -> PyResult<()> { +fn lasprs(m: &Bound<'_, PyModule>) -> PyResult<()> { + - pyo3_add_submodule_filter(py, m)?; - Ok(()) -} - -/// Add filter submodule to extension -#[cfg(feature = "python-bindings")] -fn pyo3_add_submodule_filter(py: Python, m: &PyModule) -> PyResult<()> { // Add filter submodule - let filter_module = PyModule::new(py, "filter")?; - filter_module.add_class::()?; - filter_module.add_class::()?; - filter_module.add_class::()?; - m.add_submodule(filter_module)?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + daq::add_py_classses(m)?; Ok(()) } - diff --git a/src/siggen.rs b/src/siggen.rs index 598b7e2..5f82009 100644 --- a/src/siggen.rs +++ b/src/siggen.rs @@ -23,9 +23,6 @@ use std::fmt::Debug; use std::iter::ExactSizeIterator; use std::slice::IterMut; -#[cfg(feature = "python-bindings")] -use pyo3::prelude::*; - use rand::prelude::*; use rand::rngs::ThreadRng; use rand_distr::StandardNormal;