Removed some unnecessary wrappers. Added interrupted noise.
This commit is contained in:
parent
9e4ce617ae
commit
adc3db1be6
@ -14,21 +14,72 @@ const PINKNOISE_ANALOG_ORDER: usize = 10;
|
|||||||
pub struct WhiteNoise {
|
pub struct WhiteNoise {
|
||||||
// SmallRng is a cheap random number generator
|
// SmallRng is a cheap random number generator
|
||||||
rng: SmallRng,
|
rng: SmallRng,
|
||||||
|
// Interruption state (whether to output, number of samples, number of samples after which a switch need to be performed)
|
||||||
|
interrupt_state: Option<InterruptState>,
|
||||||
}
|
}
|
||||||
impl WhiteNoise {
|
impl WhiteNoise {
|
||||||
pub fn new() -> Self {
|
pub fn new(fs: Flt, interrupt_period: Option<Flt>) -> Self {
|
||||||
|
let interrupt_state = if let Some(period) = interrupt_period {
|
||||||
|
if period > 0. {
|
||||||
|
Some(InterruptState {
|
||||||
|
period,
|
||||||
|
cur_idx: 0,
|
||||||
|
max_idx: (period * fs) as usize,
|
||||||
|
silence: false,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
WhiteNoise {
|
WhiteNoise {
|
||||||
rng: SmallRng::from_entropy(),
|
rng: SmallRng::from_entropy(),
|
||||||
|
interrupt_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SourceImpl for WhiteNoise {
|
impl SourceImpl for WhiteNoise {
|
||||||
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
|
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
|
||||||
|
let mut output = true;
|
||||||
|
|
||||||
|
// Look at whether we should do interruption of the played noise.
|
||||||
|
if let Some(InterruptState {
|
||||||
|
cur_idx,
|
||||||
|
max_idx,
|
||||||
|
silence,
|
||||||
|
..
|
||||||
|
}) = &mut self.interrupt_state
|
||||||
|
{
|
||||||
|
if cur_idx > max_idx {
|
||||||
|
// Swap flag
|
||||||
|
*cur_idx = 0;
|
||||||
|
*silence = !*silence;
|
||||||
|
}
|
||||||
|
output = !*silence;
|
||||||
|
*cur_idx += sig.len();
|
||||||
|
}
|
||||||
|
// If output is true, send new random noise. Otherwise, just silence
|
||||||
|
if output {
|
||||||
sig.for_each(|s| {
|
sig.for_each(|s| {
|
||||||
*s = self.rng.sample(StandardNormal);
|
*s = self.rng.sample(StandardNormal);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
sig.for_each(|s| {
|
||||||
|
*s = 0.;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self, fs: Flt) {
|
||||||
|
if let Some(state) = &mut self.interrupt_state {
|
||||||
|
// Restore to first start with output
|
||||||
|
state.silence = false;
|
||||||
|
state.cur_idx = 0;
|
||||||
|
state.max_idx = (state.period * fs) as usize;
|
||||||
|
// state.period = untouched
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn reset(&mut self, _fs: Flt) {}
|
|
||||||
fn clone_dyn(&self) -> Box<dyn SourceImpl> {
|
fn clone_dyn(&self) -> Box<dyn SourceImpl> {
|
||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
@ -38,14 +89,18 @@ impl SourceImpl for WhiteNoise {
|
|||||||
pub struct ColoredNoise {
|
pub struct ColoredNoise {
|
||||||
// White noise generator
|
// White noise generator
|
||||||
wn: WhiteNoise,
|
wn: WhiteNoise,
|
||||||
|
// Temporary storage for the generated signal. Needs to be able to slice,
|
||||||
|
// which is not guaranteed by the input iterator.
|
||||||
tmp: Vec<Flt>,
|
tmp: Vec<Flt>,
|
||||||
|
// Analog filter used to generate the digital filter below
|
||||||
analogue_blueprint: ZPKModel,
|
analogue_blueprint: ZPKModel,
|
||||||
|
// The digital filter that colors the white noise
|
||||||
filter: SeriesBiquad,
|
filter: SeriesBiquad,
|
||||||
}
|
}
|
||||||
impl ColoredNoise {
|
impl ColoredNoise {
|
||||||
/// Generate a colored noise signal source that outputs pink noise (-3 dB /
|
/// Generate a colored noise signal source that outputs pink noise (-3 dB /
|
||||||
/// octave ) from 20 Hz to 20 kHz.
|
/// octave ) from 20 Hz to 20 kHz.
|
||||||
pub fn newPinkNoise() -> Self {
|
pub fn newPinkNoise(fs: Flt, interrupt_period: Option<Flt>) -> Self {
|
||||||
let twopi = 2. * pi;
|
let twopi = 2. * pi;
|
||||||
let fl = 10.;
|
let fl = 10.;
|
||||||
let fu = 20e3;
|
let fu = 20e3;
|
||||||
@ -72,7 +127,7 @@ impl ColoredNoise {
|
|||||||
let analogue_blueprint = ZPKModel::new(zeros, poles, gain);
|
let analogue_blueprint = ZPKModel::new(zeros, poles, gain);
|
||||||
let filter = analogue_blueprint.bilinear(480000.);
|
let filter = analogue_blueprint.bilinear(480000.);
|
||||||
Self {
|
Self {
|
||||||
wn: WhiteNoise::new(),
|
wn: WhiteNoise::new(fs, interrupt_period),
|
||||||
tmp: vec![],
|
tmp: vec![],
|
||||||
analogue_blueprint,
|
analogue_blueprint,
|
||||||
filter,
|
filter,
|
||||||
@ -128,3 +183,11 @@ impl SourceImpl for ColoredNoise {
|
|||||||
Box::new(self.clone())
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct InterruptState {
|
||||||
|
period: Flt,
|
||||||
|
cur_idx: usize,
|
||||||
|
max_idx: usize,
|
||||||
|
silence: bool,
|
||||||
|
}
|
||||||
|
@ -11,6 +11,10 @@ use std::fmt::Debug;
|
|||||||
use std::iter::ExactSizeIterator;
|
use std::iter::ExactSizeIterator;
|
||||||
use std::slice::IterMut;
|
use std::slice::IterMut;
|
||||||
|
|
||||||
|
/// Dummy sampling frequency to be filled in when the sampling frequency is
|
||||||
|
/// still unknown at the point in time.
|
||||||
|
pub const DUMMY_SAMPLING_FREQ: Flt = 48000.;
|
||||||
|
|
||||||
/// Multiple channel signal generator. Able to create (acoustic) output signals. See above example on how to use.
|
/// Multiple channel signal generator. Able to create (acoustic) output signals. See above example on how to use.
|
||||||
/// Typical signal that can be created are:
|
/// Typical signal that can be created are:
|
||||||
///
|
///
|
||||||
@ -39,30 +43,9 @@ pub struct Siggen {
|
|||||||
#[cfg(feature = "python-bindings")]
|
#[cfg(feature = "python-bindings")]
|
||||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
impl Siggen {
|
impl Siggen {
|
||||||
#[pyo3(name = "newWhiteNoise")]
|
#[new]
|
||||||
#[staticmethod]
|
fn new_py() -> Self {
|
||||||
fn newWhiteNoise_py(fs: Flt) -> Siggen {
|
Siggen::new(1, Source::newSilence())
|
||||||
Siggen::newWhiteNoise(fs, 0)
|
|
||||||
}
|
|
||||||
#[pyo3(name = "newSine")]
|
|
||||||
#[staticmethod]
|
|
||||||
fn newSine_py(fs: Flt, freq: Flt, nchannels: usize) -> PyResult<Siggen> {
|
|
||||||
Ok(Siggen::newSine(fs, nchannels, freq)?)
|
|
||||||
}
|
|
||||||
#[pyo3(name = "newSweep")]
|
|
||||||
#[staticmethod]
|
|
||||||
fn newSweep_py(
|
|
||||||
fs: Flt,
|
|
||||||
nchannels: usize,
|
|
||||||
fl: Flt,
|
|
||||||
fu: Flt,
|
|
||||||
sweep_time: Flt,
|
|
||||||
quiet_time: Flt,
|
|
||||||
sweep_type: SweepType,
|
|
||||||
) -> Result<Self> {
|
|
||||||
Ok(Siggen::newSweep(
|
|
||||||
fs, nchannels, fl, fu, sweep_time, quiet_time, sweep_type,
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +54,7 @@ impl Siggen {
|
|||||||
/// # Args
|
/// # Args
|
||||||
///
|
///
|
||||||
/// - `nchannels` - The number of channels to output
|
/// - `nchannels` - The number of channels to output
|
||||||
/// - `source` - Source function
|
/// - `source` - Source that generates the signal
|
||||||
pub fn new(nchannels: usize, source: Source) -> Siggen {
|
pub fn new(nchannels: usize, source: Source) -> Siggen {
|
||||||
Siggen {
|
Siggen {
|
||||||
fs: None,
|
fs: None,
|
||||||
@ -131,8 +114,8 @@ impl Siggen {
|
|||||||
///
|
///
|
||||||
/// - `fs` - Sampling frequency \[Hz\]
|
/// - `fs` - Sampling frequency \[Hz\]
|
||||||
/// - `nchannels` - The number of channels to output
|
/// - `nchannels` - The number of channels to output
|
||||||
pub fn newWhiteNoise(_fs: Flt, nchannels: usize) -> Siggen {
|
pub fn newWhiteNoise(fs: Flt, nchannels: usize, interrupt_period: Option<Flt>) -> Siggen {
|
||||||
Siggen::new(nchannels, Source::newWhiteNoise())
|
Siggen::new(nchannels, Source::newWhiteNoise(fs, interrupt_period))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of channels this signal generator is generating for.
|
/// Returns the number of channels this signal generator is generating for.
|
||||||
@ -278,7 +261,7 @@ mod test {
|
|||||||
fn test_whitenoise() {
|
fn test_whitenoise() {
|
||||||
// This code is just to check syntax. We should really be listening to these outputs.
|
// This code is just to check syntax. We should really be listening to these outputs.
|
||||||
let mut t = [0.0; 10];
|
let mut t = [0.0; 10];
|
||||||
Siggen::newWhiteNoise(1., 1).genSignal(&mut t);
|
Siggen::newWhiteNoise(1., 1, None).genSignal(&mut t);
|
||||||
// println!("{:?}", &t);
|
// println!("{:?}", &t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +272,7 @@ mod test {
|
|||||||
const N: usize = 10000;
|
const N: usize = 10000;
|
||||||
let mut s1 = [0.0; N];
|
let mut s1 = [0.0; N];
|
||||||
let mut s2 = [0.0; N];
|
let mut s2 = [0.0; N];
|
||||||
let mut siggen = Siggen::newSine(1., 1, 1.0).unwrap();
|
let mut siggen = Siggen::newSine(10., 1, 1.0).unwrap();
|
||||||
|
|
||||||
siggen.reset(10.0);
|
siggen.reset(10.0);
|
||||||
siggen.setAllMute(false);
|
siggen.setAllMute(false);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! All sources for a signal generator. Sine waves, sweeps, noise, etc.
|
//! All sources for a signal generator. Sine waves, sweeps, noise, etc.
|
||||||
use super::sweep::{SweepParams, SweepType};
|
|
||||||
use super::noise::{ColoredNoise, WhiteNoise};
|
use super::noise::{ColoredNoise, WhiteNoise};
|
||||||
|
use super::siggen::DUMMY_SAMPLING_FREQ;
|
||||||
|
use super::sweep::{SweepParams, SweepType};
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
@ -23,7 +24,10 @@ impl Source {
|
|||||||
///
|
///
|
||||||
/// # Args
|
/// # Args
|
||||||
///
|
///
|
||||||
/// - `fs` - Sampling frequency \[Hz\]
|
/// - `fs`: Sampling frequency \[Hz\]. When not known at the point in time,
|
||||||
|
/// just fill in something sensible. If the [Siggen] runs in a
|
||||||
|
/// [StreamMgr], the [StreamMgr] cals [Siggen::reset] to set the right
|
||||||
|
/// sampling frequency.
|
||||||
/// * `freq` - Frequency of the sine wave in \[Hz\]
|
/// * `freq` - Frequency of the sine wave in \[Hz\]
|
||||||
pub fn newSine(fs: Flt, freq: Flt) -> Result<Source> {
|
pub fn newSine(fs: Flt, freq: Flt) -> Result<Source> {
|
||||||
Ok(Source {
|
Ok(Source {
|
||||||
@ -39,15 +43,32 @@ impl Source {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a white noise signal source
|
/// Create a white noise signal source
|
||||||
pub fn newWhiteNoise() -> Source {
|
///
|
||||||
|
/// # Args
|
||||||
|
///
|
||||||
|
/// - `fs`: Sampling frequency \[Hz\]. When not known at the point in time,
|
||||||
|
/// just fill in something sensible. If the [Siggen] runs in a
|
||||||
|
/// [StreamMgr], the [StreamMgr] cals [Siggen::reset] to set the right
|
||||||
|
/// sampling frequency.
|
||||||
|
/// - `interrupt_period` - when given AND > 0, this turns on and off the
|
||||||
|
/// noise source with periods given by the value, in \[s\].
|
||||||
|
pub fn newWhiteNoise(fs: Flt, interrupt_period: Option<Flt>) -> Source {
|
||||||
Source {
|
Source {
|
||||||
src: Box::new(WhiteNoise::new()),
|
src: Box::new(WhiteNoise::new(fs, interrupt_period)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a pink noise signal source
|
/// Create a pink noise signal source
|
||||||
pub fn newPinkNoise() -> Source {
|
/// # Args
|
||||||
|
///
|
||||||
|
/// - `fs`: Sampling frequency \[Hz\]. When not known at the point in time,
|
||||||
|
/// just fill in something sensible. If the [Siggen] runs in a
|
||||||
|
/// [StreamMgr], the [StreamMgr] cals [Siggen::reset] to set the right
|
||||||
|
/// sampling frequency.
|
||||||
|
/// - `interrupt_period` - when given AND > 0, this turns on and off the
|
||||||
|
/// noise source with periods given by the value, in \[s\].
|
||||||
|
pub fn newPinkNoise(fs: Flt, interrupt_period: Option<Flt>) -> Source {
|
||||||
Source {
|
Source {
|
||||||
src: Box::new(ColoredNoise::newPinkNoise()),
|
src: Box::new(ColoredNoise::newPinkNoise(fs, interrupt_period)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +76,10 @@ impl Source {
|
|||||||
///
|
///
|
||||||
/// # Args
|
/// # Args
|
||||||
///
|
///
|
||||||
/// - `fs` - Sample rate \[Hz\]
|
/// - `fs`: Sampling frequency \[Hz\]. When not known at the point in time,
|
||||||
|
/// just fill in something sensible. If the [Siggen] runs in a
|
||||||
|
/// [StreamMgr], the [StreamMgr] cals [Siggen::reset] to set the right
|
||||||
|
/// sampling frequency.
|
||||||
/// - `fl` - Lower frequency \[Hz\]
|
/// - `fl` - Lower frequency \[Hz\]
|
||||||
/// - `fu` - Upper frequency \[Hz\]
|
/// - `fu` - Upper frequency \[Hz\]
|
||||||
/// - `sweep_time` - The duration of a single sweep \[s\]
|
/// - `sweep_time` - The duration of a single sweep \[s\]
|
||||||
@ -89,14 +113,14 @@ impl Source {
|
|||||||
Self::newSilence()
|
Self::newSilence()
|
||||||
}
|
}
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
#[pyo3(name = "newWhiteNoise")]
|
#[pyo3(name = "newWhiteNoise", signature=(interrupt_period=None))]
|
||||||
fn newWhiteNoise_py() -> Source {
|
fn newWhiteNoise_py(interrupt_period: Option<Flt>) -> Source {
|
||||||
Self::newWhiteNoise()
|
Self::newWhiteNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
|
||||||
}
|
}
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
#[pyo3(name = "newPinkNoise")]
|
#[pyo3(name = "newPinkNoise", signature=(interrupt_period=None))]
|
||||||
fn newPinkNoise_py() -> Source {
|
fn newPinkNoise_py(interrupt_period: Option<Flt>) -> Source {
|
||||||
Self::newPinkNoise()
|
Self::newPinkNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
|
||||||
}
|
}
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
#[pyo3(name = "newSweep")]
|
#[pyo3(name = "newSweep")]
|
||||||
|
Loading…
Reference in New Issue
Block a user