Starting to support arrays and tuples for inout
This commit is contained in:
@ -4,4 +4,6 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
num = "0.4.3"
|
||||
oxydsp-flowgraph = {path = "../oxydsp-flowgraph/"}
|
||||
rustfft = "6.4.1"
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
pub mod math;
|
||||
pub mod synthesis;
|
||||
pub mod utilities;
|
||||
|
||||
81
oxydsp-dsp/src/blocks/synthesis.rs
Normal file
81
oxydsp-dsp/src/blocks/synthesis.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use crate::units::DigitalFrequency;
|
||||
use num::Complex;
|
||||
use num::Float;
|
||||
use oxydsp_flowgraph::BlockIO;
|
||||
use oxydsp_flowgraph::block::Block;
|
||||
use oxydsp_flowgraph::block::BlockResult;
|
||||
use oxydsp_flowgraph::edge::In;
|
||||
use oxydsp_flowgraph::edge::Out;
|
||||
use oxydsp_flowgraph::edge::PopIterable;
|
||||
use oxydsp_flowgraph::edge::stream;
|
||||
|
||||
#[derive(BlockIO)]
|
||||
pub struct OscillatorSource<T: Float + From<f32> + 'static>
|
||||
{
|
||||
nco: crate::synthesis::oscillator::Nco<T>,
|
||||
|
||||
#[output]
|
||||
output: Out<Complex<T>>,
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32> + 'static> OscillatorSource<T>
|
||||
{
|
||||
pub fn new(nco: crate::synthesis::oscillator::Nco<T>) -> (Self, In<Complex<T>>)
|
||||
{
|
||||
let (output, signal) = stream();
|
||||
(Self { nco, output }, signal)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32> + 'static> Block for OscillatorSource<T>
|
||||
{
|
||||
fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult
|
||||
{
|
||||
self.output.push_iter(&mut self.nco);
|
||||
BlockResult::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BlockIO)]
|
||||
pub struct Nco<T: Float + From<f32> + 'static>
|
||||
{
|
||||
nco: crate::synthesis::oscillator::Nco<T>,
|
||||
|
||||
#[input]
|
||||
frequency: In<DigitalFrequency>,
|
||||
|
||||
#[output]
|
||||
output: Out<Complex<T>>,
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32> + 'static> Nco<T>
|
||||
{
|
||||
pub fn new(
|
||||
input: In<DigitalFrequency>,
|
||||
nco: crate::synthesis::oscillator::Nco<T>,
|
||||
) -> (Self, In<Complex<T>>)
|
||||
{
|
||||
let (output, signal) = stream();
|
||||
(
|
||||
Self {
|
||||
nco,
|
||||
frequency: input,
|
||||
output,
|
||||
},
|
||||
signal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32> + 'static> Block for Nco<T>
|
||||
{
|
||||
fn work(&mut self) -> oxydsp_flowgraph::block::BlockResult
|
||||
{
|
||||
self.output
|
||||
.push_iter(&mut self.frequency.pop_iter().map(|f| {
|
||||
self.nco.set_frequency(f);
|
||||
self.nco.next().unwrap()
|
||||
}));
|
||||
BlockResult::Ok
|
||||
}
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
pub mod adapters;
|
||||
pub mod iter;
|
||||
|
||||
1
oxydsp-dsp/src/blocks/utilities/adapters.rs
Normal file
1
oxydsp-dsp/src/blocks/utilities/adapters.rs
Normal file
@ -0,0 +1 @@
|
||||
|
||||
@ -1 +1,10 @@
|
||||
use num::Float;
|
||||
|
||||
pub mod blocks;
|
||||
pub mod synthesis;
|
||||
pub mod units;
|
||||
|
||||
fn map<T: Float>(x: T, x_min: T, x_max: T, o_min: T, o_max: T) -> T
|
||||
{
|
||||
((x - x_min) / (x_max - x_min)) * (o_max - o_min) + o_min
|
||||
}
|
||||
|
||||
1
oxydsp-dsp/src/synthesis.rs
Normal file
1
oxydsp-dsp/src/synthesis.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod oscillator;
|
||||
91
oxydsp-dsp/src/synthesis/oscillator.rs
Normal file
91
oxydsp-dsp/src/synthesis/oscillator.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use std::f32::consts::PI;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use num::Complex;
|
||||
use num::Float;
|
||||
|
||||
use crate::map;
|
||||
use crate::units::DigitalFrequency;
|
||||
use crate::units::Phase;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Nco<T>
|
||||
{
|
||||
// Current phase angle of the oscillator
|
||||
phase: usize,
|
||||
|
||||
// "Phase derivative": How much to increase the phase per sample
|
||||
d_phase: usize,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Nco<T>
|
||||
{
|
||||
pub fn new(frequency: DigitalFrequency) -> Self
|
||||
{
|
||||
Self {
|
||||
phase: 0,
|
||||
d_phase: frequency.0,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_phase(frequency: DigitalFrequency, phase: Phase) -> Self
|
||||
{
|
||||
Self {
|
||||
phase: phase.0.0,
|
||||
d_phase: frequency.0,
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_phase(&mut self, phase: Phase)
|
||||
{
|
||||
self.phase = phase.0.0;
|
||||
}
|
||||
|
||||
pub fn set_frequency(&mut self, frequency: DigitalFrequency)
|
||||
{
|
||||
self.d_phase = frequency.0;
|
||||
}
|
||||
|
||||
pub fn step(&mut self)
|
||||
{
|
||||
let _ = self.phase.overflowing_add(self.d_phase);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32>> Nco<T>
|
||||
{
|
||||
pub fn sample(&self) -> Complex<T>
|
||||
{
|
||||
let t = map(
|
||||
<T as From<f32>>::from(self.phase as f32),
|
||||
<T as From<f32>>::from(0.0f32),
|
||||
<T as From<f32>>::from(usize::MAX as f32),
|
||||
<T as From<f32>>::from(0.0f32),
|
||||
<T as From<f32>>::from(2. * PI),
|
||||
);
|
||||
Complex::new(t.cos(), t.sin())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + From<f32>> Iterator for Nco<T>
|
||||
{
|
||||
type Item = Complex<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>
|
||||
{
|
||||
let s = self.sample();
|
||||
self.step();
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<DigitalFrequency> for Nco<T>
|
||||
{
|
||||
fn from(value: DigitalFrequency) -> Self
|
||||
{
|
||||
Nco::new(value)
|
||||
}
|
||||
}
|
||||
38
oxydsp-dsp/src/units.rs
Normal file
38
oxydsp-dsp/src/units.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use crate::map;
|
||||
|
||||
// Represents digital frequency
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct DigitalFrequency(pub usize);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
pub struct Phase(pub DigitalFrequency);
|
||||
|
||||
impl DigitalFrequency
|
||||
{
|
||||
pub fn from_rad(radians_per_sample: f64) -> Self
|
||||
{
|
||||
// Frequnecy wraps arround : Going at 2 pi radians per second
|
||||
// Is like not oscillating at all
|
||||
let f = radians_per_sample.rem_euclid(radians_per_sample);
|
||||
|
||||
// Then we map the [0, 2*PI] range into the 0 to usize range
|
||||
DigitalFrequency(map(f, 0., 2. * PI, 0., usize::MAX as f64).floor() as usize)
|
||||
}
|
||||
|
||||
pub fn from_time_frequency(hertz: f64, sample_rate: f64) -> Self
|
||||
{
|
||||
Self::from_rad(map(hertz, 0., sample_rate, 0., 2. * PI))
|
||||
}
|
||||
|
||||
pub fn as_rad(&self) -> f64
|
||||
{
|
||||
map(self.0 as f64, 0., usize::MAX as f64, 0., 2. * PI)
|
||||
}
|
||||
|
||||
pub fn as_time_frequency(&self, sample_rate: f64) -> f64
|
||||
{
|
||||
map(self.0 as f64, 0., usize::MAX as f64, 0., sample_rate)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user