#include #include #include "asserts.h" #include "LookupTable.h" #include "AsymWaveShaper.h" #include "ExtremeTester.h" #include "Shaper.h" #include "TestComposite.h" #include "TestSignal.h" using Spline = std::vector< std::pair >; static void gen() { for (int i = 0; i < AsymWaveShaper::iSymmetryTables; ++i) { float symmetry = float(i) / float(AsymWaveShaper::iSymmetryTables - 1); AsymWaveShaper::genTable(i, symmetry); } } static void testLook0() { NonUniformLookup l; l.add(0, 0); l.add(1, 1); l.add(2, 2); l.add(3, 3); double x = l.lookup(2.5); assertClose(x, 2.5, .0001); } static void testLook1() { NonUniformLookup l; l.add(0, 0); l.add(1, 1); l.add(2, 4); l.add(3, 3); double x = l.lookup(1.5); assertClose(x, 2.5, .0001); } static void testLook2() { NonUniformLookup l; l.add(0, 0); l.add(1, 1); l.add(2, 2); l.add(3, 3); double x = l.lookup(.5); assertClose(x, .5, .0001); } static void testLook3() { NonUniformLookup l; l.add(0, 0); l.add(1, 1); l.add(2, 2); l.add(3, 3); double x = l.lookup(2.5); assertClose(x, 2.5, .0001); } static void testLook4() { NonUniformLookup l; l.add(0, 0); l.add(1, 1); l.add(2, 2); l.add(3, 3); double x = l.lookup(0); assertClose(x, 0, .0001); } static void testGen0() { AsymWaveShaper g; const int index = 3;// symmetry float x = g.lookup(0, index); x = g.lookup(1, index); x = g.lookup(-1, index); } static void testDerivativeSub(int index, float delta) { AsymWaveShaper ws; const float ya = ws.lookup(-delta, index); const float y0 = ws.lookup(0, index); const float yb = ws.lookup(delta, index); const float slopeLeft = -ya / delta; const float slopeRight = yb / delta; // printf("[%d] y0 = %f, slope left = %f, right =%f\n", index, y0, slopeLeft, slopeRight); // since I changed AsymWaveShaper to be points-1 this is worse assertClose(y0, 0, .00001); assertClose(slopeRight, 2, .01); if (index != 0) { assertClose(slopeLeft, 2, .3 ); } } static void testDerivative() { // 6 with .1 for (int i = AsymWaveShaper::iSymmetryTables - 1; i >= 0; --i) { testDerivativeSub(i, .0001f); } } static void testShaper0() { Shaper gmr; int shapeMax = (int) Shaper::Shapes::Invalid; for (int i = 0; i < shapeMax; ++i) { gmr.params[Shaper::PARAM_SHAPE].value = (float) i; std::string s = gmr.getString(Shaper::Shapes(i)); assertGT(s.length(), 0); assertLT(s.length(), 20); gmr.params[Shaper::PARAM_OFFSET].value = -5; for (int i = 0; i < 50; ++i) gmr.step(); const float x = gmr.outputs[Shaper::OUTPUT_AUDIO].value; gmr.params[Shaper::PARAM_OFFSET].value = 5; for (int i = 0; i < 50; ++i) gmr.step(); const float y = gmr.outputs[Shaper::OUTPUT_AUDIO].value; assertLT(x, 10); assertLT(y, 10); assertGT(x, -10); assertGT(y, -10); } } static void testShaper1Sub(int shape, float gain, float targetRMS) { Shaper gmr; gmr.params[Shaper::PARAM_SHAPE].value = (float) shape; gmr.params[Shaper::PARAM_GAIN].value = gain; // max gain const int buffSize = 1 * 1024; float buffer[buffSize]; TestSignal::generateSin(buffer, buffSize, 1.f / 40); double rms = TestSignal::getRMS(buffer, buffSize); //printf("signal=%f\n", rms); for (int i = 0; i < buffSize; ++i) { const float x = buffer[i]; gmr.inputs[Shaper::INPUT_AUDIO].value = buffer[i]; gmr.step(); buffer[i] = gmr.outputs[Shaper::OUTPUT_AUDIO].value; } rms = TestSignal::getRMS(buffer, buffSize); // const float targetRMS = 5 * .707f; const char* p = gmr.getString(Shaper::Shapes(shape)); // printf("rms[%s] = %f target = %f ratio=%f\n", p, rms, targetRMS, targetRMS / rms); if (targetRMS > .01) { assertClose(rms, targetRMS, .5); } } static void testShaper1() { int shapeMax = (int) Shaper::Shapes::Invalid; for (int i = 0; i < shapeMax; ++i) { const float targetOutput = (i == (int) Shaper::Shapes::Crush) ? 0 : 5 * .707f; testShaper1Sub(i, 5, targetOutput); testShaper1Sub(i, 0, 0); } } static void testSplineExtremes() { printf("running testSplineExtremes\n"); fflush(stdout); Shaper sp; using fp = std::pair; std::vector< std::pair > paramLimits; paramLimits.resize(sp.NUM_PARAMS); paramLimits[sp.PARAM_SHAPE] = fp(0.f, float(Shaper::Shapes::Invalid) - 1); paramLimits[sp.PARAM_GAIN] = fp(-5.0f, 5.0f); paramLimits[sp.PARAM_GAIN_TRIM] = fp(-1.f, 1.f); paramLimits[sp.PARAM_OFFSET] = fp(-5.f, 5.f); paramLimits[sp.PARAM_OFFSET_TRIM] = fp(-1.f, 1.f); ExtremeTester< Shaper>::test(sp, paramLimits, true, "shaper"); } static void testShaper2d(Shaper::Shapes shape, float gain, float offset, float input) { Shaper sh; sh.params[Shaper::PARAM_SHAPE].value = (float) shape; sh.params[Shaper::PARAM_GAIN].value = gain; sh.params[Shaper::PARAM_OFFSET].value = offset; for (int i = 0; i < 100; ++i) { sh.inputs[Shaper::INPUT_AUDIO].value = input; sh.step(); const float out = sh.outputs[Shaper::OUTPUT_AUDIO].value; // brief ringing goes > 10 assert(out < 20 && out > -20); } } static void testShaper2c(Shaper::Shapes shape, float gain, float offset) { testShaper2d(shape, gain, offset, 0); testShaper2d(shape, gain, offset, 5); testShaper2d(shape, gain, offset, -5); } static void testShaper2b(Shaper::Shapes shape, float gain) { testShaper2c(shape, gain, 0.f); testShaper2c(shape, gain, 5.f); testShaper2c(shape, gain, -5.f); } static void testShaper2a(Shaper::Shapes shape) { testShaper2b(shape, 0); testShaper2b(shape, -5); testShaper2b(shape, 5); } const int shapeMax = (int) Shaper::Shapes::Invalid; static void testShaper2() { for (int i = 0; i < shapeMax; ++i) { testShaper2a(Shaper::Shapes(i)); } } // test for DC shift static void testShaper3Sub(Shaper::Shapes shape) { Shaper sh; sh.params[Shaper::PARAM_OVERSAMPLE].value = 2; // turn off oversampling sh.params[Shaper::PARAM_SHAPE].value = (float) shape; sh.params[Shaper::PARAM_GAIN].value = -3; // gain up a bit sh.params[Shaper::PARAM_OFFSET].value = 0; // no offset sh.inputs[Shaper::INPUT_AUDIO].value = 0; for (int i = 0; i < 100; ++i) { sh.step(); } const float out = sh.outputs[Shaper::OUTPUT_AUDIO].value; if (shape != Shaper::Shapes::Crush) { assertEQ(out, 0); } else { // crash had a dc offset issue assertLT(out, 1); assertGT(out, -1); } } static void testShaper3() { // testShaper3Sub(Shaper::Shapes::Crush); for (int i = 0; i < shapeMax; ++i) { testShaper3Sub(Shaper::Shapes(i)); } } void testSpline(bool doEmit) { if (doEmit) { gen(); return; } testLook0(); testLook1(); testLook2(); testLook3(); testLook4(); testGen0(); testDerivative(); testShaper0(); //printf("!! skipping testShaper1\n"); testShaper1(); testShaper2(); testShaper3(); //testSplineExtremes(); printf("skipping shaper extremems becuase of bug in crush"); }