#include "FFTCrossFader.h" #include "ColoredNoise.h" #include "asserts.h" #include class Tester { public: Tester(int crossFadeSize, int frameSize) : f(crossFadeSize) { for (int i = 0; i < 3; ++i) { std::shared_ptr p = std::make_shared(frameSize); messages.push_back(p); } } FFTCrossFader f; std::vector< std::shared_ptr > messages; }; // accepting data on empty should not return on static void test0() { Tester test(4, 10); assertEQ(test.messages[0]->dataBuffer->get(0), 0); assertEQ(test.messages[0]->dataBuffer->get(9), 0); NoiseMessage* t = test.f.acceptData(test.messages[0].get()); assertEQ(t, 0); } //empty should return 0 static void test1() { Tester test(4, 10); for (int i = 0; i < 20; ++i) { float x = 5; test.f.step(&x); assertEQ(x, 0); } } // one buff, should play it static void test2() { Tester test(4, 10); for (int i = 0; i < 10; ++i) { test.messages[0]->dataBuffer->set(i, float(i)); } NoiseMessage* t = test.f.acceptData(test.messages[0].get()); assertEQ(t, 0); // pluy buffer once for (int i = 0; i < 10; ++i) { float x = 5; test.f.step(&x); assertEQ(x, i); } //play it again. for (int i = 0; i < 10; ++i) { float x = 5; test.f.step(&x); assertEQ(x, i); } } // two buff, should crossfade static void test3(bool testBuff0) { Tester test(4, 10); // fill the buff to test with data, other one with zeros for (int i = 0; i < 10; ++i) { test.messages[0]->dataBuffer->set(i, testBuff0 ? 9.f : 0.f); test.messages[1]->dataBuffer->set(i, testBuff0 ? 0.f : 18.f); } // put both in, to cross fade NoiseMessage* t = test.f.acceptData(test.messages[0].get()); assertEQ(t, 0); t = test.f.acceptData(test.messages[1].get()); assertEQ(t, 0); int emptyCount = 0; // play buffer once // buffer 0 full of 9, so should see fade 9..0 float expected0[] = {9, 6, 3, 0, 0, 0, 0, 0, 0, 0}; // buffer 0 all zero, 1 all 18, so should see 0..18 float expected1[] = {0, 6, 12, 18, 18, 18, 18, 18, 18, 18}; for (int i = 0; i < 10; ++i) { float x = 5; t = test.f.step(&x); if (t) { ++emptyCount; } const float expected = testBuff0 ? expected0[i] : expected1[i]; assertEQ(x, expected); } //play it again. for (int i = 0; i < 10; ++i) { float x = 5; // test.f.step(&x); t = test.f.step(&x); if (t) { ++emptyCount; } const float expectedTail = testBuff0 ? 0.f : 18.f; assertEQ(x, expectedTail); } assertEQ(emptyCount, 1); } // two buff, should crossfade. odd size crossfade static void test7(bool testBuff0) { Tester test(5, 10); // fill the buff to test with data, other one with zeros for (int i = 0; i < 10; ++i) { test.messages[0]->dataBuffer->set(i, testBuff0 ? 12.f : 0.f); test.messages[1]->dataBuffer->set(i, testBuff0 ? 0.f : 24.f); } // put both in, to cross fade NoiseMessage* t = test.f.acceptData(test.messages[0].get()); assertEQ(t, 0); t = test.f.acceptData(test.messages[1].get()); assertEQ(t, 0); int emptyCount = 0; // play buffer once float expected0[] = {12, 9, 6, 3, 0, 0, 0, 0, 0, 0}; float expected1[] = {0, 6, 12, 18, 24, 24, 24, 24, 24, 24}; for (int i = 0; i < 10; ++i) { float x = 5; t = test.f.step(&x); if (t) { ++emptyCount; } const float expected = testBuff0 ? expected0[i] : expected1[i]; assertEQ(x, expected); } //play it again. for (int i = 0; i < 10; ++i) { float x = 5; // test.f.step(&x); t = test.f.step(&x); if (t) { ++emptyCount; } const float expectedTail = testBuff0 ? 0.f : 24.f; assertEQ(x, expectedTail); } assertEQ(emptyCount, 1); } // extra buffer rejected static void test4() { Tester test(4, 10); NoiseMessage* t = test.f.acceptData(test.messages[0].get()); assertEQ(t, 0); t = test.f.acceptData(test.messages[1].get()); assertEQ(t, 0); t = test.f.acceptData(test.messages[2].get()); assertNE(t, 0); } // test wrap-around case static void test5() { // fade of 4, buffer of 8 Tester test(4, 8); // fill the buff to test with data, other one with zeros for (int i = 0; i < 8; ++i) { test.messages[0]->dataBuffer->set(i, 0.f); test.messages[1]->dataBuffer->set(i, float(i)); } // put zero 0 NoiseMessage* t = test.f.acceptData(test.messages[0].get()); float x; // clock 6 for (int i = 0; i < 6; ++i) { x = 5; t = test.f.step(&x); assertEQ(x, 0); // 0 assertEQ(t, 0); } // now start crossfade t = test.f.acceptData(test.messages[1].get()); assertEQ(t, 0); // sample #6. start fade t = test.f.step(&x); assertEQ(x, 0); // 0 assertEQ(t, 0); // sample #7. fade #2 t = test.f.step(&x); assertClose(x, .3333333f, .0001); // 0 assertEQ(t, 0); // sample#8, fade #3 t = test.f.step(&x); assertClose(x, 1.3333333f, .0001); // 0 assertEQ(t, 0); // sample#8, fade #4 (last), buff 0 (?) gets returned t = test.f.step(&x); assertClose(x, 3.f, .0001); // 0 assertNE(t, 0); // done fading t = test.f.step(&x); assertClose(x, 4.f, .0001); // 0 assertEQ(t, 0); t = test.f.step(&x); assertClose(x, 5.f, .0001); // 0 assertEQ(t, 0); t = test.f.step(&x); assertClose(x, 6.f, .0001); // 0 assertEQ(t, 0); t = test.f.step(&x); assertClose(x, 7.f, .0001); // 0 assertEQ(t, 0); t = test.f.step(&x); assertClose(x, 0.f, .0001); // 0 assertEQ(t, 0); } // test makeup gain static void test6(bool makeup) { // fade of 5, buffer of 8 Tester test(5, 8); test.f.enableMakeupGain(makeup); // fill the buffers with 1 for (int i = 0; i < 8; ++i) { test.messages[0]->dataBuffer->set(i, 1.f); test.messages[1]->dataBuffer->set(i, 1.f); } // put messages test.f.acceptData(test.messages[0].get()); test.f.acceptData(test.messages[1].get()); float x; for (int i = 0; i < 5; ++i) { x = 5; test.f.step(&x); float expected = 1; if (makeup) switch (i) { case 0: case 4: expected = 1; break; case 2: expected = std::sqrt(2.f); break; case 1: case 3: expected = (1.f + std::sqrt(2.f)) / 2.f; break; default: assert(false); } assertClose(x, expected, .0001); } } void testFFTCrossFader() { assertEQ(FFTDataReal::_count, 0); test0(); test1(); test2(); test3(true); test3(false); test7(true); test7(false); test4(); test5(); test6(false); test6(true); assertEQ(FFTDataReal::_count, 0); }