#include #include #include #include "test_fx_helper.h" #include "../fx_engine.hpp" #define PRINT_EXEC(ctx, x) \ std::cout.fill(' '); \ std::cout.width(80); \ std::cout << std::left; \ std::cout.precision(6); \ std::cout << std::fixed; \ std::cout << #x; \ x \ { \ float32_t v = 0.0f; \ ctx.write(v); \ std::cout << " // accumulator_: " << showpos << v; \ } \ std::cout << std::endl #define TAIL , -1 typedef FxEngine<16384, Format::FORMAT_FLOAT32, true> Engine; void processDebugReverberatorSample( Engine& engine_, size_t index, float32_t& lp_decay_1_, float32_t& lp_decay_2_, float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { // This is the Griesinger topology described in the Dattorro paper // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). // Modulation is applied in the loop of the first diffuser AP for additional // smearing; and to the two long delays for a slow shimmer/chorus effect. typedef Engine::Reserve< 113, Engine::Reserve< 162, Engine::Reserve< 241, Engine::Reserve< 399, Engine::Reserve<1653, Engine::Reserve<2038, Engine::Reserve<3411, Engine::Reserve<1913, Engine::Reserve<1663, Engine::Reserve<4782> > > > > > > > > > Memory; Engine::DelayLine ap1; Engine::DelayLine ap2; Engine::DelayLine ap3; Engine::DelayLine ap4; Engine::DelayLine dap1a; Engine::DelayLine dap1b; Engine::DelayLine del1; Engine::DelayLine dap2a; Engine::DelayLine dap2b; Engine::DelayLine del2; Engine::Context c; const float32_t kap = 0.8f; const float32_t klp = 0.7f; const float32_t krt = 0.75f; const float32_t gain = 0.55f; float32_t lp_1 = lp_decay_1_; float32_t lp_2 = lp_decay_2_; float32_t wet = 0.0f; float32_t apout = 0.0f; engine_.start(&c); // Smear AP1 inside the loop. PRINT_EXEC(c, c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f);); PRINT_EXEC(c, c.write(ap1, 100, 0.0f);); PRINT_EXEC(c, c.read(inL + inR, gain);); // Diffuse through 4 allpasses. PRINT_EXEC(c, c.read(ap1 TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(ap1, -kap);); PRINT_EXEC(c, c.read(ap2 TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(ap2, -kap);); PRINT_EXEC(c, c.read(ap3 TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(ap3, -kap);); PRINT_EXEC(c, c.read(ap4 TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(ap4, -kap);); PRINT_EXEC(c, c.write(apout);); // Main reverb loop. PRINT_EXEC(c, c.load(apout);); PRINT_EXEC(c, c.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt);); PRINT_EXEC(c, c.lp(lp_1, klp);); PRINT_EXEC(c, c.read(dap1a TAIL, -kap);); PRINT_EXEC(c, c.writeAllPass(dap1a, kap);); PRINT_EXEC(c, c.read(dap1b TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(dap1b, -kap);); PRINT_EXEC(c, c.write(del1, 1.5f);); PRINT_EXEC(c, c.write(wet, 0.0f);); outL = wet; PRINT_EXEC(c, c.load(apout);); // PRINT_EXEC(c, c.interpolate(del1, 4450.0f, Engine::LFOIndex::LFO_1, 50.0f, krt);); PRINT_EXEC(c, c.read(del1 TAIL, krt);); PRINT_EXEC(c, c.lp(lp_2, klp);); PRINT_EXEC(c, c.read(dap2a TAIL, kap);); PRINT_EXEC(c, c.writeAllPass(dap2a, -kap);); PRINT_EXEC(c, c.read(dap2b TAIL, -kap);); PRINT_EXEC(c, c.writeAllPass(dap2b, kap);); PRINT_EXEC(c, c.write(del2, 1.5f);); PRINT_EXEC(c, c.write(wet, 0.0f);); outR = wet; lp_decay_1_ = lp_1; lp_decay_2_ = lp_2; std::cout << "Index # " << index << " - ( " << inL << ", " << inR << " ) ==> ( " << outL << ", " << outR << " )" << std::endl; std::cout << std::endl << "***********************************************************************************************************" << std::endl << std::endl; } TEST(LowLevel, TestDiracReverberatorAlgo) { const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); std::string full_test_name = test_info->test_case_name(); full_test_name += "."; full_test_name += test_info->name(); Engine engine_(SAMPLING_FREQUENCY); engine_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); engine_.reset(); float32_t lp1 = 0.0f; float32_t lp2 = 0.0f; const size_t size = static_cast(SAMPLING_FREQUENCY) * 4; float32_t* inSamples = new float32_t[size]; memset(inSamples, 0, size * sizeof(float32_t)); inSamples[0] = 1.0f; float32_t* outSamplesL = new float32_t[size]; float32_t* outSamplesR = new float32_t[size]; memset(outSamplesL, 0, size * sizeof(float32_t)); memset(outSamplesR, 0, size * sizeof(float32_t)); for(size_t i = 0; i < size; ++i) { processDebugReverberatorSample(engine_, i, lp1, lp2, inSamples[i], inSamples[i], outSamplesL[i], outSamplesR[i]); } saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); delete[] outSamplesL; delete[] outSamplesR; delete[] inSamples; } void processReverberatorSample( Engine& engine_L_, Engine& engine_R_, size_t index, float32_t& lp_decay_1_, float32_t& lp_decay_2_, float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { // This is the Griesinger topology described in the Dattorro paper // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). // Modulation is applied in the loop of the first diffuser AP for additional // smearing; and to the two long delays for a slow shimmer/chorus effect. typedef Engine::Reserve< 113, Engine::Reserve< 162, Engine::Reserve< 241, Engine::Reserve< 399, Engine::Reserve<1653, Engine::Reserve<2038, Engine::Reserve<3411, Engine::Reserve<1913, Engine::Reserve<1663, Engine::Reserve<4782> > > > > > > > > > Memory; Engine::DelayLine ap1; Engine::DelayLine ap2; Engine::DelayLine ap3; Engine::DelayLine ap4; Engine::DelayLine dap1a; Engine::DelayLine dap1b; Engine::DelayLine del1; Engine::DelayLine dap2a; Engine::DelayLine dap2b; Engine::DelayLine del2; Engine::Context cL; Engine::Context cR; const float32_t kap = 0.8f; const float32_t klp = 0.7f; const float32_t krt = 0.75f; const float32_t gain = 0.55f; float32_t lp_1 = lp_decay_1_; float32_t lp_2 = lp_decay_2_; float32_t wet = 0.0f; float32_t apout = 0.0f; engine_L_.start(&cL); engine_R_.start(&cR); // Smear AP1 inside the loop. cL.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); cL.write(ap1, 100, 0.0f); cL.read(inL, gain); // Diffuse through 4 allpasses. cL.read(ap1 TAIL, kap); cL.writeAllPass(ap1, -kap); cL.read(ap2 TAIL, kap); cL.writeAllPass(ap2, -kap); cL.read(ap3 TAIL, kap); cL.writeAllPass(ap3, -kap); cL.read(ap4 TAIL, kap); cL.writeAllPass(ap4, -kap); cL.write(apout); // Main reverb loop. cL.load(apout); cL.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt); cL.lp(lp_1, klp); cL.read(dap1a TAIL, -kap); cL.writeAllPass(dap1a, kap); cL.read(dap1b TAIL, kap); cL.writeAllPass(dap1b, -kap); cL.write(del1, 1.5f); cL.write(wet, 0.0f); outL = wet; // Smear AP1 inside the loop. cR.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); cR.write(ap1, 100, 0.0f); cR.read(inL + inR, gain); // Diffuse through 4 allpasses. cR.read(ap1 TAIL, kap); cR.writeAllPass(ap1, -kap); cR.read(ap2 TAIL, kap); cR.writeAllPass(ap2, -kap); cR.read(ap3 TAIL, kap); cR.writeAllPass(ap3, -kap); cR.read(ap4 TAIL, kap); cR.writeAllPass(ap4, -kap); cR.write(apout); // Main reverb loop. cR.load(apout); // cR.interpolate(del1, 4450.0f, Engine::LFOIndex::LFO_1, 50.0f, krt); cR.read(del1 TAIL, krt); cR.lp(lp_2, klp); cR.read(dap2a TAIL, kap); cR.writeAllPass(dap2a, -kap); cR.read(dap2b TAIL, -kap); cR.writeAllPass(dap2b, kap); cR.write(del2, 1.5f); cR.write(wet, 0.0f); outR = wet; lp_decay_1_ = lp_1; lp_decay_2_ = lp_2; } TEST(LowLevel, TestStereoReverberatorAlgo) { const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); std::string full_test_name = test_info->test_case_name(); full_test_name += "."; full_test_name += test_info->name(); Engine engine_L_(SAMPLING_FREQUENCY); Engine engine_R_(SAMPLING_FREQUENCY); engine_L_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); engine_L_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); engine_L_.reset(); engine_R_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); engine_R_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); engine_R_.reset(); float32_t lp1 = 0.0f; float32_t lp2 = 0.0f; size_t size = 0; float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); float32_t* outSamplesL = new float32_t[size]; float32_t* outSamplesR = new float32_t[size]; memset(outSamplesL, 0, size * sizeof(float32_t)); memset(outSamplesR, 0, size * sizeof(float32_t)); for(size_t i = 0; i < size; ++i) { processReverberatorSample(engine_L_, engine_R_, i, lp1, lp2, inSamples[0][i], inSamples[1][i], outSamplesL[i], outSamplesR[i]); } saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); delete[] outSamplesL; delete[] outSamplesR; delete[] inSamples[0]; delete[] inSamples[1]; delete[] inSamples; } void processReverberatorSample( Engine& engine_, size_t index, float32_t& lp_decay_1_, float32_t& lp_decay_2_, float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) { // This is the Griesinger topology described in the Dattorro paper // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). // Modulation is applied in the loop of the first diffuser AP for additional // smearing; and to the two long delays for a slow shimmer/chorus effect. typedef Engine::Reserve< 113, Engine::Reserve< 162, Engine::Reserve< 241, Engine::Reserve< 399, Engine::Reserve<1653, Engine::Reserve<2038, Engine::Reserve<3411, Engine::Reserve<1913, Engine::Reserve<1663, Engine::Reserve<4782> > > > > > > > > > Memory; Engine::DelayLine ap1; Engine::DelayLine ap2; Engine::DelayLine ap3; Engine::DelayLine ap4; Engine::DelayLine dap1a; Engine::DelayLine dap1b; Engine::DelayLine del1; Engine::DelayLine dap2a; Engine::DelayLine dap2b; Engine::DelayLine del2; Engine::Context c; const float32_t kap = 0.8f; const float32_t klp = 0.7f; const float32_t krt = 0.75f; const float32_t gain = 0.55f; float32_t lp_1 = lp_decay_1_; float32_t lp_2 = lp_decay_2_; float32_t wet = 0.0f; float32_t apout = 0.0f; engine_.start(&c); // Smear AP1 inside the loop. c.interpolate(ap1, 10.0f, Engine::LFOIndex::LFO_1, 60.0f, 1.0f); c.writeAndLoad(ap1, 100, 0.0f); c.read(inL + inR, gain); // Diffuse through 4 allpasses. c.read(ap1 TAIL, kap); c.writeAllPass(ap1, -kap); c.read(ap2 TAIL, kap); c.writeAllPass(ap2, -kap); c.read(ap3 TAIL, kap); c.writeAllPass(ap3, -kap); c.read(ap4 TAIL, kap); c.writeAllPass(ap4, -kap); c.write(apout); // Main reverb loop. c.load(apout); c.interpolate(del2, 4680.0f, Engine::LFOIndex::LFO_2, 100.0f, krt); c.lp(lp_1, klp); c.read(dap1a TAIL, -kap); c.writeAllPass(dap1a, kap); c.read(dap1b TAIL, kap); c.writeAllPass(dap1b, -kap); c.write(del1, 2.0f); c.writeAndLoad(wet, 0.0f); outL = wet; c.load(apout); c.read(del1 TAIL, krt); c.lp(lp_2, klp); c.read(dap2a TAIL, kap); c.writeAllPass(dap2a, -kap); c.read(dap2b TAIL, -kap); c.writeAllPass(dap2b, kap); c.write(del2, 2.0f); c.writeAndLoad(wet, 0.0f); outR = wet; lp_decay_1_ = lp_1; lp_decay_2_ = lp_2; } TEST(LowLevel, TestMonoReverberatorAlgo) { const testing::TestInfo* test_info = testing::UnitTest::GetInstance()->current_test_info(); std::string full_test_name = test_info->test_case_name(); full_test_name += "."; full_test_name += test_info->name(); Engine engine_(SAMPLING_FREQUENCY); engine_.setLFOFrequency(Engine::LFOIndex::LFO_1, 0.5f); engine_.setLFOFrequency(Engine::LFOIndex::LFO_2, 0.3f); engine_.reset(); float32_t lp1 = 0.0f; float32_t lp2 = 0.0f; size_t size = 0; float32_t** inSamples = readWaveFile(AUDIO_SOURCE_FILE, size); float32_t* outSamplesL = new float32_t[size]; float32_t* outSamplesR = new float32_t[size]; memset(outSamplesL, 0, size * sizeof(float32_t)); memset(outSamplesR, 0, size * sizeof(float32_t)); for(size_t i = 0; i < size; ++i) { processReverberatorSample(engine_, i, lp1, lp2, inSamples[0][i], inSamples[1][i], outSamplesL[i], outSamplesR[i]); } saveWaveFile(getResultFile(full_test_name + ".wav", true), outSamplesL, outSamplesR, size, SAMPLING_FREQUENCY, 16); delete[] outSamplesL; delete[] outSamplesR; delete[] inSamples[0]; delete[] inSamples[1]; delete[] inSamples; }