diff --git a/benches/gen_bench.rs b/benches/gen_bench.rs index e621b17..152644d 100644 --- a/benches/gen_bench.rs +++ b/benches/gen_bench.rs @@ -6,7 +6,6 @@ extern crate criterion; use criterion::Criterion; -use dsp::node::SourceNode; use dsp::core::generator::*; diff --git a/examples/file_sink.rs b/examples/file_sink.rs index 1b98c84..3f406eb 100644 --- a/examples/file_sink.rs +++ b/examples/file_sink.rs @@ -1,5 +1,4 @@ use clap::Parser; -use dsp::node::{SourceNode, SinkNode}; use dsp::core::generator::Sine; use dsp::core::file::FileSink; diff --git a/examples/file_source.rs b/examples/file_source.rs index c91e598..95a16b1 100644 --- a/examples/file_source.rs +++ b/examples/file_source.rs @@ -1,5 +1,4 @@ use gnuplot::{Figure, Color, AxesCommon}; -use dsp::node::SourceNode; use dsp::core::file::FileSource; diff --git a/examples/heart_rate.rs b/examples/heart_rate.rs index 5e2c6f8..39fedd4 100644 --- a/examples/heart_rate.rs +++ b/examples/heart_rate.rs @@ -1,6 +1,5 @@ use std::fs::File; use gnuplot::{Figure, Color, AxesCommon}; -use dsp::node::ProcessNode; use dsp::core::correlation::*; diff --git a/examples/play.rs b/examples/play.rs index 57bee51..7a71345 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -1,6 +1,5 @@ use dsp::core::generator::Sine; -use dsp::node::SourceNode; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; diff --git a/examples/plot_freq.rs b/examples/plot_freq.rs index 0b5625e..b382748 100644 --- a/examples/plot_freq.rs +++ b/examples/plot_freq.rs @@ -1,7 +1,6 @@ use gnuplot::{Figure, Color, AxesCommon}; use clap::Parser; use dsp::num_complex::Complex32; -use dsp::node::{ProcessNode, SourceNode}; use dsp::core::{generator::Sine, fft::*, complex::*}; diff --git a/examples/synth.rs b/examples/synth.rs index a9a23fe..92c150e 100644 --- a/examples/synth.rs +++ b/examples/synth.rs @@ -1,6 +1,5 @@ use dsp::core::generator::Sine; -use dsp::node::SourceNode; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; diff --git a/examples/tuner.rs b/examples/tuner.rs index 07943e0..09e8190 100644 --- a/examples/tuner.rs +++ b/examples/tuner.rs @@ -1,7 +1,6 @@ use std::env; use dsp::audio::AudioFileSource; use gnuplot::{Figure, Color, AxesCommon}; -use dsp::node::*; use dsp::core::{fft::*, complex::*}; use dsp::num_complex::Complex32; use dsp::spectrum; diff --git a/examples/udp_sink.rs b/examples/udp_sink.rs index db9f200..d923910 100644 --- a/examples/udp_sink.rs +++ b/examples/udp_sink.rs @@ -1,4 +1,3 @@ -use dsp::node::{SourceNode, SinkNode}; use dsp::core::generator::Sine; use dsp::core::network::UdpSink; diff --git a/src/audio/audio_file.rs b/src/audio/audio_file.rs index 40ea83b..c523a6e 100644 --- a/src/audio/audio_file.rs +++ b/src/audio/audio_file.rs @@ -3,8 +3,6 @@ use audrey::Reader; -use crate::node::SourceNode; - pub struct AudioFileSource { reader: Reader>, @@ -15,10 +13,8 @@ impl AudioFileSource { let reader = audrey::open(file_path).unwrap(); AudioFileSource {reader} } -} -impl SourceNode for AudioFileSource { - fn write_buffer(&mut self, buffer: &mut [f32]) { + pub fn write_buffer(&mut self, buffer: &mut [f32]) { let mut samples = self.reader.samples(); for i in 0..buffer.len() { if let Some(v) = samples.next() { diff --git a/src/core/complex.rs b/src/core/complex.rs index 39ec6d6..b5f7f84 100644 --- a/src/core/complex.rs +++ b/src/core/complex.rs @@ -2,7 +2,7 @@ //! //! -use crate::{node::ProcessNode, num_complex::Complex32}; +use crate::num_complex::Complex32; /// Implement Real -> complex converter @@ -10,7 +10,6 @@ use crate::{node::ProcessNode, num_complex::Complex32}; /// Example /// /// ``` -/// use dsp::node::ProcessNode; /// use dsp::core::complex::RealToComplex; /// use dsp::num_complex::Complex32; /// @@ -29,10 +28,8 @@ impl RealToComplex { pub fn new() -> RealToComplex { RealToComplex {} } -} -impl ProcessNode for RealToComplex { - fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [Complex32]) { + pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [Complex32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { output_buffer[i] = Complex32::new(input_buffer[i], 0.); @@ -46,7 +43,6 @@ impl ProcessNode for RealToComplex { /// Example /// /// ``` -/// use dsp::node::ProcessNode; /// use dsp::core::complex::ComplexToReal; /// use dsp::num_complex::Complex32; /// @@ -65,10 +61,8 @@ impl ComplexToReal { pub fn new() -> ComplexToReal { ComplexToReal {} } -} -impl ProcessNode for ComplexToReal { - fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [f32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { output_buffer[i] = input_buffer[i].norm(); diff --git a/src/core/correlation.rs b/src/core/correlation.rs index 0b91a36..c9fa17c 100644 --- a/src/core/correlation.rs +++ b/src/core/correlation.rs @@ -1,6 +1,5 @@ //! Calculate (Auto)Correlation //! -use crate::node::ProcessNode; pub struct AutoCorrelation { @@ -11,9 +10,6 @@ impl AutoCorrelation { pub fn new(window_size: usize) -> AutoCorrelation { AutoCorrelation {window_size} } -} - -impl ProcessNode for AutoCorrelation { /// Calculate correlation between 2 buffers /// @@ -21,7 +17,6 @@ impl ProcessNode for AutoCorrelation { /// /// ``` /// use assert_approx_eq::assert_approx_eq; - /// use dsp::node::{SourceNode, ProcessNode}; /// use dsp::core::generator::Sine; /// use dsp::core::correlation::AutoCorrelation; /// @@ -38,7 +33,7 @@ impl ProcessNode for AutoCorrelation { /// assert_approx_eq!(corr_buffer[3], 0., 1e-5f32); /// assert_approx_eq!(corr_buffer[4], 1.0, 1e-5f32); /// ``` - fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { let mu: f32 = input_buffer.iter().sum::() / input_buffer.len() as f32; let max_offset = usize::min(input_buffer.len()-self.window_size, output_buffer.len()); // auto covariance diff --git a/src/core/fft.rs b/src/core/fft.rs index 897825b..19af129 100644 --- a/src/core/fft.rs +++ b/src/core/fft.rs @@ -1,8 +1,7 @@ //! Helper functions for FFT. use std::sync::Arc; -use crate::{node::ProcessNode, window}; use rustfft::{Fft, FftPlanner}; -use crate::num_complex::Complex32; +use crate::{num_complex::Complex32, window}; pub struct ForwardFFT { @@ -33,11 +32,9 @@ impl ForwardFFT { let mut fft = FftPlanner::new(); ForwardFFT { fft: fft.plan_fft_forward(sample_size), window } } -} -impl ProcessNode for ForwardFFT { - fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { + pub fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { let n = usize::min(usize::min(input_buffer.len(), output_buffer.len()), self.window.len()); for i in 0..n { output_buffer[i] = input_buffer[i].scale(self.window.as_slice()[i]); @@ -61,11 +58,9 @@ impl InverseFFT { fft: fft.plan_fft_inverse(sample_size), } } -} -impl ProcessNode for InverseFFT { - fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { + pub fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { output_buffer[i] = input_buffer[i]; @@ -80,7 +75,6 @@ impl ProcessNode for InverseFFT { #[cfg(test)] mod tests { use super::*; - use crate::node::ProcessNode; #[test] fn test_fft() { diff --git a/src/core/file.rs b/src/core/file.rs index 955118c..575d757 100644 --- a/src/core/file.rs +++ b/src/core/file.rs @@ -3,7 +3,6 @@ use std::fs::File; use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::LittleEndian; -use crate::node::{SinkNode, SourceNode}; /// Save binary data into a file @@ -11,7 +10,6 @@ use crate::node::{SinkNode, SourceNode}; /// Example /// /// ``` -/// use dsp::node::SinkNode; /// use dsp::core::file::FileSink; /// /// let mut node = FileSink::new("target/file.dat"); @@ -30,10 +28,8 @@ impl FileSink { FileSink{file: None} } } -} -impl SinkNode for FileSink { - fn read_buffer(&mut self, input_buffer: &[f32]) { + pub fn read_buffer(&mut self, input_buffer: &[f32]) { let mut file = self.file.as_ref().unwrap(); for v in input_buffer { file.write_f32::(*v).expect("Can't reead from file"); @@ -54,10 +50,8 @@ impl FileSource { let file = File::open(file_name).expect("Can't open file"); FileSource {file} } -} -impl SourceNode for FileSource { - fn write_buffer(&mut self, output_buffer: &mut [f32]) { + pub fn write_buffer(&mut self, output_buffer: &mut [f32]) { for i in 0..output_buffer.len() { if let Ok(v) = self.file.read_f32::() { output_buffer[i] = v; diff --git a/src/core/fm.rs b/src/core/fm.rs index c01d2a6..7a87b5f 100644 --- a/src/core/fm.rs +++ b/src/core/fm.rs @@ -2,7 +2,6 @@ //! use num_complex::Complex32; -use crate::node::ProcessNode; /// Demodulation block using the conjugate delay method @@ -12,7 +11,6 @@ use crate::node::ProcessNode; /// /// ``` /// use dsp::num_complex::Complex32; -/// use dsp::node::ProcessNode; /// use dsp::core::fm::QuadratureDetector; /// /// let mut node = QuadratureDetector::new(); @@ -28,10 +26,8 @@ impl QuadratureDetector { pub fn new() -> Self { Self {last_sample: Complex32::default()} } -} -impl ProcessNode for QuadratureDetector { - fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [f32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { let v = &input_buffer[i]; @@ -39,4 +35,10 @@ impl ProcessNode for QuadratureDetector { self.last_sample = *v; } } + + pub fn process_sample(&mut self, v: &Complex32) -> f32 { + let d = (v * self.last_sample.conj()).arg(); // Obtain phase of x[n] * conj(x[n-1]) + self.last_sample = *v; + d + } } diff --git a/src/core/freq_shift.rs b/src/core/freq_shift.rs index c2c770c..8e8c379 100644 --- a/src/core/freq_shift.rs +++ b/src/core/freq_shift.rs @@ -2,7 +2,6 @@ //! use num_complex::Complex32; -use crate::node::ProcessNode; use super::generator::Sine; @@ -13,7 +12,6 @@ use super::generator::Sine; /// /// ``` /// use dsp::num_complex::Complex32; -/// use dsp::node::ProcessNode; /// use dsp::core::generator::Sine; /// use dsp::core::freq_shift::FrequencyShift; /// @@ -32,13 +30,15 @@ impl FrequencyShift { let offset_signal = Sine::new(freq_offset as f32, sample_rate); Self {offset_signal} } -} -impl ProcessNode for FrequencyShift { - fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { + pub fn process_buffer(&mut self, input_buffer: &[Complex32], output_buffer: &mut [Complex32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { output_buffer[i] = self.offset_signal.next().unwrap() * input_buffer[i]; } } + + pub fn process_sample(&mut self, v: &Complex32) -> Complex32 { + self.offset_signal.next().unwrap() * v + } } diff --git a/src/core/generator.rs b/src/core/generator.rs index e89509a..85577d3 100644 --- a/src/core/generator.rs +++ b/src/core/generator.rs @@ -11,8 +11,6 @@ use rand; #[cfg(feature = "random")] use rand_distr::{Normal, Distribution}; -use crate::node::SourceNode; - /// Generate impulse signal /// x[n] = 1 if n == impulse_pos @@ -21,7 +19,6 @@ use crate::node::SourceNode; /// Example /// /// ``` -/// use dsp::node::SourceNode; /// use dsp::core::generator::Impulse; /// /// let mut signal = Impulse::new(); @@ -40,6 +37,10 @@ impl Impulse { pub fn new() -> Impulse { Impulse {impulse_send: false} } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -57,13 +58,6 @@ impl Iterator for Impulse { } } -// Node implementation -impl SourceNode for Impulse { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} - /// Step signal /// x[n] = 1 if n > step_pos @@ -72,7 +66,6 @@ impl SourceNode for Impulse { /// Example /// /// ``` -/// use dsp::node::SourceNode; /// use dsp::core::generator::Step; /// /// let mut signal = Step::new(2); @@ -92,6 +85,10 @@ impl Step { pub fn new(step_pos: usize) -> Step { Step{ step_pos } } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -109,11 +106,6 @@ impl Iterator for Step { } } -impl SourceNode for Step { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} /// Sinusoidal signal /// @@ -121,7 +113,6 @@ impl SourceNode for Step { /// /// ``` /// use assert_approx_eq::assert_approx_eq; -/// use dsp::node::SourceNode; /// use dsp::core::generator::Sine; /// /// let mut signal = Sine::new(2.0, 8); @@ -146,6 +137,10 @@ impl Sine { pub fn new(freq: f32, sample_rate: usize) -> Sine { Sine { step_pos: 0, freq, sample_rate} } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -163,19 +158,12 @@ impl Iterator for Sine { } } -impl SourceNode for Sine { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} - /// Generate triangular signal /// /// Example /// /// ``` /// use assert_approx_eq::assert_approx_eq; -/// use dsp::node::SourceNode; /// use dsp::core::generator::Sawtooth; /// /// let mut signal = Sawtooth::new(4.0, 16); @@ -201,6 +189,10 @@ impl Sawtooth { pub fn new(freq: f32, sample_rate: usize) -> Sawtooth { Sawtooth { step_pos: 0, freq, sample_rate} } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -217,12 +209,6 @@ impl Iterator for Sawtooth { } } -impl SourceNode for Sawtooth { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} - /// Generate square signal /// @@ -230,7 +216,6 @@ impl SourceNode for Sawtooth { /// /// ``` /// use assert_approx_eq::assert_approx_eq; -/// use dsp::node::SourceNode; /// use dsp::core::generator::Square; /// /// let mut signal = Square::new(4.0, 16); @@ -256,6 +241,10 @@ impl Square { pub fn new(freq: f32, sample_rate: usize) -> Square { Square { step_pos: 0, freq, sample_rate} } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -276,12 +265,6 @@ impl Iterator for Square { } } -impl SourceNode for Square { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} - /// Generate noise /// @@ -358,6 +341,10 @@ impl Chirp { let w = 2.0 * PI * (c/2.0*t.powi(2) + self.start_freq*t); f32::sin(w) } + + pub fn write_buffer(&mut self, buffer: &mut [f32]) { + for e in buffer.iter_mut() {*e = self.next().unwrap()}; + } } // Iterator implementation @@ -374,12 +361,6 @@ impl Iterator for Chirp { } } -impl SourceNode for Chirp { - fn write_buffer(&mut self, buffer: &mut [f32]) { - for e in buffer.iter_mut() {*e = self.next().unwrap()}; - } -} - /// ------------------------------------------------------------------------------------------------ /// Module unit tests @@ -388,7 +369,6 @@ impl SourceNode for Chirp { #[cfg(test)] mod tests { use assert_approx_eq::assert_approx_eq; - use crate::node::SourceNode; use crate::core::generator::Sine; #[test] diff --git a/src/core/multiply.rs b/src/core/multiply.rs index 34d0ede..d631379 100644 --- a/src/core/multiply.rs +++ b/src/core/multiply.rs @@ -1,15 +1,12 @@ //! Node for multiplying signal samples by constant value //! -use crate::node::ProcessNode; - /// Multiply buffer sample by constant value /// /// Example /// /// ``` -/// use dsp::node::ProcessNode; /// use dsp::core::multiply::MultiplyConst; /// /// let mut node = MultiplyConst::new(3.); @@ -29,10 +26,8 @@ impl MultiplyConst { pub fn new(value: f32) -> MultiplyConst { MultiplyConst {value} } -} -impl ProcessNode for MultiplyConst { - fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { let n = usize::min(input_buffer.len(), output_buffer.len()); for i in 0..n { output_buffer[i] = self.value * input_buffer[i]; diff --git a/src/core/network.rs b/src/core/network.rs index b193e08..c89fcd0 100644 --- a/src/core/network.rs +++ b/src/core/network.rs @@ -2,7 +2,6 @@ //! use std::net::UdpSocket; use byteorder::{ByteOrder, LittleEndian}; -use crate::node::{SinkNode, SourceNode}; /// Send binary data via the TCP sockek @@ -10,7 +9,6 @@ use crate::node::{SinkNode, SourceNode}; /// Example /// /// ``` -/// use dsp::node::SinkNode; /// use dsp::core::network::UdpSink; /// /// let mut node = UdpSink::new(3456, "127.0.0.1:1234"); @@ -28,10 +26,8 @@ impl UdpSink { let socket = UdpSocket::bind(bind_addr).expect("Failed to bind UdpSink"); UdpSink {socket, addr: addr.to_owned()} } -} -impl SinkNode for UdpSink { - fn read_buffer(&mut self, input_buffer: &[f32]) { + pub fn read_buffer(&mut self, input_buffer: &[f32]) { let mut bytes: Vec = vec![0; 4 * input_buffer.len()]; LittleEndian::write_f32_into(&input_buffer, &mut bytes); self.socket.send_to(&bytes, &self.addr).unwrap(); @@ -51,10 +47,8 @@ impl UdpSource { let socket = UdpSocket::bind(format!("127.0.0.1:{}", port)).expect("Can't bind to udp socket"); UdpSource {socket, bytes: vec![0; 4*buffer_size]} } -} -impl SourceNode for UdpSource { - fn write_buffer(&mut self, output_buffer: &mut [f32]) { + pub fn write_buffer(&mut self, output_buffer: &mut [f32]) { let _ = self.socket.recv_from(&mut self.bytes).expect("Error reading from socket"); LittleEndian::read_f32_into(&self.bytes, output_buffer); } diff --git a/src/filter/biquad.rs b/src/filter/biquad.rs index 4fd0293..dd0b5da 100644 --- a/src/filter/biquad.rs +++ b/src/filter/biquad.rs @@ -2,8 +2,6 @@ use arraydeque::{ArrayDeque, Wrapping}; use itertools::izip; -use crate::node::ProcessNode; - /// A biquad filter (IIR) #[derive(Clone,Debug)] @@ -76,11 +74,7 @@ impl BiquadFilter { sum } -} - -impl ProcessNode for BiquadFilter { - - fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { let size = std::cmp::min(input_buffer.len(), output_buffer.len()); (0..size).for_each(|i| output_buffer[i] = self.process_one(input_buffer[i])); } diff --git a/src/filter/leaky.rs b/src/filter/leaky.rs index 35536f8..28a2259 100644 --- a/src/filter/leaky.rs +++ b/src/filter/leaky.rs @@ -4,8 +4,6 @@ //! //! x[t] = alpha * x + (1-alpha)*x[t-1] -use crate::node::ProcessNode; - #[derive(Clone,Debug)] pub struct LeakyIntegrator { alpha: f32, @@ -37,11 +35,9 @@ impl LeakyIntegrator { self.last_value = self.alpha*v + (1. - self.alpha)*self.last_value; self.last_value } -} -impl ProcessNode for LeakyIntegrator { - fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { + pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) { let size = std::cmp::min(input_buffer.len(), output_buffer.len()); for i in 0..size { output_buffer[i] = self.next_value(input_buffer[i]); diff --git a/src/lib.rs b/src/lib.rs index b210318..7ad0d01 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ //! Signals can be processed in Time or Frequency domain //! -pub mod node; pub mod core; pub mod filter; pub mod signal; diff --git a/src/node.rs b/src/node.rs deleted file mode 100644 index 83ad49e..0000000 --- a/src/node.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Node definition -//! -//! Node is basic unit of computation -//! -use anyhow::Result; - - -pub trait SourceNode { - fn write_buffer(&mut self, buffer: &mut [T]); -} - -pub trait SinkNode { - fn read_buffer(&mut self, buffer: &[T]); -} - -pub trait ProcessNode { - fn process_buffer(&mut self, input_buffer: &[I], output_buffer: &mut [O]); -} \ No newline at end of file diff --git a/src/signal.rs b/src/signal.rs index 7e0a0bf..6b31e13 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -16,7 +16,6 @@ pub fn power(buffer: &[f32]) -> f32 { mod tests { use assert_approx_eq::assert_approx_eq; use crate::core::generator::Sine; - use crate::node::SourceNode; use super::*;