Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
sinc_resampler.hpp
Go to the documentation of this file.
1#pragma once
2
27
29
30#include <algorithm>
31#include <cmath>
32#include <cstddef>
33#include <vector>
34
35namespace pulp::signal {
36
37template <typename SampleType = float> class SincResamplerT {
38 public:
39 static bool checked_retained_bytes(int half_width, int phases,
40 std::uint64_t target_max_bytes,
41 std::uint64_t& bytes) noexcept {
42 std::uint64_t elements = 0;
43 if (!checked_capacity_product(static_cast<std::uint64_t>(std::max(1, phases) + 1),
44 static_cast<std::uint64_t>(2 * std::max(1, half_width)),
45 UINT64_MAX, elements))
46 return false;
47 CheckedRetainedByteCharge charge(target_max_bytes);
48 if (!charge.add<SampleType>(elements)) return false;
49 bytes = charge.total();
50 return true;
51 }
52
59 void build(int half_width = 16, int phases = 512, double beta = 9.0, double cutoff = 1.0) {
60 begin_build(half_width, phases, beta, cutoff);
61 while (!build_step()) {
62 }
63 }
64
67 void begin_build(int half_width = 16, int phases = 512, double beta = 9.0,
68 double cutoff = 1.0) {
69 half_ = std::max(1, half_width);
70 phases_ = std::max(1, phases);
71 cutoff_ = std::isfinite(cutoff) ? std::clamp(cutoff, 1.0e-6, 1.0) : 1.0;
72 const int taps = 2 * half_;
73 // table_[phase][tap]; phase in [0, phases_] inclusive (the extra row
74 // lets read() linearly interpolate up to phase == phases_).
75 table_.assign(static_cast<size_t>(phases_ + 1) * taps, SampleType{0.0f});
76 build_beta_ = beta;
77 build_i0_beta_ = bessel_i0(beta);
78 next_build_phase_ = 0;
79 }
80
81 bool build_step() {
82 if (next_build_phase_ > phases_)
83 return true;
84 const int ph = next_build_phase_;
85 const int taps = 2 * half_;
86 const double frac = static_cast<double>(ph) / phases_;
87 double sum = 0.0;
88 for (int t = 0; t < taps; ++t) {
89 const double x = static_cast<double>(t - half_ + 1) - frac;
90 const double s = cutoff_ * sinc(cutoff_ * x);
91 const double wn = x / half_;
92 const double w =
93 (wn > -1.0 && wn < 1.0)
94 ? bessel_i0(build_beta_ * std::sqrt(1.0 - wn * wn)) / build_i0_beta_
95 : 0.0;
96 table_[static_cast<size_t>(ph) * taps + t] = static_cast<SampleType>(s * w);
97 sum += s * w;
98 }
99 if (std::abs(sum) > 1.0e-12)
100 for (int t = 0; t < taps; ++t)
101 table_[static_cast<size_t>(ph) * taps + t] /= static_cast<SampleType>(sum);
102 ++next_build_phase_;
103 return next_build_phase_ > phases_;
104 }
105
106 int half_width() const {
107 return half_;
108 }
109 int taps() const {
110 return 2 * half_;
111 }
112 bool ready() const {
113 return !table_.empty() && next_build_phase_ > phases_;
114 }
115 double cutoff() const {
116 return cutoff_;
117 }
118 std::size_t prepared_bytes() const noexcept {
119 return table_.capacity() * sizeof(SampleType);
120 }
121
127 SampleType apply(const SampleType* samples, double frac) const {
128 const int taps = 2 * half_;
129 const double ph = frac * phases_;
130 const int p0 = static_cast<int>(ph);
131 const SampleType a = static_cast<SampleType>(ph - p0);
132 const SampleType* row0 = table_.data() + static_cast<size_t>(p0) * taps;
133 const SampleType* row1 = table_.data() + static_cast<size_t>(p0 + 1) * taps;
134 SampleType acc = SampleType{0.0f};
135 for (int t = 0; t < taps; ++t)
136 acc += (row0[t] + a * (row1[t] - row0[t])) * samples[t];
137 return acc;
138 }
139
144 SampleType read(const SampleType* src, int len, double pos) const {
145 if (src == nullptr || len <= 0 || !ready() || !std::isfinite(pos))
146 return SampleType{0};
147 pos = std::clamp(pos, 0.0, static_cast<double>(len - 1));
148 const int taps = 2 * half_;
149 const long i0 = static_cast<long>(std::floor(pos));
150 const double frac = pos - static_cast<double>(i0);
151 // Phase table lookup with linear interpolation between rows.
152 const double ph = frac * phases_;
153 const int p0 = static_cast<int>(ph);
154 const SampleType a = static_cast<SampleType>(ph - p0);
155 const SampleType* row0 = table_.data() + static_cast<size_t>(p0) * taps;
156 const SampleType* row1 = table_.data() + static_cast<size_t>(p0 + 1) * taps;
157 SampleType acc = SampleType{0.0f};
158 for (int t = 0; t < taps; ++t) {
159 long idx = i0 + t - half_ + 1;
160 if (idx < 0)
161 idx = 0;
162 else if (idx >= len)
163 idx = len - 1;
164 const SampleType k = row0[t] + a * (row1[t] - row0[t]);
165 acc += k * src[idx];
166 }
167 return acc;
168 }
169
170 private:
171 static double sinc(double x) {
172 if (std::abs(x) < 1e-9)
173 return 1.0;
174 const double px = 3.14159265358979323846 * x;
175 return std::sin(px) / px;
176 }
177 // Modified Bessel function of the first kind, order 0 (series).
178 static double bessel_i0(double x) {
179 double sum = 1.0, term = 1.0;
180 const double half_x = x * 0.5;
181 for (int k = 1; k < 40; ++k) {
182 term *= (half_x / k) * (half_x / k);
183 sum += term;
184 if (term < 1e-12 * sum)
185 break;
186 }
187 return sum;
188 }
189
190 int half_ = 0;
191 int phases_ = 0;
192 double cutoff_ = 1.0;
193 std::vector<SampleType> table_;
194 double build_beta_ = 9.0;
195 double build_i0_beta_ = 1.0;
196 int next_build_phase_ = 1;
197};
198
201
202} // namespace pulp::signal
Definition checked_allocation.hpp:57
bool add(std::uint64_t elements) noexcept
Definition checked_allocation.hpp:63
std::uint64_t total() const noexcept
Definition checked_allocation.hpp:83
Definition sinc_resampler.hpp:37
std::size_t prepared_bytes() const noexcept
Definition sinc_resampler.hpp:118
bool build_step()
Definition sinc_resampler.hpp:81
void build(int half_width=16, int phases=512, double beta=9.0, double cutoff=1.0)
Build the kernel.
Definition sinc_resampler.hpp:59
bool ready() const
Definition sinc_resampler.hpp:112
SampleType apply(const SampleType *samples, double frac) const
Apply the kernel at fractional phase frac (in [0,1)) to exactly taps() contiguous samples,...
Definition sinc_resampler.hpp:127
double cutoff() const
Definition sinc_resampler.hpp:115
SampleType read(const SampleType *src, int len, double pos) const
Read src at fractional position pos (in samples).
Definition sinc_resampler.hpp:144
static bool checked_retained_bytes(int half_width, int phases, std::uint64_t target_max_bytes, std::uint64_t &bytes) noexcept
Definition sinc_resampler.hpp:39
void begin_build(int half_width=16, int phases=512, double beta=9.0, double cutoff=1.0)
Starts a resumable kernel-table build.
Definition sinc_resampler.hpp:67
int taps() const
Definition sinc_resampler.hpp:109
int half_width() const
Definition sinc_resampler.hpp:106
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
bool checked_capacity_product(std::uint64_t lhs, std::uint64_t rhs, std::uint64_t maximum, std::uint64_t &product) noexcept
Definition checked_allocation.hpp:12
SincResamplerT< float > SincResampler
Definition sinc_resampler.hpp:199
@ a
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
SincResamplerT< double > SincResampler64
Definition sinc_resampler.hpp:200