104template <
typename SampleType =
float>
114 x_z1_ = SampleType{0.0f};
115 y_z1_ = SampleType{0.0f};
120 const SampleType y = a_ * (x - y_z1_) + x_z1_;
127 SampleType a_ = SampleType{0.0f};
128 SampleType x_z1_ = SampleType{0.0f};
129 SampleType y_z1_ = SampleType{0.0f};
153 [[nodiscard]]
bool valid() const noexcept {
157 return (
count + 1) / 2;
162 [[nodiscard]]
double path_a(std::size_t
i)
const noexcept {
165 [[nodiscard]]
double path_b(std::size_t
i)
const noexcept {
186 double passband_edge)
noexcept {
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;
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;
200 const double q =
e * (1.0 + e4 * (2.0 + e4 * (15.0 + e4 * 150.0)));
202 for (std::size_t
i = 1;
i <= coefficient_count; ++
i) {
203 const double angle = std::numbers::pi *
static_cast<double>(
i) / order;
205 double numerator = 0.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);
210 if (std::abs(term) < 1
e-30)
214 double denominator = 0.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);
219 if (std::abs(term) < 1
e-30)
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);
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;
240template <
typename Coefficient>
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));
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);
262template <
typename Coefficient =
double>
264 double frequency)
noexcept {
265 std::complex<double>
a, b;
266 double ta = 0.0, tb = 0.0;
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);
275template <
typename Coefficient =
double>
277 double frequency)
noexcept {
278 return 20.0 * std::log10(std::max(
286template <
typename Coefficient =
double>
288 std::complex<double>
a, b;
289 double ta = 0.0, tb = 0.0;
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));
300template <
typename Coefficient =
double>
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;
320 0.04536216434896102f,
327 0.16808748123450207f,
368 using State = std::conditional_t<(
sizeof(SampleType) <
sizeof(double)),
double, SampleType>;
390 return (count_ + 1) / 2;
402 void upsample(
const SampleType* input, SampleType* output, std::size_t n)
noexcept {
404 std::fill_n(output, 2 * n, SampleType{0});
407 dispatch(count_, [&](
auto na,
auto nb) {
408 upsample_kernel<decltype(na)::value, decltype(nb)::value>(input, output, n);
413 void downsample(
const SampleType* input, SampleType* output, std::size_t n)
noexcept {
415 std::fill_n(output, n, SampleType{0});
418 dispatch(count_, [&](
auto na,
auto nb) {
419 downsample_kernel<decltype(na)::value, decltype(nb)::value>(input, output, n);
425 std::array<State, kMaxPath> x{};
426 std::array<State, kMaxPath> y{};
428 struct DirectionState {
433 std::array<State, kMaxPath> a_{};
434 std::array<State, kMaxPath> b_{};
436 DirectionState down_;
437 std::size_t count_ = 0;
443 static constexpr State kFlushThreshold =
444 static_cast<State>(std::numeric_limits<SampleType>::min()) *
State{1e8};
447 return std::abs(v) < kFlushThreshold ?
State{0} :
v;
450 template <std::
size_t N>
using Count = std::integral_constant<std::size_t, N>;
452 template <
typename Fn>
static void dispatch(std::size_t count, Fn&& fn)
noexcept {
455 fn(Count<1>{}, Count<0>{});
458 fn(Count<1>{}, Count<1>{});
461 fn(Count<2>{}, Count<1>{});
464 fn(Count<2>{}, Count<2>{});
467 fn(Count<3>{}, Count<2>{});
470 fn(Count<3>{}, Count<3>{});
473 fn(Count<4>{}, Count<3>{});
476 fn(Count<4>{}, Count<4>{});
479 fn(Count<5>{}, Count<4>{});
482 fn(Count<5>{}, Count<5>{});
485 fn(Count<6>{}, Count<5>{});
488 fn(Count<6>{}, Count<6>{});
491 fn(Count<7>{}, Count<6>{});
494 fn(Count<7>{}, Count<7>{});
497 fn(Count<8>{}, Count<7>{});
500 fn(Count<8>{}, Count<8>{});
507 template <std::
size_t N>
509 for (std::size_t k = 0;
k < N; ++
k) {
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) {
526 for (std::size_t k = 0;
k < NB; ++
k) {
530 for (std::size_t
i = 0;
i <
n; ++
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));
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]);
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]);
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];
553 for (std::size_t k = 0;
k < NB; ++
k) {
554 xb[
k] = down_.b.x[
k];
555 yb[
k] = down_.b.y[
k];
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));
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]);
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]);
586template <
typename Coefficient>
588 std::span<const Coefficient> b)
noexcept {
590 const std::size_t count =
a.size() + b.size();
592 a.size() > b.size() + 1)
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]);
634 std::span<const SampleType> coeffs_b)
noexcept {
640 return stage_.sections_a();
644 return stage_.sections_b();
647 return stage_.configured();
655 void process(SampleType x, SampleType& out_lo, SampleType& out_hi)
noexcept {
657 stage_.upsample(&x, out, 1);
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);
699 std::span<const SampleType> coeffs_b)
noexcept {
704 return stage_.sections_a();
707 return stage_.sections_b();
710 return stage_.configured();
719 SampleType
process(SampleType in_lo, SampleType in_hi)
noexcept {
720 const SampleType in[2] = {in_lo, in_hi};
722 stage_.downsample(in, &out, 1);
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);
One single-coefficient allpass section run at the sub-rate.
Definition halfband_iir.hpp:105
HalfBandAllpassSectionT()=default
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