36 std::uint32_t
tap_count() const noexcept {
return half_width_ * 2; }
38 return 1 -
static_cast<std::int32_t
>(half_width_);
40 std::uint32_t
half_width() const noexcept {
return half_width_; }
41 std::uint32_t
phases() const noexcept {
return phases_; }
42 double cutoff() const noexcept {
return cutoff_; }
45 const auto count =
static_cast<std::size_t
>(
tap_count());
46 const auto rows =
static_cast<std::size_t
>(phases_) + 1;
47 return table_ !=
nullptr && half_width_ >= 2 && half_width_ <= 64 &&
48 phases_ >= 2 && phases_ <= 4096 &&
49 cutoff_ > 0.0 && cutoff_ <= 1.0 && std::isfinite(cutoff_) &&
50 rows <= std::numeric_limits<std::size_t>::max() / count &&
51 table_sample_count_ >= rows * count;
54 float apply(std::span<const float> taps,
double fraction)
const noexcept {
56 !std::isfinite(fraction)) {
59 const auto phase = std::clamp(fraction, 0.0, 1.0) *
60 static_cast<double>(phases_);
61 const auto first_phase = phase >=
static_cast<double>(phases_)
63 :
static_cast<std::uint32_t
>(phase);
64 const auto blend =
static_cast<float>(
65 std::clamp(phase -
static_cast<double>(first_phase), 0.0, 1.0));
66 const auto count =
static_cast<std::size_t
>(
tap_count());
67 const auto* first = table_ +
static_cast<std::size_t
>(first_phase) * count;
68 const auto* second = first + count;
70 for (std::size_t tap = 0; tap < count; ++tap) {
71 const auto coefficient =
72 first[tap] + blend * (second[tap] - first[tap]);
73 result += coefficient * taps[tap];
82 std::size_t table_sample_count,
87 table_sample_count_(table_sample_count),
92 const float* table_ =
nullptr;
93 std::size_t table_sample_count_ = 0;
94 std::uint32_t half_width_ = 0;
95 std::uint32_t phases_ = 0;
99static_assert(std::is_trivially_copyable_v<SampleSincKernelView>);
111 if (!valid_config(config))
return false;
112 const auto taps =
static_cast<std::size_t
>(config.half_width) * 2;
113 const auto rows =
static_cast<std::size_t
>(config.phases) + 1;
114 if (rows > std::numeric_limits<std::size_t>::max() / taps)
118 table_.assign(rows * taps, 0.0f);
123 const auto window_denominator = bessel_i0(config.kaiser_beta);
124 for (std::uint32_t phase = 0; phase <= config.phases; ++phase) {
125 const auto fraction =
static_cast<double>(phase) /
126 static_cast<double>(config.phases);
128 auto* row = table_.data() +
static_cast<std::size_t
>(phase) * taps;
129 for (std::size_t tap = 0; tap < taps; ++tap) {
130 const auto offset =
static_cast<double>(
131 static_cast<std::int64_t
>(tap) + 1 -
132 static_cast<std::int64_t
>(config.half_width)) - fraction;
133 const auto normalized = offset /
134 static_cast<double>(config.half_width);
135 const auto window = std::abs(normalized) < 1.0
136 ? bessel_i0(config.kaiser_beta *
137 std::sqrt(1.0 - normalized * normalized)) /
140 const auto coefficient = config.cutoff *
141 sinc(config.cutoff * offset) * window;
142 row[tap] =
static_cast<float>(coefficient);
145 if (!std::isfinite(sum) || std::abs(sum) < 1.0e-12) {
149 const auto inverse_sum = 1.0 / sum;
150 for (std::size_t tap = 0; tap < taps; ++tap)
151 row[tap] =
static_cast<float>(
152 static_cast<double>(row[tap]) * inverse_sum);
156 config.half_width, config.phases,
163 std::vector<float>().swap(table_);
170 return config.half_width >= 2 && config.half_width <= 64 &&
171 config.phases >= 2 && config.phases <= 4096 &&
172 config.cutoff > 0.0 && config.cutoff <= 1.0 &&
173 std::isfinite(config.cutoff) &&
174 config.kaiser_beta >= 0.0 &&
175 std::isfinite(config.kaiser_beta);
178 static double sinc(
double value)
noexcept {
179 if (std::abs(value) < 1.0e-12)
return 1.0;
180 constexpr double pi = 3.1415926535897932384626433832795;
181 const auto argument = pi * value;
182 return std::sin(argument) / argument;
185 static double bessel_i0(
double value)
noexcept {
188 const auto half = value * 0.5;
189 for (
int order = 1; order < 64; ++order) {
190 const auto ratio = half /
static_cast<double>(order);
191 term *= ratio * ratio;
193 if (term <= sum * 1.0e-15)
break;
198 std::vector<float> table_;
199 SampleSincKernelView view_{};
214 float apply(std::span<const float> taps,
double fraction)
const noexcept {
215 if (!
valid())
return 0.0f;
218 return wider.apply(taps, fraction);
220 return narrower.apply(taps, fraction);
221 const auto wide =
wider.apply(taps, fraction);
222 const auto narrow =
narrower.apply(taps, fraction);
227static_assert(std::is_trivially_copyable_v<SampleSincKernelSelection>);
230 std::array<SampleSincKernelView, kMaximumSampleSincCutoffTables>
kernels{};
235 const auto taps =
kernels[0].tap_count();
236 for (std::uint32_t index = 0; index <
kernel_count; ++index) {
247 double source_frames_per_output)
const noexcept {
248 if (!
valid() || !(source_frames_per_output > 0.0) ||
249 !std::isfinite(source_frames_per_output)) {
252 const auto target_cutoff = std::min(1.0, 1.0 / source_frames_per_output);
253 if (target_cutoff >=
kernels[0].cutoff())
255 for (std::uint32_t index = 1; index <
kernel_count; ++index) {
256 if (target_cutoff ==
kernels[index].cutoff())
258 if (target_cutoff <
kernels[index].cutoff())
continue;
259 const auto wider =
kernels[index - 1];
260 const auto narrower =
kernels[index];
261 const auto gain =
static_cast<float>(
262 (wider.cutoff() - target_cutoff) /
263 (wider.cutoff() - narrower.cutoff()));
264 return {wider, narrower, std::clamp(gain, 0.0f, 1.0f)};
270static_assert(std::is_trivially_copyable_v<SampleSincKernelBankView>);
281 double maximum_source_frames_per_output,
283 std::uint32_t phases = 512,
284 double kaiser_beta = 9.0) {
286 if (!(maximum_source_frames_per_output > 0.0) ||
287 !std::isfinite(maximum_source_frames_per_output)) {
290 const auto octave = maximum_source_frames_per_output <= 1.0
292 : std::ceil(std::log2(maximum_source_frames_per_output));
295 return build(
static_cast<std::uint32_t
>(octave) + 1,
296 half_width, phases, kaiser_beta);
305 double maximum_source_frames_per_output,
306 std::uint32_t tables_per_octave = 4,
308 std::uint32_t phases = 512,
309 double kaiser_beta = 9.0) {
311 if (!(maximum_source_frames_per_output > 0.0) ||
312 maximum_source_frames_per_output >
314 !std::isfinite(maximum_source_frames_per_output) ||
315 tables_per_octave == 0 || tables_per_octave > 8) {
318 const auto intervals = maximum_source_frames_per_output <= 1.0
320 : std::ceil(std::log2(maximum_source_frames_per_output) *
321 static_cast<double>(tables_per_octave));
324 const auto table_count =
static_cast<std::uint32_t
>(intervals) + 1;
325 for (std::uint32_t index = 0; index < table_count; ++index) {
326 const auto cutoff = std::exp2(
327 -
static_cast<double>(index) /
328 static_cast<double>(tables_per_octave));
329 if (!kernels_[index].
build(
330 {half_width, phases, cutoff, kaiser_beta})) {
334 view_.kernels[index] = kernels_[index].view();
336 view_.kernel_count = table_count;
340 bool build(std::uint32_t cutoff_table_count,
342 std::uint32_t phases = 512,
343 double kaiser_beta = 9.0) {
345 if (cutoff_table_count == 0 ||
349 for (std::uint32_t index = 0; index < cutoff_table_count; ++index) {
350 const auto cutoff = std::ldexp(1.0, -
static_cast<int>(index));
351 if (!kernels_[index].
build({half_width, phases, cutoff, kaiser_beta})) {
355 view_.kernels[index] = kernels_[index].view();
357 view_.kernel_count = cutoff_table_count;
363 for (
auto& kernel : kernels_) kernel.release();
369 std::array<SampleSincKernel, kMaximumSampleSincCutoffTables> kernels_{};
370 SampleSincKernelBankView view_{};
SampleSincKernelBankView view() const noexcept
Definition sample_sinc_kernel.hpp:366
SampleSincKernelBank(const SampleSincKernelBank &)=delete
void release() noexcept
Definition sample_sinc_kernel.hpp:361
bool build_for_maximum_consumption(double maximum_source_frames_per_output, std::uint32_t half_width=kDefaultSampleSincHalfWidth, std::uint32_t phases=512, double kaiser_beta=9.0)
Definition sample_sinc_kernel.hpp:280
bool build_dense_for_maximum_consumption(double maximum_source_frames_per_output, std::uint32_t tables_per_octave=4, std::uint32_t half_width=kHighQualitySampleSincHalfWidth, std::uint32_t phases=512, double kaiser_beta=9.0)
Definition sample_sinc_kernel.hpp:304
SampleSincKernelBank(SampleSincKernelBank &&)=delete
bool build(std::uint32_t cutoff_table_count, std::uint32_t half_width=kDefaultSampleSincHalfWidth, std::uint32_t phases=512, double kaiser_beta=9.0)
Definition sample_sinc_kernel.hpp:340
SampleSincKernelBank & operator=(SampleSincKernelBank &&)=delete
SampleSincKernelBank & operator=(const SampleSincKernelBank &)=delete
SampleSincKernelBank()=default
Definition sample_sinc_kernel.hpp:30
SampleSincKernelView()=default
const void * storage_identity() const noexcept
Definition sample_sinc_kernel.hpp:43
bool valid() const noexcept
Definition sample_sinc_kernel.hpp:44
double cutoff() const noexcept
Definition sample_sinc_kernel.hpp:42
float apply(std::span< const float > taps, double fraction) const noexcept
Definition sample_sinc_kernel.hpp:54
std::uint32_t phases() const noexcept
Definition sample_sinc_kernel.hpp:41
friend class SampleSincKernel
Definition sample_sinc_kernel.hpp:79
std::uint32_t tap_count() const noexcept
Definition sample_sinc_kernel.hpp:36
std::uint32_t half_width() const noexcept
Definition sample_sinc_kernel.hpp:40
std::int32_t first_offset() const noexcept
Definition sample_sinc_kernel.hpp:37
Definition sample_sinc_kernel.hpp:101
SampleSincKernel & operator=(const SampleSincKernel &)=delete
SampleSincKernel(SampleSincKernel &&)=delete
void release() noexcept
Definition sample_sinc_kernel.hpp:161
SampleSincKernel & operator=(SampleSincKernel &&)=delete
SampleSincKernelView view() const noexcept
Definition sample_sinc_kernel.hpp:166
SampleSincKernel(const SampleSincKernel &)=delete
bool build(const SampleSincKernelConfig &config)
Definition sample_sinc_kernel.hpp:109
SampleSincKernel()=default
Definition analyzer_provider.hpp:12
constexpr std::size_t kMaximumSampleSincCutoffTables
Definition sample_sinc_kernel.hpp:15
constexpr std::uint32_t kHighQualitySampleSincHalfWidth
Definition sample_sinc_kernel.hpp:19
constexpr double kMaximumDenseSampleSincConsumption
Definition sample_sinc_kernel.hpp:17
constexpr std::size_t kMaximumSampleSincOctaveTables
Definition sample_sinc_kernel.hpp:16
constexpr std::uint32_t kDefaultSampleSincHalfWidth
Definition sample_sinc_kernel.hpp:18
float sum(const float *data, std::size_t count) noexcept
Definition sample_sinc_kernel.hpp:229
SampleSincKernelSelection select(double source_frames_per_output) const noexcept
Definition sample_sinc_kernel.hpp:246
bool valid() const noexcept
Definition sample_sinc_kernel.hpp:233
std::array< SampleSincKernelView, kMaximumSampleSincCutoffTables > kernels
Definition sample_sinc_kernel.hpp:230
std::uint32_t kernel_count
Definition sample_sinc_kernel.hpp:231
Definition sample_sinc_kernel.hpp:21
std::uint32_t phases
Definition sample_sinc_kernel.hpp:23
double cutoff
Definition sample_sinc_kernel.hpp:24
std::uint32_t half_width
Definition sample_sinc_kernel.hpp:22
double kaiser_beta
Definition sample_sinc_kernel.hpp:25
Definition sample_sinc_kernel.hpp:202
SampleSincKernelView narrower
Definition sample_sinc_kernel.hpp:204
SampleSincKernelView wider
Definition sample_sinc_kernel.hpp:203
bool valid() const noexcept
Definition sample_sinc_kernel.hpp:207
float apply(std::span< const float > taps, double fraction) const noexcept
Definition sample_sinc_kernel.hpp:214
float narrower_gain
Definition sample_sinc_kernel.hpp:205