Removed dependency on ndarray_rand. Crate is not updated. Updated pyo3 to new version and updated enum pyclass derive macros. Switch to SmallRng for white noise random number generation.
This commit is contained in:
parent
0567e7fb92
commit
8a573266df
17
Cargo.toml
17
Cargo.toml
@ -16,11 +16,15 @@ crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
# Error handling
|
||||
anyhow = "1.0.86"
|
||||
anyhow = "1.0.91"
|
||||
|
||||
# Numerics
|
||||
# Optional future feature for ndarray: blas
|
||||
ndarray = { version = "0.15.6", features = ["rayon"] }
|
||||
ndarray = { version = "0.16.1", features = ["rayon"] }
|
||||
|
||||
# This is required for HDF5, as it apparently doesn't update anymore.
|
||||
ndarray15p6 = { package = "ndarray", version = "0.15.6", features = ["rayon"] }
|
||||
|
||||
num = "0.4.3"
|
||||
# blas-src = { version = "0.8", features = ["openblas"] }
|
||||
# openblas-src = { version = "0.10", features = ["cblas", "system"] }
|
||||
@ -29,15 +33,15 @@ num = "0.4.3"
|
||||
rayon = "1.10.0"
|
||||
|
||||
# Python bindings
|
||||
pyo3 = { version = "0.21.2", optional = true, features = [
|
||||
pyo3 = { version = "0.22.5", optional = true, features = [
|
||||
"extension-module",
|
||||
"anyhow",
|
||||
] }
|
||||
# Python bindings for Numpy arrays
|
||||
numpy = { version = "0.21.0", optional = true }
|
||||
numpy = { version = "0.22.0", optional = true }
|
||||
|
||||
# White noise etc
|
||||
rand = "0.8.5"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
rand_distr = "0.4.3"
|
||||
|
||||
# Cross-platform audio lib
|
||||
@ -105,9 +109,6 @@ smallvec = "1.13.2"
|
||||
# Compile time constant floating point operations
|
||||
softfloat = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ndarray-rand = "0.14.0"
|
||||
|
||||
[features]
|
||||
default = ["f64", "cpal-api", "record"]
|
||||
# Use this to test if everything works well in f32
|
||||
|
@ -32,10 +32,7 @@ pub trait Stream {
|
||||
}
|
||||
|
||||
/// Stream API descriptor: type and corresponding text
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
//#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(strum_macros::EnumMessage, Debug, Clone, PartialEq, Serialize, Deserialize, strum_macros::Display)]
|
||||
#[allow(dead_code)]
|
||||
pub enum StreamApiDescr {
|
||||
|
@ -7,10 +7,7 @@ use crate::config::*;
|
||||
|
||||
/// Data type description for samples coming from a stream
|
||||
#[derive(strum_macros::EnumMessage, PartialEq, Copy, Debug, Clone, Serialize, Deserialize)]
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
//#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[allow(dead_code)]
|
||||
pub enum DataType {
|
||||
/// 32-bit floats
|
||||
|
@ -50,10 +50,7 @@ use api::*;
|
||||
use crate::config::*;
|
||||
/// Stream types that can be started
|
||||
///
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
// #[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum StreamType {
|
||||
/// Input-only stream
|
||||
|
@ -7,9 +7,7 @@ use strum_macros;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// Physical quantities that are I/O of a Daq device.
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
// #[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(PartialEq, Serialize, Deserialize, strum_macros::EnumMessage, Debug, Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Qty {
|
||||
|
@ -161,28 +161,32 @@ impl Recording {
|
||||
nchannels: usize,
|
||||
) -> Result<()> {
|
||||
match data.getRaw() {
|
||||
// The code below uses ndarray 0.15.6, which is the version required
|
||||
// to communicate with rust-hdf5. It requires input to be C-ordered,
|
||||
// or interleaved. This happens to be the default for ndarray as
|
||||
// well.
|
||||
RawStreamData::Datai8(dat) => {
|
||||
let arr = ndarray::ArrayView2::<i8>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
let arr = ndarray15p6::ArrayView2::<i8>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ds.write_slice(arr, (ctr, .., ..))?;
|
||||
}
|
||||
RawStreamData::Datai16(dat) => {
|
||||
let arr =
|
||||
ndarray::ArrayView2::<i16>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ndarray15p6::ArrayView2::<i16>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ds.write_slice(arr, (ctr, .., ..))?;
|
||||
}
|
||||
RawStreamData::Datai32(dat) => {
|
||||
let arr =
|
||||
ndarray::ArrayView2::<i32>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ndarray15p6::ArrayView2::<i32>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ds.write_slice(arr, (ctr, .., ..))?;
|
||||
}
|
||||
RawStreamData::Dataf32(dat) => {
|
||||
let arr =
|
||||
ndarray::ArrayView2::<f32>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ndarray15p6::ArrayView2::<f32>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ds.write_slice(arr, (ctr, .., ..))?;
|
||||
}
|
||||
RawStreamData::Dataf64(dat) => {
|
||||
let arr =
|
||||
ndarray::ArrayView2::<f64>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ndarray15p6::ArrayView2::<f64>::from_shape((framesPerBlock, nchannels), dat)?;
|
||||
ds.write_slice(arr, (ctr, .., ..))?;
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,8 @@ use strum_macros::Display;
|
||||
use crate::config::*;
|
||||
|
||||
/// Errors that happen in a stream
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(strum_macros::EnumMessage, PartialEq, Debug, Clone, Display, Copy)]
|
||||
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
//#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
|
||||
pub enum StreamError {
|
||||
/// Input overrun
|
||||
#[strum(
|
||||
|
@ -2,7 +2,10 @@
|
||||
//!
|
||||
//!
|
||||
use crate::config::*;
|
||||
use ndarray::ArrayView1;
|
||||
use ndarray::{ArrayView1, IntoDimension};
|
||||
use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
|
||||
use rand_distr::StandardNormal;
|
||||
use smallvec::Array;
|
||||
|
||||
/// Compute maximum value of an array of float values
|
||||
pub fn max<T>(arr: ArrayView<Flt, T>) -> Flt
|
||||
@ -33,3 +36,22 @@ where
|
||||
{
|
||||
arr.fold(0., |acc, new| if new.abs() > acc { new.abs() } else { acc })
|
||||
}
|
||||
|
||||
/// Generate an array of *PSEUDO* random numbers from the standard normal
|
||||
/// distribution. Typically used for white noise generation. To be used for
|
||||
/// noise signals, not for anything that needs to be truly random.
|
||||
///
|
||||
/// # Args
|
||||
///
|
||||
/// - `shape` - Shape of the returned array
|
||||
///
|
||||
pub fn randNormal<Sh, D>(shape: Sh) -> ndarray::Array<Flt,D>
|
||||
where
|
||||
Sh: ShapeBuilder<Dim=D>,
|
||||
D: Dimension
|
||||
{
|
||||
// Explicit conversion to Fortran order
|
||||
let mut rng = SmallRng::from_entropy();
|
||||
let shape = shape.f().into_shape_with_order();
|
||||
ArrayBase::from_shape_simple_fn(shape, || rng.sample(StandardNormal))
|
||||
}
|
||||
|
@ -308,11 +308,9 @@ impl AvPowerSpectra {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use approx::assert_abs_diff_eq;
|
||||
use ndarray_rand::rand_distr::Normal;
|
||||
use ndarray_rand::RandomExt;
|
||||
|
||||
use super::*;
|
||||
use crate::config::*;
|
||||
use crate::{config::*, math::randNormal};
|
||||
|
||||
use super::{ApsMode, AvPowerSpectra, CPSResult, Overlap, WindowType};
|
||||
use Overlap::Percentage;
|
||||
@ -359,8 +357,7 @@ mod test {
|
||||
let mut aps = AvPowerSpectra::new(settings);
|
||||
assert_eq!(aps.overlap_keep, 0);
|
||||
|
||||
let distr = Normal::new(1.0, 1.0).expect("Distribution cannot be built");
|
||||
let timedata_some = Dmat::random((nfft, 1), distr);
|
||||
let timedata_some = randNormal((nfft,1));
|
||||
let timedata_zeros = Dmat::zeros((nfft, 1));
|
||||
|
||||
// Clone here, as first_result reference is overwritten by subsequent
|
||||
@ -382,8 +379,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_tf1() {
|
||||
let nfft = 4800;
|
||||
let distr = Normal::new(1.0, 1.0).unwrap();
|
||||
let mut timedata = Dmat::random((nfft, 1), distr);
|
||||
let mut timedata = randNormal((nfft, 1));
|
||||
timedata
|
||||
.push_column(timedata.column(0).mapv(|a| 2. * a).view())
|
||||
.unwrap();
|
||||
@ -405,8 +401,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_tf2() {
|
||||
let nfft = 4800;
|
||||
let distr = Normal::new(1.0, 1.0).unwrap();
|
||||
let mut timedata = Dmat::random((nfft, 1), distr);
|
||||
let mut timedata = randNormal((nfft, 1));
|
||||
timedata
|
||||
.push_column(timedata.column(0).mapv(|a| 2. * a).view())
|
||||
.unwrap();
|
||||
@ -431,8 +426,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_ap() {
|
||||
let nfft = 1024;
|
||||
let distr = Normal::new(1.0, 1.0).unwrap();
|
||||
let timedata = Dmat::random((150 * nfft, 1), distr);
|
||||
let timedata = randNormal((150 * nfft, 1));
|
||||
let timedata_mean_square = (&timedata * &timedata).sum() / (timedata.len() as Flt);
|
||||
|
||||
for wt in [
|
||||
|
@ -2,11 +2,7 @@ use crate::config::*;
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::{Display, EnumIter, EnumMessage};
|
||||
/// Sound level frequency weighting type (A, C, Z)
|
||||
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
// #[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(Copy, Display, Debug, EnumMessage, Default, Clone, PartialEq, EnumIter)]
|
||||
pub enum FreqWeighting {
|
||||
/// A-weighting
|
||||
|
@ -252,7 +252,6 @@ mod test {
|
||||
use approx::{abs_diff_eq, assert_relative_eq, assert_ulps_eq, ulps_eq};
|
||||
// For absolute value
|
||||
use num::complex::ComplexFloat;
|
||||
use rand_distr::StandardNormal;
|
||||
|
||||
/// Generate a sine wave at the order i
|
||||
fn generate_sinewave(nfft: usize, order: usize) -> Dcol {
|
||||
@ -267,6 +266,8 @@ mod test {
|
||||
)
|
||||
}
|
||||
|
||||
use crate::math::randNormal;
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
/// Test whether DC part of single-sided FFT has right properties
|
||||
@ -349,7 +350,6 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
use ndarray_rand::RandomExt;
|
||||
// Test parseval's theorem for some random data
|
||||
#[test]
|
||||
fn test_parseval() {
|
||||
@ -358,7 +358,7 @@ mod test {
|
||||
let mut ps = PowerSpectra::newFromWindow(rect);
|
||||
|
||||
// Start with a time signal
|
||||
let t: Dmat = Dmat::random((nfft, 1), StandardNormal);
|
||||
let t: Dmat = randNormal((nfft,1));
|
||||
|
||||
let tavg = t.sum() / (nfft as Flt);
|
||||
let t_dc_power = tavg.powi(2);
|
||||
@ -394,7 +394,7 @@ mod test {
|
||||
let mut ps = PowerSpectra::newFromWindow(window);
|
||||
|
||||
// Start with a time signal
|
||||
let t: Dmat = 2. * Dmat::random((nfft, 1), StandardNormal);
|
||||
let t: Dmat = randNormal((nfft,1));
|
||||
|
||||
let tavg = t.sum() / (nfft as Flt);
|
||||
let t_dc_power = tavg.powi(2);
|
||||
|
@ -73,11 +73,8 @@ fn hamming(N: usize) -> Dcol {
|
||||
/// * Blackman
|
||||
///
|
||||
/// The [WindowType::default] is [WindowType::Hann].
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(Display, Default, Copy, Clone, Debug, PartialEq, EnumMessage, EnumIter)]
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
// #[cfg_attr(feature = "python-bindings", pyclass(eq))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
pub enum WindowType {
|
||||
/// Von Hann window
|
||||
#[default]
|
||||
|
@ -192,8 +192,8 @@ impl Drop for PPM {
|
||||
|
||||
/// Enumerator denoting, for each channel what the level approximately is. Low,
|
||||
/// fine, high or clipped.
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[derive(Copy, Debug, Clone, Default)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(Copy, Debug, PartialEq, Clone, Default)]
|
||||
pub enum ClipState {
|
||||
/// Level is rather low
|
||||
#[default]
|
||||
|
@ -42,7 +42,7 @@ impl Source {
|
||||
/// Create a white noise signal source
|
||||
pub fn newWhiteNoise() -> Source {
|
||||
Source {
|
||||
src: Box::new(WhiteNoise {}),
|
||||
src: Box::new(WhiteNoise { rng: SmallRng::from_entropy()}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,11 +121,14 @@ impl SourceImpl for Silence {
|
||||
}
|
||||
/// White noise source. Can be colored by applying a color filter to the source
|
||||
#[derive(Clone, Debug)]
|
||||
struct WhiteNoise {}
|
||||
struct WhiteNoise {
|
||||
// SmallRng is a cheap random number generator
|
||||
rng: SmallRng
|
||||
}
|
||||
impl SourceImpl for WhiteNoise {
|
||||
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
|
||||
sig.for_each(|s| {
|
||||
*s = thread_rng().sample(StandardNormal);
|
||||
*s = self.rng.sample(StandardNormal);
|
||||
});
|
||||
}
|
||||
fn reset(&mut self, _fs: Flt) {}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Sweep signal generation code
|
||||
use strum_macros::{Display, EnumMessage};
|
||||
use strum::EnumMessage;
|
||||
use strum_macros::{Display, EnumMessage};
|
||||
use {
|
||||
crate::config::*,
|
||||
anyhow::{bail, Result},
|
||||
@ -10,8 +10,8 @@ const twopi: Flt = 2. * pi;
|
||||
|
||||
/// Enumerator representing the type of sweep source to create. Used as
|
||||
/// parameter in [Siggen::newSweep].
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[derive(Debug, Clone, Display, EnumMessage)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq, eq_int))]
|
||||
#[derive(Debug, PartialEq, Clone, Display, EnumMessage)]
|
||||
pub enum SweepType {
|
||||
/// Forward only logarithmic sweep, repeats itself
|
||||
#[strum(message = "Forward logarithmic")]
|
||||
|
@ -3,11 +3,7 @@ use crate::config::*;
|
||||
use strum::EnumMessage;
|
||||
use strum_macros::Display;
|
||||
/// Time weighting to use in level detection of Sound Level Meter.
|
||||
///
|
||||
// Do the following when Pyo3 0.22 can finally be used combined with rust-numpy:
|
||||
// #[cfg_attr(feature = "python-bindings", pyclass(eq))]
|
||||
// For now:
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass(eq))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Display)]
|
||||
pub enum TimeWeighting {
|
||||
// I know that the curly braces here are not required and add some
|
||||
@ -37,10 +33,6 @@ pub enum TimeWeighting {
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||
impl TimeWeighting {
|
||||
// This method is still required in Pyo3 0.21, not anymore in 0.22
|
||||
fn __eq__(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
fn __str__(&self) -> String {
|
||||
format!("{self}")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user