[perf](trx-backend-soapysdr): use ring history for wfm resampler
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -80,21 +80,19 @@ fn build_wfm_resample_bank() -> [[f32; WFM_RESAMP_TAPS]; WFM_RESAMP_PHASES] {
|
|||||||
bank
|
bank
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn shift_append<const N: usize>(hist: &mut [f32; N], sample: f32) {
|
|
||||||
hist.rotate_left(1);
|
|
||||||
hist[N - 1] = sample;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn polyphase_resample(
|
fn polyphase_resample(
|
||||||
hist: &[f32; WFM_RESAMP_TAPS],
|
hist: &[f32; WFM_RESAMP_TAPS],
|
||||||
|
hist_head: usize,
|
||||||
bank: &[[f32; WFM_RESAMP_TAPS]; WFM_RESAMP_PHASES],
|
bank: &[[f32; WFM_RESAMP_TAPS]; WFM_RESAMP_PHASES],
|
||||||
frac: f32,
|
frac: f32,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let phase = (frac.clamp(0.0, 0.999_999) * WFM_RESAMP_PHASES as f32).round() as usize;
|
let phase = (frac.clamp(0.0, 0.999_999) * WFM_RESAMP_PHASES as f32).round() as usize;
|
||||||
let phase = phase.min(WFM_RESAMP_PHASES - 1);
|
let phase = phase.min(WFM_RESAMP_PHASES - 1);
|
||||||
dot_product(&hist[..], &bank[phase][..])
|
let coeffs = &bank[phase];
|
||||||
|
let first = WFM_RESAMP_TAPS - hist_head.min(WFM_RESAMP_TAPS);
|
||||||
|
dot_product(&hist[hist_head..], &coeffs[..first])
|
||||||
|
+ dot_product(&hist[..hist_head], &coeffs[first..])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -694,6 +692,8 @@ pub struct WfmStereoDecoder {
|
|||||||
diff_hist: [f32; WFM_RESAMP_TAPS],
|
diff_hist: [f32; WFM_RESAMP_TAPS],
|
||||||
/// History ring for polyphase FIR resampling of the quadrature diff channel.
|
/// History ring for polyphase FIR resampling of the quadrature diff channel.
|
||||||
diff_q_hist: [f32; WFM_RESAMP_TAPS],
|
diff_q_hist: [f32; WFM_RESAMP_TAPS],
|
||||||
|
/// Shared ring head for the polyphase FIR histories; points to the oldest slot.
|
||||||
|
hist_head: usize,
|
||||||
/// Previous pilot blend sample for simple linear interpolation.
|
/// Previous pilot blend sample for simple linear interpolation.
|
||||||
prev_blend: f32,
|
prev_blend: f32,
|
||||||
/// Fractional phase increment per composite sample = audio_rate / composite_rate.
|
/// Fractional phase increment per composite sample = audio_rate / composite_rate.
|
||||||
@@ -753,6 +753,7 @@ impl WfmStereoDecoder {
|
|||||||
sum_hist: [0.0; WFM_RESAMP_TAPS],
|
sum_hist: [0.0; WFM_RESAMP_TAPS],
|
||||||
diff_hist: [0.0; WFM_RESAMP_TAPS],
|
diff_hist: [0.0; WFM_RESAMP_TAPS],
|
||||||
diff_q_hist: [0.0; WFM_RESAMP_TAPS],
|
diff_q_hist: [0.0; WFM_RESAMP_TAPS],
|
||||||
|
hist_head: 0,
|
||||||
prev_blend: 0.0,
|
prev_blend: 0.0,
|
||||||
output_phase_inc,
|
output_phase_inc,
|
||||||
output_phase: 0.0,
|
output_phase: 0.0,
|
||||||
@@ -845,9 +846,10 @@ impl WfmStereoDecoder {
|
|||||||
// --- Polyphase FIR fractional resampling ---
|
// --- Polyphase FIR fractional resampling ---
|
||||||
// This uses a short windowed-sinc bank instead of cubic interpolation
|
// This uses a short windowed-sinc bank instead of cubic interpolation
|
||||||
// to reduce top-end overshoot/ringing near the audio cutoff.
|
// to reduce top-end overshoot/ringing near the audio cutoff.
|
||||||
shift_append(&mut self.sum_hist, sum);
|
self.sum_hist[self.hist_head] = sum;
|
||||||
shift_append(&mut self.diff_hist, diff_i);
|
self.diff_hist[self.hist_head] = diff_i;
|
||||||
shift_append(&mut self.diff_q_hist, diff_q);
|
self.diff_q_hist[self.hist_head] = diff_q;
|
||||||
|
self.hist_head = (self.hist_head + 1) % WFM_RESAMP_TAPS;
|
||||||
|
|
||||||
let prev_phase = self.output_phase;
|
let prev_phase = self.output_phase;
|
||||||
self.output_phase += self.output_phase_inc;
|
self.output_phase += self.output_phase_inc;
|
||||||
@@ -861,9 +863,9 @@ impl WfmStereoDecoder {
|
|||||||
// interval. The FIR bank reconstructs a band-limited sample using
|
// interval. The FIR bank reconstructs a band-limited sample using
|
||||||
// a fixed two-sample lookahead in the decoder.
|
// a fixed two-sample lookahead in the decoder.
|
||||||
let frac = ((1.0 - prev_phase) / self.output_phase_inc) as f32;
|
let frac = ((1.0 - prev_phase) / self.output_phase_inc) as f32;
|
||||||
let sum_i = polyphase_resample(&self.sum_hist, &self.resample_bank, frac);
|
let sum_i = polyphase_resample(&self.sum_hist, self.hist_head, &self.resample_bank, frac);
|
||||||
let diff_i = polyphase_resample(&self.diff_hist, &self.resample_bank, frac);
|
let diff_i = polyphase_resample(&self.diff_hist, self.hist_head, &self.resample_bank, frac);
|
||||||
let diff_q = polyphase_resample(&self.diff_q_hist, &self.resample_bank, frac);
|
let diff_q = polyphase_resample(&self.diff_q_hist, self.hist_head, &self.resample_bank, frac);
|
||||||
let blend_i =
|
let blend_i =
|
||||||
(self.prev_blend + frac * (stereo_blend_target - self.prev_blend)).clamp(0.0, 1.0);
|
(self.prev_blend + frac * (stereo_blend_target - self.prev_blend)).clamp(0.0, 1.0);
|
||||||
self.prev_blend = stereo_blend_target;
|
self.prev_blend = stereo_blend_target;
|
||||||
@@ -953,6 +955,7 @@ impl WfmStereoDecoder {
|
|||||||
self.sum_hist = [0.0; WFM_RESAMP_TAPS];
|
self.sum_hist = [0.0; WFM_RESAMP_TAPS];
|
||||||
self.diff_hist = [0.0; WFM_RESAMP_TAPS];
|
self.diff_hist = [0.0; WFM_RESAMP_TAPS];
|
||||||
self.diff_q_hist = [0.0; WFM_RESAMP_TAPS];
|
self.diff_q_hist = [0.0; WFM_RESAMP_TAPS];
|
||||||
|
self.hist_head = 0;
|
||||||
self.prev_blend = 0.0;
|
self.prev_blend = 0.0;
|
||||||
self.output_phase = 0.0;
|
self.output_phase = 0.0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user