use std::cmp::min;
use futuredsp::FirFilter;
use futuredsp::prelude::*;
use futuresdr::runtime::dev::prelude::*;
use super::utilities::partition_filter_taps;
use super::window_buffer::WindowBuffer;
enum ResampState {
Interpolate,
Boundary,
}
struct State {
num_filters: usize,
fir_filters: Vec<FirFilter<Complex32, Complex32, Vec<f32>>>,
window_buf: WindowBuffer,
rate: f32,
delay: f32,
buff: [Complex32; 2], state: ResampState,
tau: f32, bf: f32, base_index: usize, mu: f32, }
#[derive(Block)]
pub struct PfbArbResampler<
I: CpuBufferReader<Item = Complex32> = DefaultCpuReader<Complex32>,
O: CpuBufferWriter<Item = Complex32> = DefaultCpuWriter<Complex32>,
> {
s: State,
#[input]
input: I,
#[output]
output: O,
}
impl<I, O> PfbArbResampler<I, O>
where
I: CpuBufferReader<Item = Complex32>,
O: CpuBufferWriter<Item = Complex32>,
{
#[allow(clippy::new_ret_no_self)]
pub fn new(rate: f32, taps: &[f32], num_filters: usize) -> Self {
assert!(
rate > 0.,
"PfbArbResampler: resampling rate must be greater than zero"
);
assert!(
taps.len() >= num_filters,
"PfbArbResampler: prototype filter length must be at least num_filters"
);
assert_ne!(
num_filters, 0,
"PfbArbResampler: number of filter banks must be greater than zero"
);
let (partitioned_filters, filter_length) = partition_filter_taps(taps, num_filters);
let mut output = O::default();
output.set_min_items(rate.ceil() as usize);
Self {
s: State {
num_filters,
fir_filters: partitioned_filters,
window_buf: WindowBuffer::new(filter_length, false),
rate,
delay: 1.0 / rate,
buff: [Complex32::new(0., 0.); 2],
state: ResampState::Interpolate,
tau: 0.0,
bf: 0.0,
base_index: 0,
mu: 0.0,
},
input: I::default(),
output,
}
}
}
impl State {
fn update_timing_state(&mut self) {
self.tau += self.delay;
self.bf = self.tau * self.num_filters as f32;
self.base_index = self.bf.floor() as usize; self.mu = self.bf - self.base_index as f32; }
fn consume_single(&mut self, sample: Complex32, out_buf: &mut [Complex32]) -> usize {
self.window_buf.push(sample);
let mut produced: usize = 0;
while self.base_index < self.num_filters {
match self.state {
ResampState::Boundary => {
self.fir_filters[0]
.filter(self.window_buf.get_as_slice(), &mut self.buff[1..2]);
out_buf[produced] = (1.0 - self.mu) * self.buff[0] + self.mu * self.buff[1];
produced += 1;
self.update_timing_state();
self.state = ResampState::Interpolate;
}
ResampState::Interpolate => {
self.fir_filters[self.base_index]
.filter(self.window_buf.get_as_slice(), &mut self.buff[0..1]);
if self.base_index == self.num_filters - 1 {
self.state = ResampState::Boundary;
self.base_index = self.num_filters;
} else {
self.fir_filters[self.base_index + 1]
.filter(self.window_buf.get_as_slice(), &mut self.buff[1..2]);
out_buf[produced] = (1.0 - self.mu) * self.buff[0] + self.mu * self.buff[1];
produced += 1;
self.update_timing_state();
}
}
}
}
self.tau -= 1.0;
self.bf -= self.num_filters as f32;
self.base_index -= self.num_filters;
produced
}
}
#[doc(hidden)]
impl Kernel for PfbArbResampler {
async fn work(
&mut self,
io: &mut WorkIo,
_mo: &mut MessageOutputs,
_b: &mut BlockMeta,
) -> Result<()> {
let input = self.input.slice();
let ninput_items = input.len();
if !self.s.window_buf.filled() {
let mut consumed = 0;
while !self.s.window_buf.filled() && consumed < ninput_items {
self.s.window_buf.push(input[consumed]);
consumed += 1;
}
self.input.consume(consumed);
if ninput_items - consumed > 0 {
io.call_again = true;
} else if self.input.finished() {
io.finished = true;
}
return Ok(());
}
let out = self.output.slice();
let noutput_items = out.len();
let nitem_to_process = min(ninput_items, (noutput_items as f32 / self.s.rate) as usize);
if nitem_to_process > 0 {
let mut produced: usize = 0;
for sample in input.iter().take(nitem_to_process) {
produced += self.s.consume_single(*sample, &mut out[produced..])
}
self.input.consume(nitem_to_process);
self.output.produce(produced);
}
if ninput_items - nitem_to_process == 0 && self.input.finished() {
io.finished = true;
}
Ok(())
}
}