Added siggencommand. Exported sweep types. Added debug derives on some more classes. StreamMgr now returns result on stream commands, by polling back on a channel for result values.
This commit is contained in:
parent
45da6370ec
commit
0567e7fb92
@ -3,6 +3,7 @@ use super::streammgr::SharedInQueue;
|
||||
|
||||
|
||||
/// Commands that can be sent to a running stream
|
||||
#[derive(Debug)]
|
||||
pub enum StreamCommand {
|
||||
/// Add a new queue to a running INPUT stream
|
||||
AddInQueue(SharedInQueue),
|
||||
|
@ -2,7 +2,7 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
config::*,
|
||||
siggen::{self, Siggen},
|
||||
siggen::{self, Siggen, SiggenCommand},
|
||||
};
|
||||
use anyhow::{anyhow, bail, Error, Result};
|
||||
use api::StreamApiDescr;
|
||||
@ -122,7 +122,13 @@ impl StreamMgr {
|
||||
// value (not the Arc) has to be cloned.
|
||||
self.getStreamMetaData(st).map(|b| (*b).clone())
|
||||
}
|
||||
#[pyo3(name = "siggenCommand")]
|
||||
fn siggenCommand_py(&mut self, cmd: SiggenCommand) -> PyResult<()> {
|
||||
self.siggenCommand(cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StreamMgr {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
@ -148,7 +154,7 @@ impl StreamMgr {
|
||||
devs: vec![],
|
||||
input_stream: None,
|
||||
output_stream: None,
|
||||
siggen: None,
|
||||
siggen: Some(Siggen::newSilence(1., 1)),
|
||||
|
||||
#[cfg(feature = "cpal-api")]
|
||||
cpal_api: CpalApi::new(),
|
||||
@ -354,6 +360,7 @@ impl StreamMgr {
|
||||
.take()
|
||||
.unwrap_or_else(|| Siggen::newSilence(meta.samplerate, nchannels));
|
||||
|
||||
siggen.setAllMute(true);
|
||||
if siggen.nchannels() != nchannels {
|
||||
// Updating number of channels
|
||||
siggen.setNChannels(nchannels);
|
||||
@ -398,7 +405,7 @@ impl StreamMgr {
|
||||
}
|
||||
}
|
||||
}
|
||||
while tx.len() < 2 {
|
||||
if tx.len() < 1 {
|
||||
// Obtain signal from signal generator
|
||||
siggen.genSignal(&mut floatbuf);
|
||||
|
||||
@ -663,6 +670,40 @@ impl StreamMgr {
|
||||
StreamType::Output => self.stopOutputStream(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply a signal generator command to control the output stream's signal
|
||||
/// generator. see [SiggenCommand] for types of commands. Muting, setting
|
||||
/// gain etc. A result code is given back and should be checked for errors.
|
||||
pub fn siggenCommand(&mut self, cmd: SiggenCommand) -> Result<()> {
|
||||
if let Some(stream) = self.output_stream.as_ref() {
|
||||
stream
|
||||
.commtx
|
||||
.send(StreamCommand::SiggenCommand(cmd))
|
||||
.unwrap();
|
||||
return stream.commrx.recv().unwrap();
|
||||
} else if let Some(stream) = self.input_stream.as_ref() {
|
||||
// When its duplex, it should have a signal generator
|
||||
if matches!(stream.streamtype, StreamType::Duplex) {
|
||||
stream
|
||||
.commtx
|
||||
.send(StreamCommand::SiggenCommand(cmd))
|
||||
.unwrap();
|
||||
stream.commrx.recv().unwrap()
|
||||
} else {
|
||||
return self
|
||||
.siggen
|
||||
.as_mut()
|
||||
.expect("siggen should be in rest pos")
|
||||
.applyCommand(cmd);
|
||||
}
|
||||
} else {
|
||||
return self
|
||||
.siggen
|
||||
.as_mut()
|
||||
.expect("siggen should be in rest pos")
|
||||
.applyCommand(cmd);
|
||||
}
|
||||
}
|
||||
} // impl StreamMgr
|
||||
impl Drop for StreamMgr {
|
||||
fn drop(&mut self) {
|
||||
|
@ -2,7 +2,7 @@ use super::*;
|
||||
use super::seriesbiquad::*;
|
||||
use rayon::prelude::*;
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
/// Multiple biquad filter that operate in parallel on a signal, and can apply a gain value to each
|
||||
/// of the returned values. The BiquadBank can be used to decompose a signal by running it through
|
||||
/// parallel filters, or it can directly be used to eq a signal. For the latter process, also a
|
||||
|
@ -4,6 +4,8 @@
|
||||
//! Contains [Biquad], [SeriesBiquad], and [BiquadBank]. These are all constructs that work on
|
||||
//! blocks of input data, and apply filters on it. TODO: implement FIR filter.
|
||||
#![allow(non_snake_case)]
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::config::*;
|
||||
|
||||
mod biquad;
|
||||
@ -23,7 +25,7 @@ pub use seriesbiquad::SeriesBiquad;
|
||||
pub use zpkmodel::{PoleOrZero, ZPKModel, FilterSpec};
|
||||
|
||||
/// Implementations of this trait are able to DSP-filter input data.
|
||||
pub trait Filter: Send {
|
||||
pub trait Filter: Send + Debug {
|
||||
//! The filter trait is implemented by, for example, [Biquad], [SeriesBiquad], and [BiquadBank].
|
||||
|
||||
/// Filter input to generate output. A vector of output floats is generated with the same
|
||||
@ -39,7 +41,6 @@ pub trait Filter: Send {
|
||||
}
|
||||
|
||||
/// Implementations are able to generate transfer functions of itself
|
||||
|
||||
pub trait TransferFunction<'a, T>: Send
|
||||
where
|
||||
T: AsArray<'a, Flt>,
|
||||
|
@ -63,6 +63,8 @@ fn lasprs(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<siggen::Siggen>()?;
|
||||
m.add_class::<siggen::SiggenCommand>()?;
|
||||
m.add_class::<siggen::SweepType>()?;
|
||||
m.add_class::<siggen::SiggenCommand>()?;
|
||||
m.add_class::<siggen::Source>()?;
|
||||
|
||||
// SLM
|
||||
m.add_class::<slm::TimeWeighting>()?;
|
||||
|
@ -18,7 +18,7 @@ use std::slice::IterMut;
|
||||
/// * [Siggen::newSine]
|
||||
/// * [Siggen::newSilence]
|
||||
///
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
pub struct Siggen {
|
||||
// The source dynamic signal. Noise, a sine wave, sweep, etc
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::config::*;
|
||||
use crate::filter::Filter;
|
||||
/// Signal generator config for a certain channel
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SiggenChannelConfig {
|
||||
muted: bool,
|
||||
prefilter: Option<Box<dyn Filter>>,
|
||||
|
@ -4,6 +4,7 @@ use crate::config::*;
|
||||
/// Messages that can be send to a given signal generator [Siggen], to change its behaviour
|
||||
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SiggenCommand {
|
||||
/// Change the source to a sine wave with given frequency.
|
||||
ChangeSource{
|
||||
@ -13,13 +14,13 @@ pub enum SiggenCommand {
|
||||
|
||||
/// Reset the signal generator state
|
||||
ResetSiggen {
|
||||
/// New sampling frequency \[Hz\]
|
||||
/// Sampling frequency \[Hz\]
|
||||
fs: Flt,
|
||||
},
|
||||
|
||||
/// Set all gains to value g
|
||||
SetAllGains {
|
||||
/// Linear gain level to apply
|
||||
/// Linear gain level to apply to all channels
|
||||
g: Flt,
|
||||
},
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! All sources for a signal generator. Sine waves, sweeps, noise, etc.
|
||||
use super::sweep::{SweepParams, SweepType};
|
||||
use crate::config::*;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Ratio between circumference and radius of a circle
|
||||
@ -14,7 +15,7 @@ use rand_distr::StandardNormal;
|
||||
/// Signal source for a signal generator. A signal source is capable of creating
|
||||
/// new signal data.
|
||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Source {
|
||||
src: Box<dyn SourceImpl>,
|
||||
}
|
||||
@ -69,7 +70,41 @@ impl Source {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||
impl Source {
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newSine")]
|
||||
fn newSine_py(fs: Flt, freq: Flt) -> PyResult<Source> {
|
||||
Ok(Self::newSine(fs, freq)?)
|
||||
}
|
||||
#[pyo3(name = "newSilence")]
|
||||
#[staticmethod]
|
||||
fn newSilence_py() -> Source {
|
||||
Self::newSilence()
|
||||
}
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newWhiteNoise")]
|
||||
fn newWhiteNoise_py() -> Source {
|
||||
Self::newWhiteNoise()
|
||||
}
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newSweep")]
|
||||
fn newSweep_py(
|
||||
fs: Flt,
|
||||
fl: Flt,
|
||||
fu: Flt,
|
||||
sweep_time: Flt,
|
||||
quiet_time: Flt,
|
||||
sweep_type: SweepType,
|
||||
) -> PyResult<Source> {
|
||||
Ok(Self::newSweep(
|
||||
fs, fl, fu, sweep_time, quiet_time, sweep_type,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Silence source. Most simple one does only send out a 0.
|
||||
struct Silence {}
|
||||
|
||||
@ -85,7 +120,7 @@ impl SourceImpl for Silence {
|
||||
}
|
||||
}
|
||||
/// White noise source. Can be colored by applying a color filter to the source
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct WhiteNoise {}
|
||||
impl SourceImpl for WhiteNoise {
|
||||
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
|
||||
@ -100,7 +135,7 @@ impl SourceImpl for WhiteNoise {
|
||||
}
|
||||
|
||||
/// Sine wave, with configurable frequency
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct Sine {
|
||||
// Sampling freq \[Hz\]
|
||||
fs: Flt,
|
||||
@ -212,7 +247,7 @@ impl DerefMut for Source {
|
||||
}
|
||||
|
||||
/// Source for the signal generator. Implementations are sine waves, sweeps, noise.
|
||||
pub trait SourceImpl: Send {
|
||||
pub trait SourceImpl: Send + Debug {
|
||||
/// Generate the 'pure' source signal. Output is placed inside the `sig` argument.
|
||||
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>);
|
||||
/// Reset the source state, i.e. set phase to 0, etc
|
||||
|
@ -1,4 +1,6 @@
|
||||
//! Sweep signal generation code
|
||||
use strum_macros::{Display, EnumMessage};
|
||||
use strum::EnumMessage;
|
||||
use {
|
||||
crate::config::*,
|
||||
anyhow::{bail, Result},
|
||||
@ -9,23 +11,49 @@ 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)]
|
||||
#[derive(Debug, Clone, Display, EnumMessage)]
|
||||
pub enum SweepType {
|
||||
/// Forward only logarithmic sweep, repeats itself
|
||||
#[strum(message = "Forward logarithmic")]
|
||||
ForwardLog,
|
||||
/// Reverse only logarithmic sweep, repeats itself
|
||||
#[strum(message = "Backward logarithmic")]
|
||||
BackwardLog,
|
||||
/// Continuous logarithmic sweep, repeats itself
|
||||
#[strum(message = "Continuous logarithmic")]
|
||||
ContinuousLog,
|
||||
|
||||
/// Forward only linear sweep, repeats itself
|
||||
#[strum(message = "Forward linear")]
|
||||
ForwardLin,
|
||||
/// Reverse only linear sweep, repeats itself
|
||||
#[strum(message = "Backward linear")]
|
||||
BackwardLin,
|
||||
/// Continuous linear sweep, repeats itself
|
||||
#[strum(message = "Continuous linear")]
|
||||
ContinuousLin,
|
||||
}
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||
impl SweepType {
|
||||
#[staticmethod]
|
||||
fn all() -> Vec<SweepType> {
|
||||
use SweepType::*;
|
||||
vec![
|
||||
ForwardLin,
|
||||
ForwardLog,
|
||||
BackwardLin,
|
||||
BackwardLog,
|
||||
ContinuousLin,
|
||||
ContinuousLog,
|
||||
]
|
||||
}
|
||||
fn __str__(&self) -> String {
|
||||
self.get_message().unwrap().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SweepParams {
|
||||
// These parameters are described at [Source::newSweep]
|
||||
|
Loading…
Reference in New Issue
Block a user