Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
halfband_iir.hpp
Go to the documentation of this file.
1#pragma once
2
67
68#include <algorithm>
69#include <array>
70#include <cmath>
71#include <complex>
72#include <cstddef>
73#include <limits>
74#include <numbers>
75#include <span>
76#include <type_traits>
77
78namespace pulp::signal {
79
104template <typename SampleType = float>
106public:
108 explicit HalfBandAllpassSectionT(SampleType a) : a_(a) {}
109
110 void set_coefficient(SampleType a) { a_ = a; }
111 SampleType coefficient() const { return a_; }
112
113 void reset() {
114 x_z1_ = SampleType{0.0f};
115 y_z1_ = SampleType{0.0f};
116 }
117
119 SampleType process(SampleType x) {
120 const SampleType y = a_ * (x - y_z1_) + x_z1_;
121 x_z1_ = x;
122 y_z1_ = y;
123 return y;
124 }
125
126private:
127 SampleType a_ = SampleType{0.0f};
128 SampleType x_z1_ = SampleType{0.0f};
129 SampleType y_z1_ = SampleType{0.0f};
130};
131
134
138inline constexpr std::size_t kHalfBandIirMaxCoefficients = 16;
139
142inline constexpr double kHalfBandIirMinPassbandEdge = 1e-4;
143
149 std::array<double, kHalfBandIirMaxCoefficients> coefficients{};
150 std::size_t count = 0;
151 double passband_edge = 0.0;
152
153 [[nodiscard]] bool valid() const noexcept {
154 return count > 0;
155 }
156 [[nodiscard]] std::size_t path_a_count() const noexcept {
157 return (count + 1) / 2;
158 }
159 [[nodiscard]] std::size_t path_b_count() const noexcept {
160 return count / 2;
161 }
162 [[nodiscard]] double path_a(std::size_t i) const noexcept {
163 return coefficients[2 * i];
164 }
165 [[nodiscard]] double path_b(std::size_t i) const noexcept {
166 return coefficients[2 * i + 1];
167 }
168 [[nodiscard]] double stopband_edge() const noexcept {
169 return 0.5 - passband_edge;
170 }
171};
172
185inline HalfBandIirDesign design_halfband_iir(std::size_t coefficient_count,
186 double passband_edge) noexcept {
188 if (coefficient_count == 0 || coefficient_count > kHalfBandIirMaxCoefficients ||
189 !(passband_edge >= kHalfBandIirMinPassbandEdge && passband_edge < 0.25))
190 return design;
191
192 const double order = static_cast<double>(2 * coefficient_count + 1);
193 const double t = std::tan(std::numbers::pi * passband_edge);
194 const double k = t * t; // selectivity: passband over stopband bilinear edge
195 const double kc_root = std::sqrt(std::sqrt(1.0 - k * k));
196 const double e = 0.5 * (1.0 - kc_root) / (1.0 + kc_root);
197 const double e4 = e * e * e * e;
198 // Nome of the complementary modulus; the series converges fast because
199 // e < 0.5 for every passband edge below a quarter of the rate.
200 const double q = e * (1.0 + e4 * (2.0 + e4 * (15.0 + e4 * 150.0)));
201
202 for (std::size_t i = 1; i <= coefficient_count; ++i) {
203 const double angle = std::numbers::pi * static_cast<double>(i) / order;
204 // sqrt(k) * sn(2 i K / order) as a ratio of theta series.
205 double numerator = 0.0;
206 double sign = 1.0;
207 for (int m = 0; m < 32; ++m) {
208 const double term = sign * std::pow(q, m * (m + 1)) * std::sin((2 * m + 1) * angle);
209 numerator += term;
210 if (std::abs(term) < 1e-30)
211 break;
212 sign = -sign;
213 }
214 double denominator = 0.0;
215 sign = -1.0;
216 for (int m = 1; m < 32; ++m) {
217 const double term = sign * std::pow(q, m * m) * std::cos(2.0 * m * angle);
218 denominator += term;
219 if (std::abs(term) < 1e-30)
220 break;
221 sign = -sign;
222 }
223 const double w = 2.0 * std::pow(q, 0.25) * numerator / (1.0 + 2.0 * denominator);
224 const double w2 = w * w;
225 const double x = std::sqrt((1.0 - w2 * k) * (1.0 - w2 / k)) / (1.0 + w2);
226 design.coefficients[i - 1] = (1.0 - x) / (1.0 + x);
227 }
228 std::sort(design.coefficients.begin(),
229 design.coefficients.begin() + static_cast<std::ptrdiff_t>(coefficient_count));
230 design.count = coefficient_count;
231 design.passband_edge = passband_edge;
232 return design;
233}
234
235namespace detail {
236
237// Phase factor and DC-referenced group delay of one path at `frequency`
238// (cycles per sample at the 2x rate). Each section (a + z^-2)/(1 + a z^-2)
239// has unit magnitude and group delay 2(1 - a^2)/(1 + 2a cos 2w + a^2).
240template <typename Coefficient>
241inline void halfband_path_response(const HalfBandIirDesign& design, bool path_a, double frequency,
242 std::complex<double>& response, double& group_delay) noexcept {
243 const double w = 2.0 * std::numbers::pi * frequency;
244 const std::complex<double> z2(std::cos(2.0 * w), -std::sin(2.0 * w)); // z^-2
245 response = 1.0;
246 group_delay = 0.0;
247 const std::size_t n = path_a ? design.path_a_count() : design.path_b_count();
248 for (std::size_t i = 0; i < n; ++i) {
249 const double a = static_cast<double>(
250 static_cast<Coefficient>(path_a ? design.path_a(i) : design.path_b(i)));
251 response *= (a + z2) / (1.0 + a * z2);
252 group_delay += 2.0 * (1.0 - a * a) / (1.0 + 2.0 * a * std::cos(2.0 * w) + a * a);
253 }
254}
255
256} // namespace detail
257
262template <typename Coefficient = double>
263inline std::complex<double> halfband_iir_response(const HalfBandIirDesign& design,
264 double frequency) noexcept {
265 std::complex<double> a, b;
266 double ta = 0.0, tb = 0.0;
268 detail::halfband_path_response<Coefficient>(design, false, frequency, b, tb);
269 const double w = 2.0 * std::numbers::pi * frequency;
270 return 0.5 * (a + std::complex<double>(std::cos(w), -std::sin(w)) * b);
271}
272
275template <typename Coefficient = double>
277 double frequency) noexcept {
278 return 20.0 * std::log10(std::max(
279 std::abs(halfband_iir_response<Coefficient>(design, frequency)), 1e-300));
280}
281
286template <typename Coefficient = double>
287inline double halfband_iir_group_delay(const HalfBandIirDesign& design, double frequency) noexcept {
288 std::complex<double> a, b;
289 double ta = 0.0, tb = 0.0;
291 detail::halfband_path_response<Coefficient>(design, false, frequency, b, tb);
292 const double w = 2.0 * std::numbers::pi * frequency;
293 const std::complex<double> zb = std::complex<double>(std::cos(w), -std::sin(w)) * b;
294 return std::real((ta * a + (tb + 1.0) * zb) / (a + zb));
295}
296
300template <typename Coefficient = double>
301inline double halfband_iir_stopband_db(const HalfBandIirDesign& design) noexcept {
302 if (!design.valid())
303 return 0.0;
304 constexpr int kPoints = 4096;
305 const double lo = design.stopband_edge();
306 double worst = -1e300;
307 for (int i = 0; i <= kPoints; ++i) {
308 const double f = lo + (0.5 - lo) * static_cast<double>(i) / kPoints;
309 worst = std::max(worst, halfband_iir_magnitude_db<Coefficient>(design, f));
310 }
311 return worst;
312}
313
319inline constexpr std::array<float, 3> kDefaultCoefficientsA = {
320 0.04536216434896102f,
321 0.3371496879790739f,
322 0.7080641363635385f,
323};
324
326inline constexpr std::array<float, 3> kDefaultCoefficientsB = {
327 0.16808748123450207f,
328 0.5223778543083539f,
329 0.8974455911727747f,
330};
331
345//
346// Fed silence, an allpass recurrence does not decay to zero in floating
347// point: once its state reaches the subnormal range, rounding sustains a
348// limit cycle there indefinitely, which is both a stream of nonzero output
349// and, without flush-to-zero, a large per-sample cost. State whose magnitude
350// is below kFlushThreshold is therefore set to zero when it is stored at the
351// end of each block. Doing it there rather than per sample keeps the
352// recurrence free of compares; a block-sized delay before the flush bites is
353// harmless because anything below the threshold is ~600 dB under full scale.
354template <typename SampleType = float> class HalfBandIirStageT {
355 public:
368 using State = std::conditional_t<(sizeof(SampleType) < sizeof(double)), double, SampleType>;
369
370 static constexpr std::size_t kMaxPath = kHalfBandIirMaxCoefficients / 2;
371
372 void configure(const HalfBandIirDesign& design) noexcept {
373 count_ = design.count;
374 for (std::size_t i = 0; i < kMaxPath; ++i) {
375 a_[i] = i < design.path_a_count() ? static_cast<State>(design.path_a(i)) : State{0};
376 b_[i] = i < design.path_b_count() ? static_cast<State>(design.path_b(i)) : State{0};
377 }
378 reset();
379 }
380
381 void reset() noexcept {
382 up_ = {};
383 down_ = {};
384 }
385
386 std::size_t coefficient_count() const noexcept {
387 return count_;
388 }
389 std::size_t sections_a() const noexcept {
390 return (count_ + 1) / 2;
391 }
392 std::size_t sections_b() const noexcept {
393 return count_ / 2;
394 }
395
396 bool configured() const noexcept {
397 return count_ > 0;
398 }
399
402 void upsample(const SampleType* input, SampleType* output, std::size_t n) noexcept {
403 if (count_ == 0) { // unconfigured or invalid design: silence, never garbage
404 std::fill_n(output, 2 * n, SampleType{0});
405 return;
406 }
407 dispatch(count_, [&](auto na, auto nb) {
408 upsample_kernel<decltype(na)::value, decltype(nb)::value>(input, output, n);
409 });
410 }
411
413 void downsample(const SampleType* input, SampleType* output, std::size_t n) noexcept {
414 if (count_ == 0) {
415 std::fill_n(output, n, SampleType{0});
416 return;
417 }
418 dispatch(count_, [&](auto na, auto nb) {
419 downsample_kernel<decltype(na)::value, decltype(nb)::value>(input, output, n);
420 });
421 }
422
423 private:
424 struct PathState {
425 std::array<State, kMaxPath> x{};
426 std::array<State, kMaxPath> y{};
427 };
428 struct DirectionState {
429 PathState a;
430 PathState b;
431 };
432
433 std::array<State, kMaxPath> a_{};
434 std::array<State, kMaxPath> b_{};
435 DirectionState up_;
436 DirectionState down_;
437 std::size_t count_ = 0;
438
439 // 1e8 smallest normals: about 1e-30 in float, 2e-300 in double. Far below
440 // any audible or measurable signal, far above where the limit cycle lives.
441 // Keyed to the sample type, not the state type: state below it would only
442 // ever reach the output as subnormal samples.
443 static constexpr State kFlushThreshold =
444 static_cast<State>(std::numeric_limits<SampleType>::min()) * State{1e8};
445
446 static State flush(State v) noexcept {
447 return std::abs(v) < kFlushThreshold ? State{0} : v;
448 }
449
450 template <std::size_t N> using Count = std::integral_constant<std::size_t, N>;
451
452 template <typename Fn> static void dispatch(std::size_t count, Fn&& fn) noexcept {
453 switch (count) {
454 case 1:
455 fn(Count<1>{}, Count<0>{});
456 break;
457 case 2:
458 fn(Count<1>{}, Count<1>{});
459 break;
460 case 3:
461 fn(Count<2>{}, Count<1>{});
462 break;
463 case 4:
464 fn(Count<2>{}, Count<2>{});
465 break;
466 case 5:
467 fn(Count<3>{}, Count<2>{});
468 break;
469 case 6:
470 fn(Count<3>{}, Count<3>{});
471 break;
472 case 7:
473 fn(Count<4>{}, Count<3>{});
474 break;
475 case 8:
476 fn(Count<4>{}, Count<4>{});
477 break;
478 case 9:
479 fn(Count<5>{}, Count<4>{});
480 break;
481 case 10:
482 fn(Count<5>{}, Count<5>{});
483 break;
484 case 11:
485 fn(Count<6>{}, Count<5>{});
486 break;
487 case 12:
488 fn(Count<6>{}, Count<6>{});
489 break;
490 case 13:
491 fn(Count<7>{}, Count<6>{});
492 break;
493 case 14:
494 fn(Count<7>{}, Count<7>{});
495 break;
496 case 15:
497 fn(Count<8>{}, Count<7>{});
498 break;
499 case 16:
500 fn(Count<8>{}, Count<8>{});
501 break;
502 default:
503 break;
504 }
505 }
506
507 template <std::size_t N>
508 static State run(const std::array<State, kMaxPath>& c, State* x, State* y, State v) noexcept {
509 for (std::size_t k = 0; k < N; ++k) {
510 const State out = c[k] * v + x[k] - c[k] * y[k];
511 x[k] = v;
512 y[k] = out;
513 v = out;
514 }
515 return v;
516 }
517
518 template <std::size_t NA, std::size_t NB>
519 void upsample_kernel(const SampleType* input, SampleType* output, std::size_t n) noexcept {
520 State xa[NA > 0 ? NA : 1], ya[NA > 0 ? NA : 1];
521 State xb[NB > 0 ? NB : 1], yb[NB > 0 ? NB : 1];
522 for (std::size_t k = 0; k < NA; ++k) {
523 xa[k] = up_.a.x[k];
524 ya[k] = up_.a.y[k];
525 }
526 for (std::size_t k = 0; k < NB; ++k) {
527 xb[k] = up_.b.x[k];
528 yb[k] = up_.b.y[k];
529 }
530 for (std::size_t i = 0; i < n; ++i) {
531 const State v = static_cast<State>(input[i]);
532 output[2 * i] = static_cast<SampleType>(run<NA>(a_, xa, ya, v));
533 output[2 * i + 1] = static_cast<SampleType>(run<NB>(b_, xb, yb, v));
534 }
535 for (std::size_t k = 0; k < NA; ++k) {
536 up_.a.x[k] = flush(xa[k]);
537 up_.a.y[k] = flush(ya[k]);
538 }
539 for (std::size_t k = 0; k < NB; ++k) {
540 up_.b.x[k] = flush(xb[k]);
541 up_.b.y[k] = flush(yb[k]);
542 }
543 }
544
545 template <std::size_t NA, std::size_t NB>
546 void downsample_kernel(const SampleType* input, SampleType* output, std::size_t n) noexcept {
547 State xa[NA > 0 ? NA : 1], ya[NA > 0 ? NA : 1];
548 State xb[NB > 0 ? NB : 1], yb[NB > 0 ? NB : 1];
549 for (std::size_t k = 0; k < NA; ++k) {
550 xa[k] = down_.a.x[k];
551 ya[k] = down_.a.y[k];
552 }
553 for (std::size_t k = 0; k < NB; ++k) {
554 xb[k] = down_.b.x[k];
555 yb[k] = down_.b.y[k];
556 }
557 for (std::size_t i = 0; i < n; ++i) {
558 const State even = static_cast<State>(input[2 * i]);
559 const State odd = static_cast<State>(input[2 * i + 1]);
560 const State pa = run<NA>(a_, xa, ya, odd);
561 const State pb = run<NB>(b_, xb, yb, even);
562 output[i] = static_cast<SampleType>(State{0.5} * (pa + pb));
563 }
564 for (std::size_t k = 0; k < NA; ++k) {
565 down_.a.x[k] = flush(xa[k]);
566 down_.a.y[k] = flush(ya[k]);
567 }
568 for (std::size_t k = 0; k < NB; ++k) {
569 down_.b.x[k] = flush(xb[k]);
570 down_.b.y[k] = flush(yb[k]);
571 }
572 }
573};
574
577
578namespace detail {
579
580// A design from caller-supplied per-path coefficients. Every half-band
581// design gives path A the same number of sections as path B or one more
582// (`design_halfband_iir` alternates ascending coefficients A, B, A, ...), at
583// most kHalfBandIirMaxCoefficients / 2 each; any other shape returns an empty
584// design, which leaves a stage silent rather than filtering with a guess.
585// The passband edge of hand-picked coefficients is unknown, so it is 0.
586template <typename Coefficient>
587inline HalfBandIirDesign halfband_design_from_paths(std::span<const Coefficient> a,
588 std::span<const Coefficient> b) noexcept {
590 const std::size_t count = a.size() + b.size();
591 if (count == 0 || count > kHalfBandIirMaxCoefficients || a.size() < b.size() ||
592 a.size() > b.size() + 1)
593 return design;
594 for (std::size_t i = 0; i < a.size(); ++i)
595 design.coefficients[2 * i] = static_cast<double>(a[i]);
596 for (std::size_t i = 0; i < b.size(); ++i)
597 design.coefficients[2 * i + 1] = static_cast<double>(b[i]);
598 design.count = count;
599 return design;
600}
601
603 return halfband_design_from_paths(std::span<const float>(kDefaultCoefficientsA),
604 std::span<const float>(kDefaultCoefficientsB));
605}
606
607} // namespace detail
608
627template <typename SampleType = float> class HalfBandUpsampler2xT {
628 public:
630 stage_.configure(detail::default_halfband_design());
631 }
632
633 HalfBandUpsampler2xT(std::span<const SampleType> coeffs_a,
634 std::span<const SampleType> coeffs_b) noexcept {
635 stage_.configure(detail::halfband_design_from_paths(coeffs_a, coeffs_b));
636 }
637
639 std::size_t sections_a() const noexcept {
640 return stage_.sections_a();
641 }
642
643 std::size_t sections_b() const noexcept {
644 return stage_.sections_b();
645 }
646 bool configured() const noexcept {
647 return stage_.configured();
648 }
649
650 void reset() noexcept {
651 stage_.reset();
652 }
653
655 void process(SampleType x, SampleType& out_lo, SampleType& out_hi) noexcept {
656 SampleType out[2];
657 stage_.upsample(&x, out, 1);
658 out_lo = out[0];
659 out_hi = out[1];
660 }
661
664 void process_block(std::span<const SampleType> input, std::span<SampleType> output) noexcept {
665 const std::size_t n = std::min(input.size(), output.size() / 2);
666 stage_.upsample(input.data(), output.data(), n);
667 }
668
669 private:
671};
672
675
692template <typename SampleType = float> class HalfBandDownsampler2xT {
693 public:
695 stage_.configure(detail::default_halfband_design());
696 }
697
698 HalfBandDownsampler2xT(std::span<const SampleType> coeffs_a,
699 std::span<const SampleType> coeffs_b) noexcept {
700 stage_.configure(detail::halfband_design_from_paths(coeffs_a, coeffs_b));
701 }
702
703 std::size_t sections_a() const noexcept {
704 return stage_.sections_a();
705 }
706 std::size_t sections_b() const noexcept {
707 return stage_.sections_b();
708 }
709 bool configured() const noexcept {
710 return stage_.configured();
711 }
712
713 void reset() noexcept {
714 stage_.reset();
715 }
716
719 SampleType process(SampleType in_lo, SampleType in_hi) noexcept {
720 const SampleType in[2] = {in_lo, in_hi};
721 SampleType out;
722 stage_.downsample(in, &out, 1);
723 return out;
724 }
725
728 void process_block(std::span<const SampleType> input, std::span<SampleType> output) noexcept {
729 const std::size_t n = std::min(output.size(), input.size() / 2);
730 stage_.downsample(input.data(), output.data(), n);
731 }
732
733 private:
735};
736
739
740} // namespace pulp::signal
One single-coefficient allpass section run at the sub-rate.
Definition halfband_iir.hpp:105
void reset()
Definition halfband_iir.hpp:113
SampleType coefficient() const
Definition halfband_iir.hpp:111
HalfBandAllpassSectionT(SampleType a)
Definition halfband_iir.hpp:108
void set_coefficient(SampleType a)
Definition halfband_iir.hpp:110
SampleType process(SampleType x)
Process one sample. Returns y[n].
Definition halfband_iir.hpp:119
2x half-band downsampler.
Definition halfband_iir.hpp:692
HalfBandDownsampler2xT(std::span< const SampleType > coeffs_a, std::span< const SampleType > coeffs_b) noexcept
Definition halfband_iir.hpp:698
std::size_t sections_a() const noexcept
Definition halfband_iir.hpp:703
void process_block(std::span< const SampleType > input, std::span< SampleType > output) noexcept
Block helper.
Definition halfband_iir.hpp:728
std::size_t sections_b() const noexcept
Definition halfband_iir.hpp:706
HalfBandDownsampler2xT() noexcept
Definition halfband_iir.hpp:694
void reset() noexcept
Definition halfband_iir.hpp:713
SampleType process(SampleType in_lo, SampleType in_hi) noexcept
Consume two input samples (lo = even phase, hi = odd phase) and return one decimated output sample.
Definition halfband_iir.hpp:719
bool configured() const noexcept
Definition halfband_iir.hpp:709
Fixed-storage polyphase half-band stage that filters whole blocks: the engine behind HalfBandUpsample...
Definition halfband_iir.hpp:354
std::conditional_t<(sizeof(SampleType)< sizeof(double)), double, SampleType > State
Coefficients and recurrence state are kept in double even for a float stage.
Definition halfband_iir.hpp:368
void configure(const HalfBandIirDesign &design) noexcept
Definition halfband_iir.hpp:372
std::size_t sections_b() const noexcept
Definition halfband_iir.hpp:392
void upsample(const SampleType *input, SampleType *output, std::size_t n) noexcept
n input samples -> 2n output samples.
Definition halfband_iir.hpp:402
std::size_t sections_a() const noexcept
Definition halfband_iir.hpp:389
static constexpr std::size_t kMaxPath
Definition halfband_iir.hpp:370
bool configured() const noexcept
True when a valid design is loaded; an unconfigured stage emits silence.
Definition halfband_iir.hpp:396
void downsample(const SampleType *input, SampleType *output, std::size_t n) noexcept
2n input samples -> n output samples. output may alias input.
Definition halfband_iir.hpp:413
void reset() noexcept
Definition halfband_iir.hpp:381
std::size_t coefficient_count() const noexcept
Definition halfband_iir.hpp:386
2x half-band upsampler.
Definition halfband_iir.hpp:627
std::size_t sections_b() const noexcept
Number of allpass sections in path B.
Definition halfband_iir.hpp:643
std::size_t sections_a() const noexcept
Number of allpass sections in path A.
Definition halfband_iir.hpp:639
void process(SampleType x, SampleType &out_lo, SampleType &out_hi) noexcept
Produce two output samples for one input sample.
Definition halfband_iir.hpp:655
bool configured() const noexcept
Definition halfband_iir.hpp:646
void process_block(std::span< const SampleType > input, std::span< SampleType > output) noexcept
Block helper.
Definition halfband_iir.hpp:664
HalfBandUpsampler2xT() noexcept
Definition halfband_iir.hpp:629
HalfBandUpsampler2xT(std::span< const SampleType > coeffs_a, std::span< const SampleType > coeffs_b) noexcept
Definition halfband_iir.hpp:633
void reset() noexcept
Definition halfband_iir.hpp:650
Definition design_adherence.hpp:32
Round-2 effect-wave vocabulary.
Definition analysis_frontends.hpp:50
HalfBandIirDesign halfband_design_from_paths(std::span< const Coefficient > a, std::span< const Coefficient > b) noexcept
Definition halfband_iir.hpp:587
void halfband_path_response(const HalfBandIirDesign &design, bool path_a, double frequency, std::complex< double > &response, double &group_delay) noexcept
Definition halfband_iir.hpp:241
HalfBandIirDesign default_halfband_design() noexcept
Definition halfband_iir.hpp:602
Definition additive_bank.hpp:218
HalfBandIirStageT< float > HalfBandIirStage
Definition halfband_iir.hpp:575
double halfband_iir_group_delay(const HalfBandIirDesign &design, double frequency) noexcept
Group delay of H(z) in samples of the filter's own rate.
Definition halfband_iir.hpp:287
double halfband_iir_magnitude_db(const HalfBandIirDesign &design, double frequency) noexcept
Magnitude of design in dB at frequency (cycles per sample of the filter's own rate).
Definition halfband_iir.hpp:276
@ c
Definition spectrum_trace.hpp:35
HalfBandDownsampler2xT< float > HalfBandDownsampler2x
Definition halfband_iir.hpp:737
HalfBandUpsampler2xT< float > HalfBandUpsampler2x
Definition halfband_iir.hpp:673
HalfBandIirDesign design_halfband_iir(std::size_t coefficient_count, double passband_edge) noexcept
Closed-form elliptic half-band design (see the file comment).
Definition halfband_iir.hpp:185
std::complex< double > halfband_iir_response(const HalfBandIirDesign &design, double frequency) noexcept
Complex response of design at frequency cycles per sample of the filter's own rate: 1/2 (A(z^2) + z^-...
Definition halfband_iir.hpp:263
HalfBandIirStageT< double > HalfBandIirStage64
Definition halfband_iir.hpp:576
HalfBandDownsampler2xT< double > HalfBandDownsampler2x64
Definition halfband_iir.hpp:738
constexpr std::array< float, 3 > kDefaultCoefficientsB
Default half-band design, path B. See kDefaultCoefficientsA.
Definition halfband_iir.hpp:326
HalfBandAllpassSectionT< float > HalfBandAllpassSection
Definition halfband_iir.hpp:132
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
constexpr double kHalfBandIirMinPassbandEdge
Smallest passband edge design_halfband_iir() accepts, in cycles per sample at the filter's own rate.
Definition halfband_iir.hpp:142
constexpr std::size_t kHalfBandIirMaxCoefficients
Largest coefficient count a HalfBandIirDesign carries (eight sections per path).
Definition halfband_iir.hpp:138
HalfBandAllpassSectionT< double > HalfBandAllpassSection64
Definition halfband_iir.hpp:133
constexpr std::array< float, 3 > kDefaultCoefficientsA
Default half-band design: design_halfband_iir(6, 0.21), path A.
Definition halfband_iir.hpp:319
double halfband_iir_stopband_db(const HalfBandIirDesign &design) noexcept
Worst-case (largest) stopband magnitude in dB over [stopband_edge(), 0.5], sampled densely enough to ...
Definition halfband_iir.hpp:301
HalfBandUpsampler2xT< double > HalfBandUpsampler2x64
Definition halfband_iir.hpp:674
@ y
Definition input_events.hpp:42
@ n
Definition input_events.hpp:40
@ k
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
@ v
Definition input_events.hpp:41
Coefficients of one elliptic polyphase half-band filter, ascending.
Definition halfband_iir.hpp:148
double path_b(std::size_t i) const noexcept
Definition halfband_iir.hpp:165
std::size_t count
Definition halfband_iir.hpp:150
std::array< double, kHalfBandIirMaxCoefficients > coefficients
Definition halfband_iir.hpp:149
double passband_edge
Definition halfband_iir.hpp:151
std::size_t path_b_count() const noexcept
Definition halfband_iir.hpp:159
double stopband_edge() const noexcept
Definition halfband_iir.hpp:168
bool valid() const noexcept
Definition halfband_iir.hpp:153
std::size_t path_a_count() const noexcept
Definition halfband_iir.hpp:156
double path_a(std::size_t i) const noexcept
Definition halfband_iir.hpp:162