Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
zero_latency_convolver_support.hpp
Go to the documentation of this file.
1#pragma once
2
134
135// `convolver_non_uniform.hpp` is named above for the reconciliation, but is
136// deliberately NOT included: it pulls the convolver message/slot machinery and
137// its CHOC FIFO dependency, and nothing here uses its types.
140#include <pulp/signal/units.hpp>
142
143#include <algorithm>
144#include <cmath>
145#include <complex>
146#include <cstddef>
147#include <cstdint>
148#include <limits>
149#include <vector>
150
151namespace pulp::signal {
152
162
163namespace detail {
164
180template <typename SampleType>
182public:
183 // Cost weights, in real flops per work item. A radix-2 butterfly is one
184 // complex multiply (6 flops) plus two complex adds (4 flops); a permuted
185 // gather or an in-place swap is one load/store pair; the inverse's
186 // conjugate and conjugate-scale passes are a negate, and a negate plus two
187 // multiplies, respectively.
188 static constexpr long kGatherCost = 1;
189 static constexpr long kButterflyCost = 10;
190 static constexpr long kConjugateCost = 2;
191 static constexpr long kScaleCost = 4;
192 static constexpr long kMacCost = 8;
193
194 void prepare(int size) {
195 size_ = size;
196 log2_size_ = 0;
197 for (int n = size; n > 1; n >>= 1) ++log2_size_;
198 reversed_.resize(static_cast<std::size_t>(size));
199 for (int i = 0; i < size; ++i) {
200 int r = 0;
201 for (int b = 0; b < log2_size_; ++b)
202 if (i & (1 << b)) r |= 1 << (log2_size_ - 1 - b);
203 reversed_[static_cast<std::size_t>(i)] = r;
204 }
205 // Compute in double, then store the processing precision once. The
206 // butterflies use these same rounded coefficients without converting
207 // every table load; float plans also need half the table storage.
208 twiddles_.resize(static_cast<std::size_t>(std::max(1, size / 2)));
209 constexpr double two_pi = 6.283185307179586476925286766559;
210 for (int i = 0; i < size / 2; ++i) {
211 const double angle = -two_pi * static_cast<double>(i) / static_cast<double>(size);
212 twiddles_[static_cast<std::size_t>(i)] = {static_cast<SampleType>(std::cos(angle)),
213 static_cast<SampleType>(std::sin(angle))};
214 }
215 }
216
217 int size() const { return size_; }
218 int log2_size() const { return log2_size_; }
219
224 long forward_cost() const {
225 return static_cast<long>(size_) * kGatherCost +
226 static_cast<long>(log2_size_) * static_cast<long>(size_ / 2) * kButterflyCost;
227 }
228 long inverse_cost() const {
229 return forward_cost() + static_cast<long>(size_) * (kConjugateCost + kScaleCost);
230 }
231
241 struct Cursor {
243
245 int cursor = 0;
246 int stage = 1;
247 bool inverse = false;
248 const SampleType* gather_src = nullptr;
249 std::size_t gather_base = 0;
250 std::size_t gather_mask = 0;
251
252 bool done() const { return phase == Phase::idle; }
253
255 void begin_forward_from_ring(const SampleType* ring, std::size_t base, std::size_t mask) {
257 cursor = 0;
258 stage = 1;
259 inverse = false;
260 gather_src = ring;
261 gather_base = base;
262 gather_mask = mask;
263 }
264
269 cursor = 0;
270 stage = 1;
271 inverse = false;
272 gather_src = nullptr;
273 }
274
279 cursor = 0;
280 stage = 1;
281 inverse = true;
282 gather_src = nullptr;
283 }
284
286
287 long advance(const SlicedFftPlanT& plan, std::complex<SampleType>* data, long budget) {
288 long spent = 0;
289 const int n = plan.size_;
290 while (phase != Phase::idle && spent < budget) {
291 switch (phase) {
292 case Phase::gather: {
293 const int take = affordable(budget - spent, kGatherCost, n - cursor);
294 if (take <= 0) return spent;
295 for (int i = cursor; i < cursor + take; ++i) {
296 const std::size_t src =
297 (gather_base +
298 static_cast<std::size_t>(plan.reversed_[static_cast<std::size_t>(i)])) &
300 data[i] = {gather_src[src], SampleType{0}};
301 }
302 cursor += take;
303 spent += static_cast<long>(take) * kGatherCost;
304 if (cursor >= n) { phase = Phase::butterflies; cursor = 0; stage = 1; }
305 break;
306 }
307 case Phase::butterflies: {
308 const int total = n / 2;
309 const int take = affordable(budget - spent, kButterflyCost, total - cursor);
310 if (take <= 0) return spent;
311 const int len = 1 << stage;
312 const int half = len / 2;
313 const int step = n / len;
314 for (int b = cursor; b < cursor + take; ++b) {
315 // half is 2^(stage - 1); keep the butterfly index
316 // arithmetic as shifts/masks in this hot loop.
317 const int group = b >> (stage - 1);
318 const int j = b & (half - 1);
319 const int i = group * len;
320 const auto& tw = plan.twiddles_[static_cast<std::size_t>(j * step)];
321 const auto w = tw;
322 const auto u = data[i + j];
323 const auto v = data[i + j + half] * w;
324 data[i + j] = u + v;
325 data[i + j + half] = u - v;
326 }
327 cursor += take;
328 spent += static_cast<long>(take) * kButterflyCost;
329 if (cursor >= total) {
330 cursor = 0;
331 ++stage;
332 if (stage > plan.log2_size_)
334 }
335 break;
336 }
337 case Phase::conjugate: {
338 const int take = affordable(budget - spent, kConjugateCost, n - cursor);
339 if (take <= 0) return spent;
340 for (int i = cursor; i < cursor + take; ++i)
341 data[i] = std::conj(data[i]);
342 cursor += take;
343 spent += static_cast<long>(take) * kConjugateCost;
344 if (cursor >= n) { phase = Phase::permute; cursor = 0; }
345 break;
346 }
347 case Phase::permute: {
348 const int take = affordable(budget - spent, kGatherCost, n - cursor);
349 if (take <= 0) return spent;
350 for (int i = cursor; i < cursor + take; ++i) {
351 const int j = plan.reversed_[static_cast<std::size_t>(i)];
352 if (i < j) std::swap(data[i], data[j]);
353 }
354 cursor += take;
355 spent += static_cast<long>(take) * kGatherCost;
356 if (cursor >= n) { phase = Phase::butterflies; cursor = 0; stage = 1; }
357 break;
358 }
359 case Phase::scale: {
360 const int take = affordable(budget - spent, kScaleCost, n - cursor);
361 if (take <= 0) return spent;
362 const SampleType inv_n = SampleType{1} / static_cast<SampleType>(n);
363 for (int i = cursor; i < cursor + take; ++i)
364 data[i] = std::conj(data[i]) * inv_n;
365 cursor += take;
366 spent += static_cast<long>(take) * kScaleCost;
367 if (cursor >= n) phase = Phase::idle;
368 break;
369 }
370 case Phase::idle:
371 return spent;
372 }
373 }
374 return spent;
375 }
376
377 private:
378 static int affordable(long budget, long unit_cost, int remaining) {
379 if (budget <= 0 || remaining <= 0) return 0;
380 return static_cast<int>(std::min<long>(budget / unit_cost, remaining));
381 }
382 };
383
386 void transform_real_padded(const double* src, int src_len,
387 std::complex<SampleType>* dst) const {
388 for (int i = 0; i < size_; ++i) {
389 const int r = reversed_[static_cast<std::size_t>(i)];
390 const double v = r < src_len ? src[r] : 0.0;
391 dst[i] = {static_cast<SampleType>(v), SampleType{0}};
392 }
393 for (int stage = 1; stage <= log2_size_; ++stage) {
394 const int len = 1 << stage;
395 const int half = len / 2;
396 const int step = size_ / len;
397 for (int i = 0; i < size_; i += len) {
398 for (int j = 0; j < half; ++j) {
399 const auto& tw = twiddles_[static_cast<std::size_t>(j * step)];
400 const auto w = tw;
401 const auto u = dst[i + j];
402 const auto v = dst[i + j + half] * w;
403 dst[i + j] = u + v;
404 dst[i + j + half] = u - v;
405 }
406 }
407 }
408 }
409
410private:
411 int size_ = 0;
412 int log2_size_ = 0;
413 std::vector<int> reversed_;
414 std::vector<std::complex<SampleType>> twiddles_;
415};
416
417} // namespace detail
418
423 // ── Design parameters ────────────────────────────────────────────────────
424 // Every value below is ours to tune and is declared with default + range.
425
430 static constexpr int kHeadLenMin = 64;
431 static constexpr int kHeadLenMax = 512;
432
434 static constexpr double kIrGainDbDefault = 0.0;
435 static constexpr double kIrGainDbMin = -24.0;
436 static constexpr double kIrGainDbMax = 24.0;
437
440 static constexpr double kPredelayMsDefault = 0.0;
441 static constexpr double kPredelayMsMin = 0.0;
442 static constexpr double kPredelayMsMax = 250.0;
443
445 static constexpr double kWetPercentDefault = 100.0;
446 static constexpr double kDryPercentDefault = 0.0;
447
455 static constexpr double kLowcutHzDefault = 20.0;
456 static constexpr double kLowcutHzMin = 20.0;
457 static constexpr double kLowcutHzMax = 500.0;
458 static constexpr double kHighcutHzDefault = 20000.0;
459 static constexpr double kHighcutHzMin = 1000.0;
460 static constexpr double kHighcutHzMax = 20000.0;
461
463 static constexpr double kWidthPercentDefault = 100.0;
464 static constexpr double kWidthPercentMin = 0.0;
465 static constexpr double kWidthPercentMax = 200.0;
466
469 static constexpr double kTailTrimDbDefault = -72.0;
470 static constexpr double kTailTrimDbMin = -90.0;
471 static constexpr double kTailTrimDbMax = -40.0;
472
475 static constexpr double kTailFadeMsDefault = 5.0;
476 static constexpr double kTailFadeMsMin = 2.0;
477 static constexpr double kTailFadeMsMax = 20.0;
478
483 static constexpr int kResampTapsPerPhaseDefault = 32;
484 static constexpr int kResampTapsPerPhaseMin = 16;
485 static constexpr int kResampTapsPerPhaseMax = 64;
486
491 static bool valid_resample_geometry(int source_length, double source_rate,
492 double destination_rate, int taps_per_phase) noexcept {
493 if (source_length <= 0 || taps_per_phase <= 0 ||
494 !std::isfinite(source_rate) || source_rate <= 0.0 ||
495 !std::isfinite(destination_rate) || destination_rate <= 0.0)
496 return false;
497 constexpr double int_max = static_cast<double>(std::numeric_limits<int>::max());
498 const double ratio = destination_rate / source_rate;
499 if (!std::isfinite(ratio) || ratio <= 0.0) return false;
500
501 const double destination_length =
502 std::ceil(static_cast<double>(source_length) * ratio);
503 if (!std::isfinite(destination_length) || destination_length < 1.0 ||
504 destination_length > int_max)
505 return false;
506
507 const double cutoff = std::min(1.0, ratio);
508 const double half_width = 0.5 * static_cast<double>(taps_per_phase) / cutoff;
509 const double reach = std::ceil(half_width) + 1.0;
510 if (!std::isfinite(half_width) || !std::isfinite(reach) || reach < 1.0 ||
511 reach > int_max)
512 return false;
513
514 // The kernel loop forms `i0 +/- reach` in signed-int arithmetic. Its
515 // largest possible centre is the final source sample; reserve one more
516 // value so the loop increment after the last iteration cannot overflow.
517 return reach <= int_max - static_cast<double>(source_length);
518 }
519
522 static constexpr double kResampKaiserBeta = 8.0;
523
529 static constexpr double kBlockCostSlackDefault = 1.5;
530 static constexpr double kBlockCostSlackMin = 1.2;
531 static constexpr double kBlockCostSlackMax = 2.0;
532
536 static constexpr double kSilenceEpsilon = 1e-24;
537
538
539};
540
541namespace detail {
542
543// ── Partition group: two length-L partitions covering IR [2L, 4L) ────────
544template <typename SampleType>
547 int block_len = 0; // L
548 int fft_size = 0; // K = 2L
549 int ir_start = 0; // d of the first partition = 2L
550 int num_partitions = 0; // 1 or 2
551 int slot = 0; // FDL slot the in-flight frame writes to
552
554
555 // Per audio channel: FDL of forward spectra, depth num_partitions.
556 std::vector<std::vector<std::complex<SampleType>>> spectra; // [ch*np + p][K]
557 // Per cell: IR partition spectra, accumulator, ping-pong output.
558 std::vector<std::vector<std::complex<SampleType>>> ir_spectra; // [cell*np + p][K]
559 std::vector<std::vector<std::complex<SampleType>>> accum; // [cell][K]
560 std::vector<std::vector<SampleType>> emit; // [cell][L]
561 std::vector<std::vector<SampleType>> ready; // [cell][L]
562
563 // Sliced-execution state for the period currently in flight.
564 long total_cost = 0;
565 long granted = 0;
566 std::size_t frame_base = 0; // ring index of the closed frame's first sample
567 int job = 0;
568 int job_step = 0;
569 int job_cursor = 0;
571 bool armed = false;
572 bool has_result = false;
573
574 void reset() {
575 for (auto& s : spectra) std::fill(s.begin(), s.end(), std::complex<SampleType>{});
576 for (auto& a : accum) std::fill(a.begin(), a.end(), std::complex<SampleType>{});
577 for (auto& e : emit) std::fill(e.begin(), e.end(), SampleType{0});
578 for (auto& r : ready) std::fill(r.begin(), r.end(), SampleType{0});
579 slot = 0;
580 granted = 0;
581 frame_base = 0;
582 job = 0;
583 job_step = 0;
584 job_cursor = 0;
585 fft.cancel();
586 armed = false;
587 has_result = false;
588 }
589};
590
591template <typename SampleType>
593 int src = 0;
594 int dst = 0;
595 int ir_channel = 0;
596 double l1 = 0.0;
597 std::vector<SampleType> head; // N0 direct-FIR taps; head[t] weighs x[n - t]
598 // head reversed (oldest-first) for pulp::simd::correlate over a linear
599 // window of the input history.
600 std::vector<SampleType> head_reversed;
601};
602
604 double z = 0.0;
605 double prev_x = 0.0;
606};
607
608} // namespace detail
609
610} // namespace pulp::signal
Resumable in-place radix-2 complex FFT (Cooley & Tukey 1965).
Definition zero_latency_convolver_support.hpp:181
void transform_real_padded(const double *src, int src_len, std::complex< SampleType > *dst) const
Blocking forward transform of a contiguous real buffer, zero-padded to size().
Definition zero_latency_convolver_support.hpp:386
long forward_cost() const
Metered cost of one complete forward transform (permuted gather plus all butterfly stages) and of one...
Definition zero_latency_convolver_support.hpp:224
static constexpr long kConjugateCost
Definition zero_latency_convolver_support.hpp:190
static constexpr long kButterflyCost
Definition zero_latency_convolver_support.hpp:189
static constexpr long kMacCost
Definition zero_latency_convolver_support.hpp:192
static constexpr long kScaleCost
Definition zero_latency_convolver_support.hpp:191
int log2_size() const
Definition zero_latency_convolver_support.hpp:218
int size() const
Definition zero_latency_convolver_support.hpp:217
static constexpr long kGatherCost
Definition zero_latency_convolver_support.hpp:188
long inverse_cost() const
Definition zero_latency_convolver_support.hpp:228
void prepare(int size)
Definition zero_latency_convolver_support.hpp:194
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
Definition additive_bank.hpp:218
@ peak
Sample peak. Literal, fastest-responding.
Definition feedforward_compressor.hpp:119
IrNormalizeMode
IR amplitude policy applied at load, after resampling and tail trim.
Definition zero_latency_convolver_support.hpp:157
@ energy
sum h^2 = 1 per output ear (default; convolution reverb).
Definition zero_latency_convolver_support.hpp:160
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
@ a
Definition formant_filter_bank.hpp:20
@ u
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ none
Definition dither.hpp:17
Hardware flush-to-zero RAII guard for the audio callback boundary.
Zero-latency non-uniform partitioned convolution engine.
Definition zero_latency_convolver_support.hpp:422
static constexpr double kWidthPercentMax
Definition zero_latency_convolver_support.hpp:465
static constexpr double kWidthPercentMin
Definition zero_latency_convolver_support.hpp:464
static constexpr double kResampKaiserBeta
House Kaiser window shape, fixed by the series contract's oversampling / resampling law — not a tunab...
Definition zero_latency_convolver_support.hpp:522
static constexpr double kPredelayMsMin
Definition zero_latency_convolver_support.hpp:441
static constexpr double kTailTrimDbMin
Definition zero_latency_convolver_support.hpp:470
static constexpr int kResampTapsPerPhaseMax
Definition zero_latency_convolver_support.hpp:485
static constexpr double kHighcutHzDefault
Definition zero_latency_convolver_support.hpp:458
static constexpr double kTailFadeMsDefault
Raised-cosine fade applied to the last part of the kept IR so the truncation cannot click.
Definition zero_latency_convolver_support.hpp:475
static constexpr double kDryPercentDefault
Definition zero_latency_convolver_support.hpp:446
static constexpr double kTailTrimDbDefault
IR truncation floor on the Schroeder backward-energy curve.
Definition zero_latency_convolver_support.hpp:469
static constexpr double kBlockCostSlackMax
Definition zero_latency_convolver_support.hpp:531
static constexpr double kLowcutHzMin
Definition zero_latency_convolver_support.hpp:456
static constexpr int kHeadLenMax
Definition zero_latency_convolver_support.hpp:431
static constexpr double kIrGainDbDefault
IR trim on the convolved path. [dp, default 0 dB, range -24..+24 dB].
Definition zero_latency_convolver_support.hpp:434
static constexpr double kTailTrimDbMax
Definition zero_latency_convolver_support.hpp:471
static constexpr double kHighcutHzMin
Definition zero_latency_convolver_support.hpp:459
static constexpr int kResampTapsPerPhaseMin
Definition zero_latency_convolver_support.hpp:484
static constexpr int kResampTapsPerPhaseDefault
Resampler kernel width, in taps per polyphase branch.
Definition zero_latency_convolver_support.hpp:483
static constexpr double kSilenceEpsilon
Below this, energies are treated as zero.
Definition zero_latency_convolver_support.hpp:536
static constexpr double kIrGainDbMin
Definition zero_latency_convolver_support.hpp:435
static constexpr double kBlockCostSlackDefault
Peak-to-mean per-block cost slack the distributed schedule is allowed.
Definition zero_latency_convolver_support.hpp:529
static constexpr double kPredelayMsMax
Definition zero_latency_convolver_support.hpp:442
static constexpr double kHighcutHzMax
Definition zero_latency_convolver_support.hpp:460
static constexpr double kLowcutHzMax
Definition zero_latency_convolver_support.hpp:457
static constexpr double kBlockCostSlackMin
Definition zero_latency_convolver_support.hpp:530
static constexpr double kTailFadeMsMin
Definition zero_latency_convolver_support.hpp:476
static constexpr double kLowcutHzDefault
Send-EQ corners.
Definition zero_latency_convolver_support.hpp:455
static bool valid_resample_geometry(int source_length, double source_rate, double destination_rate, int taps_per_phase) noexcept
Proves every resampler geometry value is finite and representable before the ingest path casts it to ...
Definition zero_latency_convolver_support.hpp:491
static constexpr double kWidthPercentDefault
Mid/side width of the wet output. [dp, default 100 %, range 0..200 %].
Definition zero_latency_convolver_support.hpp:463
static constexpr double kIrGainDbMax
Definition zero_latency_convolver_support.hpp:436
static constexpr int kHeadLenMin
Head length N0, in taps.
Definition zero_latency_convolver_support.hpp:430
static constexpr double kTailFadeMsMax
Definition zero_latency_convolver_support.hpp:477
static constexpr double kPredelayMsDefault
Pre-IR delay of the wet path.
Definition zero_latency_convolver_support.hpp:440
static constexpr double kWetPercentDefault
Wet / dry mix, in percent. [dp, defaults 100 % / 0 %, range 0..100 %].
Definition zero_latency_convolver_support.hpp:445
Cursor over one transform.
Definition zero_latency_convolver_support.hpp:241
std::size_t gather_mask
Definition zero_latency_convolver_support.hpp:250
void begin_forward_from_ring(const SampleType *ring, std::size_t base, std::size_t mask)
Forward transform of ring[(base + i) & mask], i in [0, size).
Definition zero_latency_convolver_support.hpp:255
long advance(const SlicedFftPlanT &plan, std::complex< SampleType > *data, long budget)
Definition zero_latency_convolver_support.hpp:287
void begin_inverse_in_place()
Inverse transform of whatever already sits in data, computed as conj(fft(conj(x)))/N.
Definition zero_latency_convolver_support.hpp:277
Phase
Definition zero_latency_convolver_support.hpp:242
@ scale
Definition zero_latency_convolver_support.hpp:242
@ permute
Definition zero_latency_convolver_support.hpp:242
@ gather
Definition zero_latency_convolver_support.hpp:242
@ butterflies
Definition zero_latency_convolver_support.hpp:242
@ conjugate
Definition zero_latency_convolver_support.hpp:242
@ idle
Definition zero_latency_convolver_support.hpp:242
const SampleType * gather_src
Definition zero_latency_convolver_support.hpp:248
bool inverse
Definition zero_latency_convolver_support.hpp:247
int stage
Definition zero_latency_convolver_support.hpp:246
int cursor
Definition zero_latency_convolver_support.hpp:245
Phase phase
Definition zero_latency_convolver_support.hpp:244
void cancel()
Definition zero_latency_convolver_support.hpp:285
bool done() const
Definition zero_latency_convolver_support.hpp:252
std::size_t gather_base
Definition zero_latency_convolver_support.hpp:249
void begin_forward_in_place()
Forward transform of the already gathered natural-order data in data, using the in-place permutation ...
Definition zero_latency_convolver_support.hpp:267
Definition zero_latency_convolver_support.hpp:592
int ir_channel
Definition zero_latency_convolver_support.hpp:595
std::vector< SampleType > head_reversed
Definition zero_latency_convolver_support.hpp:600
int dst
Definition zero_latency_convolver_support.hpp:594
int src
Definition zero_latency_convolver_support.hpp:593
double l1
Definition zero_latency_convolver_support.hpp:596
std::vector< SampleType > head
Definition zero_latency_convolver_support.hpp:597
Definition zero_latency_convolver_support.hpp:545
typename SlicedFftPlanT< SampleType >::Cursor FftCursor
Definition zero_latency_convolver_support.hpp:546
int ir_start
Definition zero_latency_convolver_support.hpp:549
bool has_result
Definition zero_latency_convolver_support.hpp:572
int job_cursor
Definition zero_latency_convolver_support.hpp:569
detail::SlicedFftPlanT< SampleType > plan
Definition zero_latency_convolver_support.hpp:553
int fft_size
Definition zero_latency_convolver_support.hpp:548
FftCursor fft
Definition zero_latency_convolver_support.hpp:570
void reset()
Definition zero_latency_convolver_support.hpp:574
int slot
Definition zero_latency_convolver_support.hpp:551
std::size_t frame_base
Definition zero_latency_convolver_support.hpp:566
std::vector< std::vector< std::complex< SampleType > > > accum
Definition zero_latency_convolver_support.hpp:559
int job
Definition zero_latency_convolver_support.hpp:567
std::vector< std::vector< SampleType > > emit
Definition zero_latency_convolver_support.hpp:560
long total_cost
Definition zero_latency_convolver_support.hpp:564
long granted
Definition zero_latency_convolver_support.hpp:565
int block_len
Definition zero_latency_convolver_support.hpp:547
std::vector< std::vector< std::complex< SampleType > > > ir_spectra
Definition zero_latency_convolver_support.hpp:558
bool armed
Definition zero_latency_convolver_support.hpp:571
std::vector< std::vector< std::complex< SampleType > > > spectra
Definition zero_latency_convolver_support.hpp:556
int num_partitions
Definition zero_latency_convolver_support.hpp:550
std::vector< std::vector< SampleType > > ready
Definition zero_latency_convolver_support.hpp:561
int job_step
Definition zero_latency_convolver_support.hpp:568
Definition zero_latency_convolver_support.hpp:603
double prev_x
Definition zero_latency_convolver_support.hpp:605
double z
Definition zero_latency_convolver_support.hpp:604
The real-unit vocabulary shared by every DSP block in the catalog.
Shared Kaiser-windowed-sinc FIR design helpers.