Skip to content

Commit

Permalink
Handle systems that don't support std::cyl_bessel_i
Browse files Browse the repository at this point in the history
  • Loading branch information
kcat committed Nov 19, 2023
1 parent 3ff5cb1 commit d6d572d
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 4 deletions.
47 changes: 45 additions & 2 deletions common/polyphase_resampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <algorithm>
#include <cmath>
#include <numeric>
#include <stdexcept>

#include "alnumbers.h"
#include "opthelpers.h"
Expand All @@ -15,6 +16,48 @@ constexpr double Epsilon{1e-9};

using uint = unsigned int;

#if __cpp_lib_math_special_functions >= 201603L
using std::cyl_bessel_i;

#else

/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
*
* This implementation only handles nu = 0, and isn't the most precise (it
* starts with the largest value and accumulates successively smaller values,
* compounding the rounding and precision error), but it's good enough.
*/
template<typename T, typename U>
U cyl_bessel_i(T nu, U x)
{
if(nu != T{0})
throw std::runtime_error{"cyl_bessel_i: nu != 0"};

/* Start at k=1 since k=0 is trivial. */
const double x2{x/2.0};
double term{1.0};
double sum{1.0};
int k{1};

/* Let the integration converge until the term of the sum is no longer
* significant.
*/
double last_sum{};
do {
const double y{x2 / k};
++k;
last_sum = sum;
term *= y * y;
sum += term;
} while(sum != last_sum);
return static_cast<U>(sum);
}
#endif

/* This is the normalized cardinal sine (sinc) function.
*
* sinc(x) = { 1, x = 0
Expand Down Expand Up @@ -45,7 +88,7 @@ double Kaiser(const double beta, const double k, const double besseli_0_beta)
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
return std::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
}

/* Calculates the size (order) of the Kaiser window. Rejection is in dB and
Expand Down Expand Up @@ -122,7 +165,7 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate)
// calculating the left offset to avoid increasing the transition width.
const uint l{(CalcKaiserOrder(180.0, width)+1) / 2};
const double beta{CalcKaiserBeta(180.0)};
const double besseli_0_beta{std::cyl_bessel_i(0, beta)};
const double besseli_0_beta{cyl_bessel_i(0, beta)};
mM = l*2 + 1;
mL = l;
mF.resize(mM);
Expand Down
46 changes: 44 additions & 2 deletions core/bsinc_tables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <limits>
#include <memory>
#include <stddef.h>
#include <stdexcept>

#include "alnumbers.h"
#include "alnumeric.h"
Expand All @@ -19,6 +20,47 @@ namespace {

using uint = unsigned int;

#if __cpp_lib_math_special_functions >= 201603L
using std::cyl_bessel_i;

#else

/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
*
* This implementation only handles nu = 0, and isn't the most precise (it
* starts with the largest value and accumulates successively smaller values,
* compounding the rounding and precision error), but it's good enough.
*/
template<typename T, typename U>
U cyl_bessel_i(T nu, U x)
{
if(nu != T{0})
throw std::runtime_error{"cyl_bessel_i: nu != 0"};

/* Start at k=1 since k=0 is trivial. */
const double x2{x/2.0};
double term{1.0};
double sum{1.0};
int k{1};

/* Let the integration converge until the term of the sum is no longer
* significant.
*/
double last_sum{};
do {
const double y{x2 / k};
++k;
last_sum = sum;
term *= y * y;
sum += term;
} while(sum != last_sum);
return static_cast<U>(sum);
}
#endif

/* This is the normalized cardinal sine (sinc) function.
*
Expand Down Expand Up @@ -51,7 +93,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
return std::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
}

/* Calculates the (normalized frequency) transition width of the Kaiser window.
Expand Down Expand Up @@ -123,7 +165,7 @@ struct BSincFilterArray {
using filter_type = double[BSincPhaseCount+1][BSincPointsMax];
auto filter = std::make_unique<filter_type[]>(BSincScaleCount);

const double besseli_0_beta{std::cyl_bessel_i(0, hdr.beta)};
const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)};

/* Calculate the Kaiser-windowed Sinc filter coefficients for each
* scale and phase index.
Expand Down

0 comments on commit d6d572d

Please sign in to comment.