bsinc_tables.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include "bsinc_tables.h"
  2. #include <algorithm>
  3. #include <array>
  4. #include <cassert>
  5. #include <cmath>
  6. #include <cstddef>
  7. #include <limits>
  8. #include <stdexcept>
  9. #include <vector>
  10. #include "alnumbers.h"
  11. #include "alnumeric.h"
  12. #include "alspan.h"
  13. #include "bsinc_defs.h"
  14. #include "opthelpers.h"
  15. #include "resampler_limits.h"
  16. namespace {
  17. using uint = unsigned int;
  18. /* The zero-order modified Bessel function of the first kind, used for the
  19. * Kaiser window.
  20. *
  21. * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
  22. * = sum_{k=0}^inf ((x / 2)^k / k!)^2
  23. *
  24. * This implementation only handles nu = 0, and isn't the most precise (it
  25. * starts with the largest value and accumulates successively smaller values,
  26. * compounding the rounding and precision error), but it's good enough.
  27. */
  28. template<typename T, typename U>
  29. constexpr auto cyl_bessel_i(T nu, U x) -> U
  30. {
  31. if(nu != T{0})
  32. throw std::runtime_error{"cyl_bessel_i: nu != 0"};
  33. /* Start at k=1 since k=0 is trivial. */
  34. const double x2{x/2.0};
  35. double term{1.0};
  36. double sum{1.0};
  37. int k{1};
  38. /* Let the integration converge until the term of the sum is no longer
  39. * significant.
  40. */
  41. double last_sum{};
  42. do {
  43. const double y{x2 / k};
  44. ++k;
  45. last_sum = sum;
  46. term *= y * y;
  47. sum += term;
  48. } while(sum != last_sum);
  49. return static_cast<U>(sum);
  50. }
  51. /* This is the normalized cardinal sine (sinc) function.
  52. *
  53. * sinc(x) = { 1, x = 0
  54. * { sin(pi x) / (pi x), otherwise.
  55. */
  56. constexpr double Sinc(const double x)
  57. {
  58. constexpr double epsilon{std::numeric_limits<double>::epsilon()};
  59. if(!(x > epsilon || x < -epsilon))
  60. return 1.0;
  61. return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
  62. }
  63. /* Calculate a Kaiser window from the given beta value and a normalized k
  64. * [-1, 1].
  65. *
  66. * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
  67. * { 0, elsewhere.
  68. *
  69. * Where k can be calculated as:
  70. *
  71. * k = i / l, where -l <= i <= l.
  72. *
  73. * or:
  74. *
  75. * k = 2 i / M - 1, where 0 <= i <= M.
  76. */
  77. constexpr double Kaiser(const double beta, const double k, const double besseli_0_beta)
  78. {
  79. if(!(k >= -1.0 && k <= 1.0))
  80. return 0.0;
  81. return ::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
  82. }
  83. /* Calculates the (normalized frequency) transition width of the Kaiser window.
  84. * Rejection is in dB.
  85. */
  86. constexpr double CalcKaiserWidth(const double rejection, const uint order) noexcept
  87. {
  88. if(rejection > 21.19)
  89. return (rejection - 7.95) / (2.285 * al::numbers::pi*2.0 * order);
  90. /* This enforces a minimum rejection of just above 21.18dB */
  91. return 5.79 / (al::numbers::pi*2.0 * order);
  92. }
  93. /* Calculates the beta value of the Kaiser window. Rejection is in dB. */
  94. constexpr double CalcKaiserBeta(const double rejection)
  95. {
  96. if(rejection > 50.0)
  97. return 0.1102 * (rejection-8.7);
  98. if(rejection >= 21.0)
  99. return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0));
  100. return 0.0;
  101. }
  102. struct BSincHeader {
  103. double beta{};
  104. double scaleBase{};
  105. double scaleLimit{};
  106. std::array<double,BSincScaleCount> a{};
  107. std::array<uint,BSincScaleCount> m{};
  108. uint total_size{};
  109. constexpr BSincHeader(uint rejection, uint order, uint maxScale) noexcept
  110. : beta{CalcKaiserBeta(rejection)}, scaleBase{CalcKaiserWidth(rejection, order) / 2.0}
  111. , scaleLimit{1.0 / maxScale}
  112. {
  113. const auto base_a = (order+1.0) / 2.0;
  114. for(uint si{0};si < BSincScaleCount;++si)
  115. {
  116. const auto scale = lerpd(scaleBase, 1.0, (si+1u) / double{BSincScaleCount});
  117. a[si] = std::min(base_a/scale, base_a*maxScale);
  118. /* std::ceil() isn't constexpr until C++23, this should behave the
  119. * same.
  120. */
  121. auto a_ = static_cast<uint>(a[si]);
  122. a_ += (static_cast<double>(a_) != a[si]);
  123. m[si] = a_ * 2u;
  124. total_size += 4u * BSincPhaseCount * ((m[si]+3u) & ~3u);
  125. }
  126. }
  127. };
  128. /* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop
  129. * at nyquist. Each filter will scale up to double size when downsampling, to
  130. * 23rd and 47th order respectively.
  131. */
  132. constexpr auto bsinc12_hdr = BSincHeader{60, 11, 2};
  133. constexpr auto bsinc24_hdr = BSincHeader{60, 23, 2};
  134. /* 47th order filter (48-point) with an 80dB drop at nyquist. The filter order
  135. * doesn't increase when downsampling.
  136. */
  137. constexpr auto bsinc48_hdr = BSincHeader{80, 47, 1};
  138. template<const BSincHeader &hdr>
  139. struct SIMDALIGN BSincFilterArray {
  140. alignas(16) std::array<float, hdr.total_size> mTable{};
  141. BSincFilterArray()
  142. {
  143. static constexpr auto BSincPointsMax = (hdr.m[0]+3u) & ~3u;
  144. static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
  145. using filter_type = std::array<std::array<double,BSincPointsMax>,BSincPhaseCount>;
  146. auto filter = std::vector<filter_type>(BSincScaleCount);
  147. static constexpr auto besseli_0_beta = ::cyl_bessel_i(0, hdr.beta);
  148. /* Calculate the Kaiser-windowed Sinc filter coefficients for each
  149. * scale and phase index.
  150. */
  151. for(uint si{0};si < BSincScaleCount;++si)
  152. {
  153. const auto a = hdr.a[si];
  154. const auto m = hdr.m[si];
  155. const auto l = std::floor(m*0.5) - 1.0;
  156. const auto o = size_t{BSincPointsMax-m} / 2u;
  157. const auto scale = lerpd(hdr.scaleBase, 1.0, (si+1u) / double{BSincScaleCount});
  158. /* Calculate an appropriate cutoff frequency. An explanation may be
  159. * in order here.
  160. *
  161. * When up-sampling, or down-sampling by less than the max scaling
  162. * factor (when scale >= scaleLimit), the filter order increases as
  163. * the down-sampling factor is reduced, enabling a consistent
  164. * filter response output.
  165. *
  166. * When down-sampling by more than the max scale factor, the filter
  167. * order stays constant to avoid further increasing the processing
  168. * cost, causing the transition width to increase. This would
  169. * normally be compensated for by reducing the cutoff frequency,
  170. * to keep the transition band under the nyquist frequency and
  171. * avoid aliasing. However, this has the side-effect of attenuating
  172. * more of the original high frequency content, which can be
  173. * significant with more extreme down-sampling scales.
  174. *
  175. * To combat this, we can allow for some aliasing to keep the
  176. * cutoff frequency higher than it would otherwise be. We can allow
  177. * the transition band to "wrap around" the nyquist frequency, so
  178. * the output would have some low-level aliasing that overlays with
  179. * the attenuated frequencies in the transition band. This allows
  180. * the cutoff frequency to remain fixed as the transition width
  181. * increases, until the stop frequency aliases back to the cutoff
  182. * frequency and the transition band becomes fully wrapped over
  183. * itself, at which point the cutoff frequency will lower at half
  184. * the rate the transition width increases.
  185. *
  186. * This has an additional benefit when dealing with typical output
  187. * rates like 44 or 48khz. Since human hearing maxes out at 20khz,
  188. * and these rates handle frequencies up to 22 or 24khz, this lets
  189. * some aliasing get masked. For example, the bsinc24 filter with
  190. * 48khz output has a cutoff of 20khz when down-sampling, and a
  191. * 4khz transition band. When down-sampling by more extreme scales,
  192. * the cutoff frequency can stay at 20khz while the transition
  193. * width doubles before any aliasing noise may become audible.
  194. *
  195. * This is what we do here.
  196. *
  197. * 'max_cutoff` is the upper bound normalized cutoff frequency for
  198. * this scale factor, that aligns with the same absolute frequency
  199. * as nominal resample factors. When up-sampling (scale == 1), the
  200. * cutoff can't be raised further than this, or else it would
  201. * prematurely add audible aliasing noise.
  202. *
  203. * 'width' is the normalized transition width for this scale
  204. * factor.
  205. *
  206. * '(scale - width)*0.5' calculates the cutoff frequency necessary
  207. * for the transition band to fully wrap on itself around the
  208. * nyquist frequency. If this is larger than max_cutoff, the
  209. * transition band is not fully wrapped at this scale and the
  210. * cutoff doesn't need adjustment.
  211. */
  212. const auto max_cutoff = (0.5 - hdr.scaleBase)*scale;
  213. const auto width = hdr.scaleBase * std::max(hdr.scaleLimit, scale);
  214. const auto cutoff2 = std::min(max_cutoff, (scale - width)*0.5) * 2.0;
  215. for(uint pi{0};pi < BSincPhaseCount;++pi)
  216. {
  217. const auto phase = l + (pi/double{BSincPhaseCount});
  218. for(uint i{0};i < m;++i)
  219. {
  220. const auto x = static_cast<double>(i) - phase;
  221. filter[si][pi][o+i] = Kaiser(hdr.beta, x/a, besseli_0_beta) * cutoff2 *
  222. Sinc(cutoff2*x);
  223. }
  224. }
  225. }
  226. size_t idx{0};
  227. for(size_t si{0};si < BSincScaleCount;++si)
  228. {
  229. const auto m = (hdr.m[si]+3_uz) & ~3_uz;
  230. const auto o = size_t{BSincPointsMax-m} / 2u;
  231. /* Write out each phase index's filter and phase delta for this
  232. * quality scale.
  233. */
  234. for(size_t pi{0};pi < BSincPhaseCount;++pi)
  235. {
  236. for(size_t i{0};i < m;++i)
  237. mTable[idx++] = static_cast<float>(filter[si][pi][o+i]);
  238. /* Linear interpolation between phases is simplified by pre-
  239. * calculating the delta (b - a) in: x = a + f (b - a)
  240. */
  241. if(pi < BSincPhaseCount-1)
  242. {
  243. for(size_t i{0};i < m;++i)
  244. {
  245. const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]};
  246. mTable[idx++] = static_cast<float>(phDelta);
  247. }
  248. }
  249. else
  250. {
  251. /* The delta target for the last phase index is the first
  252. * phase index with the coefficients offset by one. The
  253. * first delta targets 0, as it represents a coefficient
  254. * for a sample that won't be part of the filter.
  255. */
  256. mTable[idx++] = static_cast<float>(0.0 - filter[si][pi][o]);
  257. for(size_t i{1};i < m;++i)
  258. {
  259. const double phDelta{filter[si][0][o+i-1] - filter[si][pi][o+i]};
  260. mTable[idx++] = static_cast<float>(phDelta);
  261. }
  262. }
  263. }
  264. /* Now write out each phase index's scale and phase+scale deltas,
  265. * to complete the bilinear equation for the combination of phase
  266. * and scale.
  267. */
  268. if(si < BSincScaleCount-1)
  269. {
  270. for(size_t pi{0};pi < BSincPhaseCount;++pi)
  271. {
  272. for(size_t i{0};i < m;++i)
  273. {
  274. const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]};
  275. mTable[idx++] = static_cast<float>(scDelta);
  276. }
  277. if(pi < BSincPhaseCount-1)
  278. {
  279. for(size_t i{0};i < m;++i)
  280. {
  281. const double spDelta{(filter[si+1][pi+1][o+i]-filter[si+1][pi][o+i]) -
  282. (filter[si][pi+1][o+i]-filter[si][pi][o+i])};
  283. mTable[idx++] = static_cast<float>(spDelta);
  284. }
  285. }
  286. else
  287. {
  288. mTable[idx++] = static_cast<float>((0.0 - filter[si+1][pi][o]) -
  289. (0.0 - filter[si][pi][o]));
  290. for(size_t i{1};i < m;++i)
  291. {
  292. const double spDelta{(filter[si+1][0][o+i-1] - filter[si+1][pi][o+i]) -
  293. (filter[si][0][o+i-1] - filter[si][pi][o+i])};
  294. mTable[idx++] = static_cast<float>(spDelta);
  295. }
  296. }
  297. }
  298. }
  299. else
  300. {
  301. /* The last scale index doesn't have scale-related deltas. */
  302. for(size_t i{0};i < BSincPhaseCount*m*2;++i)
  303. mTable[idx++] = 0.0f;
  304. }
  305. }
  306. assert(idx == hdr.total_size);
  307. }
  308. [[nodiscard]] constexpr auto getHeader() const noexcept -> const BSincHeader& { return hdr; }
  309. [[nodiscard]] constexpr auto getTable() const noexcept { return al::span{mTable}; }
  310. };
  311. const auto bsinc12_filter = BSincFilterArray<bsinc12_hdr>{};
  312. const auto bsinc24_filter = BSincFilterArray<bsinc24_hdr>{};
  313. const auto bsinc48_filter = BSincFilterArray<bsinc48_hdr>{};
  314. template<typename T>
  315. constexpr BSincTable GenerateBSincTable(const T &filter)
  316. {
  317. BSincTable ret{};
  318. const BSincHeader &hdr = filter.getHeader();
  319. ret.scaleBase = static_cast<float>(hdr.scaleBase);
  320. ret.scaleRange = static_cast<float>(1.0 / (1.0 - hdr.scaleBase));
  321. for(size_t i{0};i < BSincScaleCount;++i)
  322. ret.m[i] = (hdr.m[i]+3u) & ~3u;
  323. ret.filterOffset[0] = 0;
  324. for(size_t i{1};i < BSincScaleCount;++i)
  325. ret.filterOffset[i] = ret.filterOffset[i-1] + ret.m[i-1]*4*BSincPhaseCount;
  326. ret.Tab = filter.getTable();
  327. return ret;
  328. }
  329. } // namespace
  330. const BSincTable gBSinc12{GenerateBSincTable(bsinc12_filter)};
  331. const BSincTable gBSinc24{GenerateBSincTable(bsinc24_filter)};
  332. const BSincTable gBSinc48{GenerateBSincTable(bsinc48_filter)};