#![allow(clippy::type_complexity)]
use futuredsp::ComputationStatus;
use futuredsp::DecimatingFirFilter;
use futuredsp::FirFilter;
use futuredsp::MmseResampler;
use futuredsp::PolyphaseResamplingFir;
use futuredsp::firdes;
use futuredsp::prelude::*;
use num_traits::Num;
use std::iter::Sum;
use std::ops::Mul;
use crate::prelude::*;
#[derive(Block)]
pub struct Fir<
InputType,
OutputType,
TapType,
Core,
IN = DefaultCpuReader<InputType>,
OUT = DefaultCpuWriter<OutputType>,
> where
InputType: CpuSample,
OutputType: CpuSample,
TapType: 'static + Send,
Core: Filter<InputType, OutputType, TapType> + Send,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
#[input]
input: IN,
#[output]
output: OUT,
filter: Core,
_tap_type: std::marker::PhantomData<TapType>,
}
impl<InputType, OutputType, TapType, Core, IN, OUT>
Fir<InputType, OutputType, TapType, Core, IN, OUT>
where
InputType: CpuSample,
OutputType: CpuSample,
TapType: 'static + Send,
Core: Filter<InputType, OutputType, TapType> + Send + 'static,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
pub fn new(filter: Core) -> Self {
let mut input = IN::default();
input.set_min_items(filter.length());
Self {
input,
output: OUT::default(),
filter,
_tap_type: std::marker::PhantomData,
}
}
pub fn n_taps(&self) -> usize {
self.filter.length()
}
}
#[doc(hidden)]
impl<InputType, OutputType, TapType, Core, IN, OUT> Kernel
for Fir<InputType, OutputType, TapType, Core, IN, OUT>
where
InputType: CpuSample,
OutputType: CpuSample,
TapType: 'static + Send,
Core: Filter<InputType, OutputType, TapType> + Send + 'static,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
async fn work(
&mut self,
io: &mut WorkIo,
_mio: &mut MessageOutputs,
_meta: &mut BlockMeta,
) -> Result<()> {
let i = self.input.slice();
let o = self.output.slice();
let (consumed, produced, status) = self.filter.filter(i, o);
self.input.consume(consumed);
self.output.produce(produced);
if self.input.finished() && !matches!(status, ComputationStatus::InsufficientOutput) {
io.finished = true;
}
Ok(())
}
}
#[derive(Block)]
pub struct StatefulFir<
InputType,
OutputType,
TapType,
Core,
IN = DefaultCpuReader<InputType>,
OUT = DefaultCpuWriter<OutputType>,
> where
InputType: 'static + Send,
OutputType: 'static + Send,
TapType: 'static + Send,
Core: StatefulFilter<InputType, OutputType, TapType> + Send,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
#[input]
input: IN,
#[output]
output: OUT,
filter: Core,
_tap_type: std::marker::PhantomData<TapType>,
}
impl<InputType, OutputType, TapType, Core, IN, OUT>
StatefulFir<InputType, OutputType, TapType, Core, IN, OUT>
where
InputType: 'static + Send,
OutputType: 'static + Send,
TapType: 'static + Send,
Core: StatefulFilter<InputType, OutputType, TapType> + Send + 'static,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
pub fn new(filter: Core) -> Self {
Self {
input: IN::default(),
output: OUT::default(),
filter,
_tap_type: std::marker::PhantomData,
}
}
}
#[doc(hidden)]
impl<InputType, OutputType, TapType, Core, IN, OUT> Kernel
for StatefulFir<InputType, OutputType, TapType, Core, IN, OUT>
where
InputType: 'static + Send,
OutputType: 'static + Send,
TapType: 'static + Send,
Core: StatefulFilter<InputType, OutputType, TapType> + Send + 'static,
IN: CpuBufferReader<Item = InputType>,
OUT: CpuBufferWriter<Item = OutputType>,
{
async fn work(
&mut self,
io: &mut WorkIo,
_mio: &mut MessageOutputs,
_meta: &mut BlockMeta,
) -> Result<()> {
let i = self.input.slice();
let o = self.output.slice();
let (consumed, produced, status) = self.filter.filter(i, o);
self.input.consume(consumed);
self.output.produce(produced);
if self.input.finished() && !matches!(status, ComputationStatus::InsufficientOutput) {
io.finished = true;
}
Ok(())
}
}
pub struct FirBuilder;
impl FirBuilder {
pub fn fir<InputType, OutputType, TapsType>(
taps: TapsType,
) -> Fir<InputType, OutputType, TapsType::TapType, FirFilter<InputType, OutputType, TapsType>>
where
InputType: CpuSample,
OutputType: CpuSample,
TapsType: 'static + Taps + Send,
TapsType::TapType: 'static + Send,
FirFilter<InputType, OutputType, TapsType>:
futuredsp::Filter<InputType, OutputType, TapsType::TapType>,
{
Fir::<InputType, OutputType, TapsType::TapType, FirFilter<InputType, OutputType, TapsType>>::new(FirFilter::new(taps))
}
pub fn decimating<InputType, OutputType, TapsType>(
decim: usize,
) -> Fir<InputType, OutputType, f32, DecimatingFirFilter<InputType, OutputType, Vec<f32>>>
where
InputType: CpuSample,
OutputType: CpuSample,
DecimatingFirFilter<InputType, OutputType, Vec<f32>>:
futuredsp::Filter<InputType, OutputType, f32>,
{
let taps = firdes::kaiser::lowpass::<f32>(1.0 / decim as f64, 0.1, 0.0001);
FirBuilder::decimating_with_taps(decim, taps)
}
pub fn decimating_with_taps<InputType, OutputType, TapsType>(
decim: usize,
taps: TapsType,
) -> Fir<
InputType,
OutputType,
TapsType::TapType,
DecimatingFirFilter<InputType, OutputType, TapsType>,
>
where
InputType: CpuSample,
OutputType: CpuSample,
TapsType: 'static + Taps + Send,
TapsType::TapType: 'static + Send,
DecimatingFirFilter<InputType, OutputType, TapsType>:
futuredsp::Filter<InputType, OutputType, TapsType::TapType>,
{
Fir::<
InputType,
OutputType,
TapsType::TapType,
DecimatingFirFilter<InputType, OutputType, TapsType>,
>::new(DecimatingFirFilter::new(decim, taps))
}
pub fn resampling<InputType, OutputType>(
interp: usize,
decim: usize,
) -> Fir<InputType, OutputType, f32, PolyphaseResamplingFir<InputType, OutputType, Vec<f32>>>
where
InputType: CpuSample,
OutputType: CpuSample,
PolyphaseResamplingFir<InputType, OutputType, Vec<f32>>: Filter<InputType, OutputType, f32>,
{
let gcd = num_integer::gcd(interp, decim);
let interp = interp / gcd;
let decim = decim / gcd;
let taps = firdes::kaiser::multirate::<f32>(interp, decim, 12, 0.0001);
FirBuilder::resampling_with_taps::<InputType, OutputType, _>(interp, decim, taps)
}
pub fn resampling_with_taps<InputType, OutputType, TapsType>(
interp: usize,
decim: usize,
taps: TapsType,
) -> Fir<
InputType,
OutputType,
TapsType::TapType,
PolyphaseResamplingFir<InputType, OutputType, TapsType>,
>
where
InputType: CpuSample,
OutputType: CpuSample,
TapsType: 'static + Taps + Send,
TapsType::TapType: 'static + Send,
PolyphaseResamplingFir<InputType, OutputType, TapsType>:
Filter<InputType, OutputType, TapsType::TapType>,
{
Fir::<
InputType,
OutputType,
TapsType::TapType,
PolyphaseResamplingFir<InputType, OutputType, TapsType>,
>::new(PolyphaseResamplingFir::new(interp, decim, taps))
}
pub fn mmse<SampleType>(
ratio: f32,
) -> StatefulFir<SampleType, SampleType, f32, MmseResampler<SampleType>>
where
SampleType:
CpuSample + Copy + Num + Sum<SampleType> + Mul<f32, Output = SampleType> + 'static,
MmseResampler<SampleType>: StatefulFilter<SampleType, SampleType, f32>,
{
StatefulFir::<SampleType, SampleType, f32, MmseResampler<SampleType>>::new(
MmseResampler::new(ratio),
)
}
}