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 {
|
||||
// SmallRng is a cheap random number generator
|
||||
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 {
|
||||
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 {
|
||||
rng: SmallRng::from_entropy(),
|
||||
interrupt_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SourceImpl for WhiteNoise {
|
||||
fn genSignal_unscaled(&mut self, sig: &mut dyn ExactSizeIterator<Item = &mut Flt>) {
|
||||
sig.for_each(|s| {
|
||||
*s = self.rng.sample(StandardNormal);
|
||||
});
|
||||
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| {
|
||||
*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> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
@ -38,14 +89,18 @@ impl SourceImpl for WhiteNoise {
|
||||
pub struct ColoredNoise {
|
||||
// White noise generator
|
||||
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>,
|
||||
// Analog filter used to generate the digital filter below
|
||||
analogue_blueprint: ZPKModel,
|
||||
// The digital filter that colors the white noise
|
||||
filter: SeriesBiquad,
|
||||
}
|
||||
impl ColoredNoise {
|
||||
/// Generate a colored noise signal source that outputs pink noise (-3 dB /
|
||||
/// 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 fl = 10.;
|
||||
let fu = 20e3;
|
||||
@ -72,7 +127,7 @@ impl ColoredNoise {
|
||||
let analogue_blueprint = ZPKModel::new(zeros, poles, gain);
|
||||
let filter = analogue_blueprint.bilinear(480000.);
|
||||
Self {
|
||||
wn: WhiteNoise::new(),
|
||||
wn: WhiteNoise::new(fs, interrupt_period),
|
||||
tmp: vec![],
|
||||
analogue_blueprint,
|
||||
filter,
|
||||
@ -128,3 +183,11 @@ impl SourceImpl for ColoredNoise {
|
||||
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::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.
|
||||
/// Typical signal that can be created are:
|
||||
///
|
||||
@ -39,30 +43,9 @@ pub struct Siggen {
|
||||
#[cfg(feature = "python-bindings")]
|
||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||
impl Siggen {
|
||||
#[pyo3(name = "newWhiteNoise")]
|
||||
#[staticmethod]
|
||||
fn newWhiteNoise_py(fs: Flt) -> Siggen {
|
||||
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,
|
||||
)?)
|
||||
#[new]
|
||||
fn new_py() -> Self {
|
||||
Siggen::new(1, Source::newSilence())
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +54,7 @@ impl Siggen {
|
||||
/// # Args
|
||||
///
|
||||
/// - `nchannels` - The number of channels to output
|
||||
/// - `source` - Source function
|
||||
/// - `source` - Source that generates the signal
|
||||
pub fn new(nchannels: usize, source: Source) -> Siggen {
|
||||
Siggen {
|
||||
fs: None,
|
||||
@ -131,8 +114,8 @@ impl Siggen {
|
||||
///
|
||||
/// - `fs` - Sampling frequency \[Hz\]
|
||||
/// - `nchannels` - The number of channels to output
|
||||
pub fn newWhiteNoise(_fs: Flt, nchannels: usize) -> Siggen {
|
||||
Siggen::new(nchannels, Source::newWhiteNoise())
|
||||
pub fn newWhiteNoise(fs: Flt, nchannels: usize, interrupt_period: Option<Flt>) -> Siggen {
|
||||
Siggen::new(nchannels, Source::newWhiteNoise(fs, interrupt_period))
|
||||
}
|
||||
|
||||
/// Returns the number of channels this signal generator is generating for.
|
||||
@ -278,7 +261,7 @@ mod test {
|
||||
fn test_whitenoise() {
|
||||
// This code is just to check syntax. We should really be listening to these outputs.
|
||||
let mut t = [0.0; 10];
|
||||
Siggen::newWhiteNoise(1., 1).genSignal(&mut t);
|
||||
Siggen::newWhiteNoise(1., 1, None).genSignal(&mut t);
|
||||
// println!("{:?}", &t);
|
||||
}
|
||||
|
||||
@ -289,7 +272,7 @@ mod test {
|
||||
const N: usize = 10000;
|
||||
let mut s1 = [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.setAllMute(false);
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! All sources for a signal generator. Sine waves, sweeps, noise, etc.
|
||||
use super::sweep::{SweepParams, SweepType};
|
||||
use super::noise::{ColoredNoise, WhiteNoise};
|
||||
use super::siggen::DUMMY_SAMPLING_FREQ;
|
||||
use super::sweep::{SweepParams, SweepType};
|
||||
use crate::config::*;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
@ -23,7 +24,10 @@ impl Source {
|
||||
///
|
||||
/// # 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\]
|
||||
pub fn newSine(fs: Flt, freq: Flt) -> Result<Source> {
|
||||
Ok(Source {
|
||||
@ -39,15 +43,32 @@ impl 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 {
|
||||
src: Box::new(WhiteNoise::new()),
|
||||
src: Box::new(WhiteNoise::new(fs, interrupt_period)),
|
||||
}
|
||||
}
|
||||
/// 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 {
|
||||
src: Box::new(ColoredNoise::newPinkNoise()),
|
||||
src: Box::new(ColoredNoise::newPinkNoise(fs, interrupt_period)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +76,10 @@ impl Source {
|
||||
///
|
||||
/// # 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\]
|
||||
/// - `fu` - Upper frequency \[Hz\]
|
||||
/// - `sweep_time` - The duration of a single sweep \[s\]
|
||||
@ -89,14 +113,14 @@ impl Source {
|
||||
Self::newSilence()
|
||||
}
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newWhiteNoise")]
|
||||
fn newWhiteNoise_py() -> Source {
|
||||
Self::newWhiteNoise()
|
||||
#[pyo3(name = "newWhiteNoise", signature=(interrupt_period=None))]
|
||||
fn newWhiteNoise_py(interrupt_period: Option<Flt>) -> Source {
|
||||
Self::newWhiteNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
|
||||
}
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newPinkNoise")]
|
||||
fn newPinkNoise_py() -> Source {
|
||||
Self::newPinkNoise()
|
||||
#[pyo3(name = "newPinkNoise", signature=(interrupt_period=None))]
|
||||
fn newPinkNoise_py(interrupt_period: Option<Flt>) -> Source {
|
||||
Self::newPinkNoise(DUMMY_SAMPLING_FREQ, interrupt_period)
|
||||
}
|
||||
#[staticmethod]
|
||||
#[pyo3(name = "newSweep")]
|
||||
|
Loading…
Reference in New Issue
Block a user