#include "cubic_tables.h" #include #include #include #include "alnumbers.h" #include "alnumeric.h" #include "cubic_defs.h" /* These gaussian filter tables are inspired by the gaussian-like filter found * in the SNES. This is based on the public domain code developed by Near, with * the help of Ryphecha and nocash, from the nesdev.org forums. * * * * Additional changes were made here, the most obvious being that it has full * floating-point precision instead of 11-bit fixed-point, but also an offset * adjustment for the coefficients to better preserve phase. */ namespace { [[nodiscard]] auto GetCoeff(double idx) noexcept -> double { const double k{0.5 + idx}; if(k > 512.0) return 0.0; const double s{ std::sin(al::numbers::pi*1.280/1024.0 * k)}; const double t{(std::cos(al::numbers::pi*2.000/1023.0 * k) - 1.0) * 0.50}; const double u{(std::cos(al::numbers::pi*4.000/1023.0 * k) - 1.0) * 0.08}; return s * (t + u + 1.0) / k; } } // namespace GaussianTable::GaussianTable() { static constexpr double IndexScale{512.0 / double{CubicPhaseCount*2}}; /* Fill in the main coefficients. */ for(std::size_t pi{0};pi < CubicPhaseCount;++pi) { const double coeff0{GetCoeff(static_cast(CubicPhaseCount + pi)*IndexScale)}; const double coeff1{GetCoeff(static_cast(pi)*IndexScale)}; const double coeff2{GetCoeff(static_cast(CubicPhaseCount - pi)*IndexScale)}; const double coeff3{GetCoeff(static_cast(CubicPhaseCount*2_uz-pi)*IndexScale)}; const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; mTable[pi].mCoeffs[0] = static_cast(coeff0 * scale); mTable[pi].mCoeffs[1] = static_cast(coeff1 * scale); mTable[pi].mCoeffs[2] = static_cast(coeff2 * scale); mTable[pi].mCoeffs[3] = static_cast(coeff3 * scale); } /* Fill in the coefficient deltas. */ for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) { mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; } const std::size_t pi{CubicPhaseCount - 1}; mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; } SplineTable::SplineTable() { static constexpr auto third = 1.0/3.0; static constexpr auto sixth = 1.0/6.0; /* This filter table is based on a Catmull-Rom spline. It retains more of * the original high-frequency content, at the cost of increased harmonics. */ for(std::size_t pi{0};pi < CubicPhaseCount;++pi) { const auto mu = static_cast(pi) / double{CubicPhaseCount}; const auto mu2 = mu*mu; const auto mu3 = mu*mu2; mTable[pi].mCoeffs[0] = static_cast( -third*mu + 0.5*mu2 - sixth*mu3); mTable[pi].mCoeffs[1] = static_cast(1.0 - 0.5*mu - mu2 + 0.5*mu3); mTable[pi].mCoeffs[2] = static_cast( mu + 0.5*mu2 - 0.5*mu3); mTable[pi].mCoeffs[3] = static_cast( -sixth*mu + sixth*mu3); } for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) { mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; } static constexpr auto pi = std::size_t{CubicPhaseCount - 1}; mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; } CubicFilter::CubicFilter() { static constexpr double IndexScale{512.0 / double{sTableSteps*2}}; /* Only half the coefficients need to be iterated here, since Coeff2 and * Coeff3 are just Coeff1 and Coeff0 in reverse respectively. */ for(size_t i{0};i < sTableSteps/2 + 1;++i) { const double coeff0{GetCoeff(static_cast(sTableSteps + i)*IndexScale)}; const double coeff1{GetCoeff(static_cast(i)*IndexScale)}; const double coeff2{GetCoeff(static_cast(sTableSteps - i)*IndexScale)}; const double coeff3{GetCoeff(static_cast(sTableSteps*2_uz - i)*IndexScale)}; const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; mFilter[sTableSteps + i] = static_cast(coeff0 * scale); mFilter[i] = static_cast(coeff1 * scale); mFilter[sTableSteps - i] = static_cast(coeff2 * scale); mFilter[sTableSteps*2 - i] = static_cast(coeff3 * scale); } }