cubic_tables.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #include "cubic_tables.h"
  2. #include <array>
  3. #include <cmath>
  4. #include <cstddef>
  5. #include "alnumbers.h"
  6. #include "alnumeric.h"
  7. #include "cubic_defs.h"
  8. /* These gaussian filter tables are inspired by the gaussian-like filter found
  9. * in the SNES. This is based on the public domain code developed by Near, with
  10. * the help of Ryphecha and nocash, from the nesdev.org forums.
  11. *
  12. * <https://forums.nesdev.org/viewtopic.php?p=251534#p251534>
  13. *
  14. * Additional changes were made here, the most obvious being that it has full
  15. * floating-point precision instead of 11-bit fixed-point, but also an offset
  16. * adjustment for the coefficients to better preserve phase.
  17. */
  18. namespace {
  19. [[nodiscard]]
  20. auto GetCoeff(double idx) noexcept -> double
  21. {
  22. const double k{0.5 + idx};
  23. if(k > 512.0) return 0.0;
  24. const double s{ std::sin(al::numbers::pi*1.280/1024.0 * k)};
  25. const double t{(std::cos(al::numbers::pi*2.000/1023.0 * k) - 1.0) * 0.50};
  26. const double u{(std::cos(al::numbers::pi*4.000/1023.0 * k) - 1.0) * 0.08};
  27. return s * (t + u + 1.0) / k;
  28. }
  29. } // namespace
  30. GaussianTable::GaussianTable()
  31. {
  32. static constexpr double IndexScale{512.0 / double{CubicPhaseCount*2}};
  33. /* Fill in the main coefficients. */
  34. for(std::size_t pi{0};pi < CubicPhaseCount;++pi)
  35. {
  36. const double coeff0{GetCoeff(static_cast<double>(CubicPhaseCount + pi)*IndexScale)};
  37. const double coeff1{GetCoeff(static_cast<double>(pi)*IndexScale)};
  38. const double coeff2{GetCoeff(static_cast<double>(CubicPhaseCount - pi)*IndexScale)};
  39. const double coeff3{GetCoeff(static_cast<double>(CubicPhaseCount*2_uz-pi)*IndexScale)};
  40. const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)};
  41. mTable[pi].mCoeffs[0] = static_cast<float>(coeff0 * scale);
  42. mTable[pi].mCoeffs[1] = static_cast<float>(coeff1 * scale);
  43. mTable[pi].mCoeffs[2] = static_cast<float>(coeff2 * scale);
  44. mTable[pi].mCoeffs[3] = static_cast<float>(coeff3 * scale);
  45. }
  46. /* Fill in the coefficient deltas. */
  47. for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi)
  48. {
  49. mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0];
  50. mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1];
  51. mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2];
  52. mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3];
  53. }
  54. const std::size_t pi{CubicPhaseCount - 1};
  55. mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0];
  56. mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1];
  57. mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2];
  58. mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3];
  59. }
  60. SplineTable::SplineTable()
  61. {
  62. static constexpr auto third = 1.0/3.0;
  63. static constexpr auto sixth = 1.0/6.0;
  64. /* This filter table is based on a Catmull-Rom spline. It retains more of
  65. * the original high-frequency content, at the cost of increased harmonics.
  66. */
  67. for(std::size_t pi{0};pi < CubicPhaseCount;++pi)
  68. {
  69. const auto mu = static_cast<double>(pi) / double{CubicPhaseCount};
  70. const auto mu2 = mu*mu;
  71. const auto mu3 = mu*mu2;
  72. mTable[pi].mCoeffs[0] = static_cast<float>( -third*mu + 0.5*mu2 - sixth*mu3);
  73. mTable[pi].mCoeffs[1] = static_cast<float>(1.0 - 0.5*mu - mu2 + 0.5*mu3);
  74. mTable[pi].mCoeffs[2] = static_cast<float>( mu + 0.5*mu2 - 0.5*mu3);
  75. mTable[pi].mCoeffs[3] = static_cast<float>( -sixth*mu + sixth*mu3);
  76. }
  77. for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi)
  78. {
  79. mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0];
  80. mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1];
  81. mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2];
  82. mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3];
  83. }
  84. static constexpr auto pi = std::size_t{CubicPhaseCount - 1};
  85. mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0];
  86. mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1];
  87. mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2];
  88. mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3];
  89. }
  90. CubicFilter::CubicFilter()
  91. {
  92. static constexpr double IndexScale{512.0 / double{sTableSteps*2}};
  93. /* Only half the coefficients need to be iterated here, since Coeff2 and
  94. * Coeff3 are just Coeff1 and Coeff0 in reverse respectively.
  95. */
  96. for(size_t i{0};i < sTableSteps/2 + 1;++i)
  97. {
  98. const double coeff0{GetCoeff(static_cast<double>(sTableSteps + i)*IndexScale)};
  99. const double coeff1{GetCoeff(static_cast<double>(i)*IndexScale)};
  100. const double coeff2{GetCoeff(static_cast<double>(sTableSteps - i)*IndexScale)};
  101. const double coeff3{GetCoeff(static_cast<double>(sTableSteps*2_uz - i)*IndexScale)};
  102. const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)};
  103. mFilter[sTableSteps + i] = static_cast<float>(coeff0 * scale);
  104. mFilter[i] = static_cast<float>(coeff1 * scale);
  105. mFilter[sTableSteps - i] = static_cast<float>(coeff2 * scale);
  106. mFilter[sTableSteps*2 - i] = static_cast<float>(coeff3 * scale);
  107. }
  108. }