#include #include #include "asserts.h" #include "AudioMath.h" #include "LookupTable.h" #include "LookupTableFactory.h" #include "ObjectCache.h" using namespace std; // test that we can call all the functions template static void test0() { LookupTableParams p; const int tableSize = 512; std::function f = [](double d) { return 0; }; LookupTable::init(p, tableSize, 0, 1, f); LookupTable::lookup(p, 0); } // test that simple lookup works template static void test1() { LookupTableParams p; const int tableSize = 512; std::function f = [](double d) { return 100; }; LookupTable::init(p, tableSize, 0, 1, f); assert(LookupTable::lookup(p, 0) == 100); assert(LookupTable::lookup(p, 1) == 100); assert(LookupTable::lookup(p, T(.342)) == 100); } // test that sin works template static void test2() { LookupTableParams p; const int tableSize = 512; std::function f = [](double d) { return std::sin(d); }; LookupTable::init(p, tableSize, 0, 1, f); const T tolerance = T(0.000001); for (double d = 0; d < 1; d += .0001) { T output = LookupTable::lookup(p, T(d)); const bool t = AudioMath::closeTo(output, std::sin(d), tolerance); if (!t) { cout << "failing with expected=" << std::sin(d) << " actual=" << output << " delta=" << std::abs(output - std::sin(d)); assert(false); } } } // test that sin works on domain 10..32 template static void test3() { LookupTableParams p; const int tableSize = 16; std::function f = [](double d) { const double s = (d - 10) / 3; return std::sin(s); }; LookupTable::init(p, tableSize, 10, 13, f); const T tolerance = T(0.01); for (double d = 10; d < 13; d += .0001) { const T output = LookupTable::lookup(p, T(d)); const T expected = (T) std::sin((d - 10.0) / 3); const bool t = AudioMath::closeTo(output, expected, tolerance); if (!t) { cout << "failing with d=" << d << " expected=" << expected << " actual=" << output << " delta=" << std::abs(output - std::sin(d)); assert(false); } } } // test that sin at extremes works template static void test4() { LookupTableParams exponential; const T xMin = -5; const T xMax = 5; std::function expFunc = AudioMath::makeFunc_Exp(-5, 5, 2, 2000); LookupTable::init(exponential, 128, -5, 5, expFunc); // Had to loosen tolerance to pass with windows gcc. Is there a problem // with precision, or is this expected with fast math? const T tolerance = T(0.0003); T outputLow = LookupTable::lookup(exponential, xMin); T outputHigh = LookupTable::lookup(exponential, xMax); bool t = AudioMath::closeTo(outputLow, 2, tolerance); if (!t) { cout << "failing l with expected=" << 2 << " actual=" << outputLow << " delta=" << std::abs(outputLow - 2) << std::endl; assert(false); } t = AudioMath::closeTo(outputHigh, 2000, tolerance); if (!t) { cout << "failing h with expected=" << 2000 << " actual=" << outputHigh << " delta=" << std::abs(outputHigh - 2000) << std::endl; assert(false); } } template static void testDiscrete1() { LookupTableParams lookup; T y[] = {0, 10}; LookupTable::initDiscrete(lookup, 2, y); assertEQ(LookupTable::lookup(lookup, 0), 0); assertEQ(LookupTable::lookup(lookup, .5), 5); assertEQ(LookupTable::lookup(lookup, 1), 10); assertEQ(LookupTable::lookup(lookup, T(.1)), 1); assertClose(LookupTable::lookup(lookup, T(.01)), T(.1), .00001); } template static void testDiscrete2() { LookupTableParams lookup; T y[] = {100, 100.5, 2000, -10}; LookupTable::initDiscrete(lookup, 4, y); assertEQ(LookupTable::lookup(lookup, 0), 100); assertEQ(LookupTable::lookup(lookup, 1), 100.5); assertEQ(LookupTable::lookup(lookup, 2), 2000); assertEQ(LookupTable::lookup(lookup, 3), -10); assertEQ(LookupTable::lookup(lookup, 2.5), 1000 - 5); } template static void testExpSimpleLookup() { LookupTableParams lookup; LookupTableFactory::makeExp2(lookup); const double xMin = LookupTableFactory::exp2XMin(); const double xMax = LookupTableFactory::exp2XMax(); assert(5 > xMin); assert(11 < xMax); assertClose(LookupTable::lookup(lookup, 5), std::pow(2, 5), .01); assertClose(LookupTable::lookup(lookup, 11), std::pow(2, 11), 2); // TODO: tighten } // test that extreme inputs is clamped template static void testExpRange() { LookupTableParams lookup; LookupTable::makeExp2(lookup); auto k1 = LookupTable::lookup(lookup, -1); auto k2 = LookupTable::lookup(lookup, 11); assertClose(LookupTable::lookup(lookup, -1), LookupTable::lookup(lookup, 0), .01); assertClose(LookupTable::lookup(lookup, 11), LookupTable::lookup(lookup, 10), .01); assertClose(LookupTable::lookup(lookup, -100), LookupTable::lookup(lookup, 0), .01); assertClose(LookupTable::lookup(lookup, 1100), LookupTable::lookup(lookup, 10), .01); } template static void testExpTolerance(T centsTolerance) { const T xMin = (T) LookupTableFactory::exp2XMin(); const T xMax = (T) LookupTableFactory::exp2XMax(); LookupTableParams table; LookupTableFactory::makeExp2(table); for (T x = xMin; x <= xMax; x += T(.001)) { T y = LookupTable::lookup(table, x); // and back double accurate = std::pow(2.0, x); double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); } } template static void testExpTolerance2(T centsTolerance, T hzTolerance) { const T xMin = (T) LookupTableFactory::exp2XMin(); const T xMax = (T) LookupTableFactory::exp2XMax(); LookupTableParams table; LookupTableFactory::makeExp2(table); for (T x = xMin; x <= xMax; x += T(.001)) { T y = LookupTable::lookup(table, x); // and back double accurate = std::pow(2.0, x); double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); const double errorHz = std::abs(y - accurate); assertClose(errorHz, 0, hzTolerance); } } template static void testExp2HiTolerance(T centsTolerance, T hzTolerance) { const T xMin = (T) LookupTableFactory::exp2ExHighXMin(); const T xMax = (T) LookupTableFactory::exp2ExHighXMax(); LookupTableParams table; LookupTableFactory::makeExp2ExHigh(table); for (T x = xMin; x <= xMax; x += T(.001)) { T y = LookupTable::lookup(table, x); // and back double accurate = std::pow(2.0, x); double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); const double errorHz = std::abs(y - accurate); // relax limits at high freq T curHzTol = hzTolerance; if (y > 10000) curHzTol *= 10; if (y > 5000) curHzTol *= 4; else if (y > 3000) curHzTol *= 2; if (y > 2000) curHzTol *= 1.1f; assertClose(errorHz, 0, curHzTol); } } template static void testExp2LowTolerance(T centsTolerance, T hzTolerance) { const T xMin = (T) LookupTableFactory::exp2ExLowXMin(); const T xMax = (T) LookupTableFactory::exp2ExLowXMax(); LookupTableParams table; LookupTableFactory::makeExp2ExLow(table); for (T x = xMin; x <= xMax; x += T(.001)) { T y = LookupTable::lookup(table, x); // and back double accurate = std::pow(2.0, x); double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); const double errorHz = std::abs(y - accurate); assertClose(errorHz, 0, hzTolerance); } } template static void testExp2CombinedTolerance(T centsTolerance, T hzTolerance) { const T xMin = (T) LookupTableFactory::exp2ExLowXMin(); const T xMax = (T) LookupTableFactory::exp2ExHighXMax(); // LookupTableParams table; auto exp = ObjectCache::getExp2Ex(); for (T x = xMin; x <= xMax; x += T(.001)) { T y = exp(x); double accurate = std::pow(2.0, x); double errorCents = AudioMath::acents(y, accurate); assertClose(errorCents, 0, centsTolerance); const double errorHz = std::abs(y - accurate); T curHzTol = hzTolerance; if (y > 10000) curHzTol *= 10; if (y > 5000) curHzTol *= 4; else if (y > 3000) curHzTol *= 2; if (y > 2000) curHzTol *= 1.1f; assertClose(errorHz, 0, curHzTol); } } template static void testBipolarSimpleLookup() { LookupTableParams lookup; LookupTableFactory::makeBipolarAudioTaper(lookup); assertClose(LookupTable::lookup(lookup, 0), 0, .01); assertClose(LookupTable::lookup(lookup, 1), 1, .01); assertClose(LookupTable::lookup(lookup, -1), -1, .01); } template static void testAudioTaperSimpleLookup() { LookupTableParams lookup; LookupTableFactory::makeAudioTaper(lookup); assertClose(LookupTable::lookup(lookup, 0), 0, .01); assertClose(LookupTable::lookup(lookup, 1), 1, .01); } template static void testBipolarTolerance() { LookupTableParams lookup; LookupTableFactory::makeBipolarAudioTaper(lookup); const double toleratedError = 1 - AudioMath::gainFromDb(-.1);// let's go for one db. assert(toleratedError > 0); auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory::audioTaperKnee()); auto refFuncNeg = [refFuncPos](double x) { assert(x <= 0); return -refFuncPos(-x); }; for (double x = -1; x < 1; x += .001) { const T test = LookupTable::lookup(lookup, (T) x); T ref = 1234; if (x < 0) { ref = (T) refFuncNeg(x); } else { ref = (T) refFuncPos(x); } assertClose(test, ref, toleratedError); } } template static void testAudioTaperTolerance() { LookupTableParams lookup; LookupTableFactory::makeAudioTaper(lookup); const double toleratedError = 1 - AudioMath::gainFromDb(-.1);// let's go for one db. assert(toleratedError > 0); auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory::audioTaperKnee()); for (double x = 0; x < 1; x += .001) { const T test = LookupTable::lookup(lookup, (T) x); T ref = (T) refFuncPos(x); assertClose(test, ref, toleratedError); } } template static void test() { test0(); test1(); test2(); test3(); test4(); testDiscrete1(); testDiscrete2(); testExpSimpleLookup(); testExpTolerance(1); // 1 cent testExp2HiTolerance(.125, T(.05)); testExp2LowTolerance(.125, T(.05)); testExp2CombinedTolerance(.125, T(.5)); testBipolarSimpleLookup(); testBipolarTolerance(); testAudioTaperSimpleLookup(); testAudioTaperTolerance(); } void testLookupTable() { test(); test(); }