45 static_cast<void>(odd_taps_oldest_first());
49 auto& own = parity_[phase_];
50 auto& other = parity_[phase_ ^ 1u];
51 if (own.head == kHistory)
53 own.samples[own.head++] = input;
56 const double centre =
coefficients()[kCentre] * own.samples[own.head - 1 - kCentre / 2];
57 const double odd = pulp::simd::dot(other.samples.data() + other.head - kOddTaps,
58 odd_taps_oldest_first().data(), kOddTaps);
64 for (
auto& p : parity_) {
73 static const std::array<double, kTaps> result = design();
78 static constexpr std::size_t kTaps = 65;
79 static constexpr std::size_t kCentre = (kTaps - 1) / 2;
80 static constexpr std::size_t kOddTaps = (kTaps - 1) / 2;
83 static constexpr std::size_t kKeep = kOddTaps;
84 static constexpr std::size_t kCapacity = 64;
85 static constexpr std::size_t kHistory = kKeep + kCapacity;
86 static constexpr double kBeta = 8.0;
88 static double sinc(
double x)
noexcept {
89 if (std::abs(x) < 1.0e-15)
91 constexpr double kPi = 3.141592653589793238462643383279502884;
92 const double pix = kPi * x;
93 return std::sin(pix) / pix;
96 static std::array<double, kTaps>
design() noexcept {
97 std::array<double, kTaps> result{};
98 const double denominator =
bessel_i0(kBeta);
100 for (std::size_t tap = 0; tap < kTaps; ++tap) {
102 static_cast<int>(tap) -
static_cast<int>(kCentre);
103 if (offset != 0 && (offset & 1) == 0) {
107 const double position =
108 static_cast<double>(offset) /
static_cast<double>(kCentre);
109 const double window =
111 std::max(0.0, 1.0 - position * position))) /
114 0.5 * sinc(0.5 *
static_cast<double>(offset)) * window;
117 for (
double& coefficient : result)
123 static const std::array<double, kOddTaps>& odd_taps_oldest_first() noexcept {
124 static const std::array<double, kOddTaps> result = [] {
125 std::array<double, kOddTaps> taps{};
126 for (std::size_t j = 0;
j < kOddTaps; ++
j)
133 struct ParityHistory {
135 std::array<double, kHistory> samples{};
136 std::size_t head = kKeep;
138 void compact() noexcept {
139 std::copy(samples.begin() +
static_cast<std::ptrdiff_t
>(head - kKeep),
140 samples.begin() +
static_cast<std::ptrdiff_t
>(head), samples.begin());
145 std::array<ParityHistory, 2> parity_{};
156 const double passband = 0.5 * passband_edge_fraction;
157 constexpr double stopband = 0.25;
158 const double transition = stopband - passband;
159 const double cutoff = passband + 0.5 * transition;
165 void configure(
double passband_edge_fraction,
double stopband_db, std::size_t
taps) {
169 std::vector<SampleType> up_even;
170 std::vector<SampleType> up_odd;
171 std::vector<SampleType> down_even;
172 std::vector<SampleType> down_odd;
173 up_even.reserve((prototype.size() + 1) / 2);
174 up_odd.reserve(prototype.size() / 2);
175 down_even.reserve((prototype.size() + 1) / 2);
176 down_odd.reserve(prototype.size() / 2);
177 for (std::size_t
i = 0;
i < prototype.size(); ++
i) {
178 const auto coefficient =
static_cast<SampleType
>(prototype[
i]);
179 auto& up_phase = (
i & 1u) == 0
u ? up_even : up_odd;
180 auto& down_phase = (
i & 1u) == 0
u ? down_even : down_odd;
181 up_phase.push_back(SampleType{2} * coefficient);
182 down_phase.push_back(coefficient);
184 up_even_.set_coefficients(std::move(up_even));
185 up_odd_.set_coefficients(std::move(up_odd));
186 down_even_.set_coefficients(std::move(down_even));
187 down_odd_.set_coefficients(std::move(down_odd));
196 previous_odd_ = SampleType{0};
199 void upsample(SampleType input, SampleType& even, SampleType& odd) {
200 even = up_even_.process(input);
201 odd = up_odd_.process(input);
205 const SampleType output = down_even_.process(even) + down_odd_.process(previous_odd_);
210 std::size_t
taps() const noexcept {
219 SampleType previous_odd_ = SampleType{0};
220 std::size_t taps_ = 0;
234#if !defined(PULP_SIGNAL_HARDWARE_FMA)
235#if defined(__aarch64__) || defined(_M_ARM64)
236#define PULP_SIGNAL_HARDWARE_FMA 1
238#define PULP_SIGNAL_HARDWARE_FMA 0
246template <
typename SampleType>
248#if PULP_SIGNAL_HARDWARE_FMA
249 return std::fma(
a, b, acc);
268 static constexpr std::size_t
kBlock = 256;
271 reversed_.assign(time_order.rbegin(), time_order.rend());
272 history_.assign(reversed_.empty() ? 0 : reversed_.size() - 1 +
kBlock, SampleType{0});
277 std::fill(history_.begin(), history_.end(), SampleType{0});
280 std::size_t
taps() const noexcept {
281 return reversed_.size();
285 void process(
const SampleType* input, SampleType* output, std::size_t n)
noexcept {
286 const std::size_t
taps = reversed_.size();
287 const std::size_t keep =
taps - 1;
288 SampleType* history = history_.data();
289 std::copy_n(input, n, history + keep);
290 const SampleType* h = reversed_.data();
291 constexpr std::size_t kLanes = 16;
293 for (;
i + kLanes <= n;
i += kLanes) {
294 SampleType acc[kLanes] = {};
295 const SampleType* x = history +
i;
296 for (std::size_t k = 0; k <
taps; ++k) {
297 const SampleType
c = h[k];
298 for (std::size_t lane = 0; lane < kLanes; ++lane)
301 std::copy_n(acc, kLanes, output +
i);
304 SampleType acc = SampleType{0};
305 const SampleType* x = history +
i;
306 for (std::size_t k = 0; k <
taps; ++k)
310 std::copy(history + n, history + n + keep, history);
314 std::vector<SampleType> reversed_;
315 std::vector<SampleType> history_;
326 void configure(
double passband_edge_fraction,
double stopband_db, std::size_t
taps) {
327 const auto prototype =
329 std::vector<SampleType> up_even, up_odd, down_even, down_odd;
330 for (std::size_t
i = 0;
i < prototype.size(); ++
i) {
331 const auto coefficient =
static_cast<SampleType
>(prototype[
i]);
332 ((
i & 1u) == 0
u ? up_even : up_odd).push_back(SampleType{2} * coefficient);
333 ((
i & 1u) == 0
u ? down_even : down_odd).push_back(coefficient);
335 up_even_.set_coefficients(up_even);
336 up_odd_.set_coefficients(up_odd);
337 down_even_.set_coefficients(down_even);
338 down_odd_.set_coefficients(down_odd);
348 previous_odd_ = SampleType{0};
351 std::size_t
taps() const noexcept {
357 void upsample(
const SampleType* input, SampleType* output, std::size_t n)
noexcept {
358 for (std::size_t done = 0; done < n; done +=
kBlock) {
359 const std::size_t count = std::min(
kBlock, n - done);
360 up_even_.process(input + done, even_.data(), count);
361 up_odd_.process(input + done, odd_.data(), count);
362 SampleType* out = output + 2 * done;
363 for (std::size_t
i = 0;
i < count; ++
i) {
364 out[2 *
i] = even_[
i];
365 out[2 *
i + 1] = odd_[
i];
372 void downsample(
const SampleType* input, SampleType* output, std::size_t n)
noexcept {
373 for (std::size_t done = 0; done < n; done +=
kBlock) {
374 const std::size_t count = std::min(
kBlock, n - done);
375 const SampleType* in = input + 2 * done;
376 for (std::size_t
i = 0;
i < count; ++
i) {
377 split_even_[
i] = in[2 *
i];
378 split_odd_[
i] =
i == 0 ? previous_odd_ : in[2 *
i - 1];
380 previous_odd_ = in[2 * count - 1];
381 down_even_.process(split_even_.data(), even_.data(), count);
382 down_odd_.process(split_odd_.data(), odd_.data(), count);
383 for (std::size_t
i = 0;
i < count; ++
i)
384 output[done +
i] = even_[
i] + odd_[
i];
393 std::array<SampleType, kBlock> split_even_{};
394 std::array<SampleType, kBlock> split_odd_{};
395 std::array<SampleType, kBlock> even_{};
396 std::array<SampleType, kBlock> odd_{};
397 SampleType previous_odd_ = SampleType{0};
398 std::size_t taps_ = 0;
FIR filter with arbitrary coefficients.
Definition fir_filter.hpp:39
double process(double input) noexcept
Definition oversampling_fir.hpp:48
static const std::array< double, 65 > & coefficients() noexcept
The 65 prototype taps (symmetric; centre index 32; even offsets zero).
Definition oversampling_fir.hpp:72
void reset() noexcept
Definition oversampling_fir.hpp:63
FixedHalfBandFir65() noexcept
Definition oversampling_fir.hpp:42
One polyphase branch of a block 2x stage: a direct-form FIR whose every output is the taps summed old...
Definition oversampling_fir.hpp:266
std::size_t taps() const noexcept
Definition oversampling_fir.hpp:280
void process(const SampleType *input, SampleType *output, std::size_t n) noexcept
n <= kBlock. output must not alias input.
Definition oversampling_fir.hpp:285
void reset()
Definition oversampling_fir.hpp:276
void set_coefficients(const std::vector< SampleType > &time_order)
Definition oversampling_fir.hpp:270
static constexpr std::size_t kBlock
Definition oversampling_fir.hpp:268
The block form of LinearPhaseOversamplingStage2x, for OversamplerT: the same prototype and the same p...
Definition oversampling_fir.hpp:322
std::size_t taps() const noexcept
Definition oversampling_fir.hpp:351
void configure(double passband_edge_fraction, double stopband_db, std::size_t taps)
Definition oversampling_fir.hpp:326
void downsample(const SampleType *input, SampleType *output, std::size_t n) noexcept
2n interleaved inputs to n outputs, the odd branch one input sample behind.
Definition oversampling_fir.hpp:372
void reset()
Definition oversampling_fir.hpp:343
void upsample(const SampleType *input, SampleType *output, std::size_t n) noexcept
n input samples to 2n interleaved outputs.
Definition oversampling_fir.hpp:357
static constexpr std::size_t kBlock
Definition oversampling_fir.hpp:324
Definition oversampling_fir.hpp:163
SampleType downsample(SampleType even, SampleType odd)
Definition oversampling_fir.hpp:204
std::size_t taps() const noexcept
Definition oversampling_fir.hpp:210
void reset()
Definition oversampling_fir.hpp:191
void configure(double passband_edge_fraction, double stopband_db, std::size_t taps)
Definition oversampling_fir.hpp:165
void upsample(SampleType input, SampleType &even, SampleType &odd)
Definition oversampling_fir.hpp:199
Finite Impulse Response filter with configurable order and coefficients.
Definition design_adherence.hpp:32
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
SampleType fir_multiply_add(SampleType a, SampleType b, SampleType acc) noexcept
acc + a * b for the block FIR: one fused multiply-add where the hardware has it, a separate multiply ...
Definition oversampling_fir.hpp:247
std::vector< double > linear_phase_oversampling_prototype(double passband_edge_fraction, double stopband_db, std::size_t taps)
The Kaiser-windowed lowpass prototype both linear-phase 2x stage forms split into polyphase branches.
Definition oversampling_fir.hpp:153
Definition additive_bank.hpp:218
float sinc(float x)
Sinc function: sin(pi*x) / (pi*x), with sinc(0) = 1.
Definition special_functions.hpp:17
constexpr double kHouseHalfBandStopbandDb
Definition oversampling_fir.hpp:22
@ c
Definition spectrum_trace.hpp:35
constexpr std::size_t kHouseHalfBandTaps
Pulp's low-latency linear-phase half-band design.
Definition oversampling_fir.hpp:21
constexpr double kHouseHalfBandStageTwoPassband
Definition oversampling_fir.hpp:24
float bessel_i0(float x)
Modified Bessel function of the first kind, order 0 Used in Kaiser window design.
Definition special_functions.hpp:25
@ a
Definition formant_filter_bank.hpp:20
@ u
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
constexpr double kHouseHalfBandStageOnePassband
Definition oversampling_fir.hpp:23
std::vector< double > design_windowed_sinc(std::size_t taps, double cutoff, double beta)
Definition windowed_sinc_design.hpp:58
double kaiser_beta_for_stopband(double stopband_db)
Definition windowed_sinc_design.hpp:27
float sum(const float *data, std::size_t count) noexcept
@ j
Definition input_events.hpp:40
Shared Kaiser-windowed-sinc FIR design helpers.