#include #include #include #include #include "EvenVCO.h" //#include "EvenVCO_orig.h" #include "AudioMath.h" #include "BiquadParams.h" #include "BiquadFilter.h" #include "BiquadState.h" #include "ColoredNoise.h" #include "FrequencyShifter.h" #include "HilbertFilterDesigner.h" #include "LookupTableFactory.h" #include "TestComposite.h" #include "Tremolo.h" #include "VocalAnimator.h" #include "VocalFilter.h" #include "LFN.h" #include "GMR.h" #include "CHB.h" #include "FunVCOComposite.h" #include "EV3.h" #include "daveguide.h" #include "Shaper.h" #include "Super.h" #include "KSComposite.h" #include "Seq.h" using Shifter = FrequencyShifter; using Animator = VocalAnimator; using VocFilter = VocalFilter; using Colors = ColoredNoise; using Trem = Tremolo; #include "MeasureTime.h" #if defined(_MSC_VER) || defined(ARCH_WIN) double SqTime::frequency = 0; #endif // There are many tests that are disabled with #if 0. // In most cases they still work, but don't need to be run regularly #if 0 static void test1() { double d = .1; srand(57); const double scale = 1.0 / RAND_MAX; MeasureTime::run("test1 sin", []() { float x = std::sin(TestBuffers::get()); return x; }, 1); MeasureTime::run("test1 sin double", []() { float x = std::sin(TestBuffers::get()); return x; }, 1); MeasureTime::run("test1 sinx2 float", []() { float x = std::sin(TestBuffers::get()); x = std::sin(x); return x; }, 1); MeasureTime::run("mult float-10", []() { float x = TestBuffers::get(); float y = TestBuffers::get(); return x * y; }, 10); MeasureTime::run("mult dbl", []() { double x = TestBuffers::get(); double y = TestBuffers::get(); return x * y; }, 1); MeasureTime::run("div float", []() { float x = TestBuffers::get(); float y = TestBuffers::get(); return x / y; }, 1); MeasureTime::run("div dbl", []() { double x = TestBuffers::get(); double y = TestBuffers::get(); return x / y; }, 1); MeasureTime::run("test1 (do nothing)", [&d, scale]() { return TestBuffers::get(); }, 1); MeasureTime::run("test1 pow2 float", []() { float x = std::pow(2, TestBuffers::get()); return x; }, 1); MeasureTime::run("test1 pow rnd float", []() { float x = std::pow(TestBuffers::get(), TestBuffers::get()); return x; }, 1); MeasureTime::run("test1 exp float", []() { float x = std::exp(TestBuffers::get()); return x; }, 1); } #endif double overheadInOut = 0; double overheadOutOnly = 0; static void setup() { #ifdef _DEBUG // assert(false); // don't run this in debug #endif double d = .1; const double scale = 1.0 / RAND_MAX; overheadInOut = MeasureTime::run(0.0, "test1 (do nothing i/o)", [&d, scale]() { return TestBuffers::get(); }, 1); overheadOutOnly = MeasureTime::run(0.0, "test1 (do nothing oo)", [&d, scale]() { return 0.0f; }, 1); } template static void testHilbert() { BiquadParams paramsSin; BiquadParams paramsCos; BiquadState state; HilbertFilterDesigner::design(44100, paramsSin, paramsCos); MeasureTime::run("hilbert", [&state, ¶msSin]() { T d = BiquadFilter::run(TestBuffers::get(), state, paramsSin); return d; }, 1); } #if 0 static void testExpRange() { using T = float; LookupTableParams table; LookupTableFactory::makeExp2(table); MeasureTime::run("exp lookup", [&table]() { T d = LookupTable::lookup(table, TestBuffers::get()); return d; }, 1); } #endif static void testShifter() { Shifter fs; fs.setSampleRate(44100); fs.init(); fs.inputs[Shifter::AUDIO_INPUT].value = 0; assert(overheadInOut >= 0); MeasureTime::run(overheadInOut, "shifter", [&fs]() { fs.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); fs.step(); return fs.outputs[Shifter::SIN_OUTPUT].value; }, 1); } static void testAnimator() { Animator an; an.setSampleRate(44100); an.init(); an.inputs[Shifter::AUDIO_INPUT].value = 0; MeasureTime::run(overheadInOut, "animator", [&an]() { an.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); an.step(); return an.outputs[Shifter::SIN_OUTPUT].value; }, 1); } static void testVocalFilter() { VocFilter an; an.setSampleRate(44100); an.init(); an.inputs[Shifter::AUDIO_INPUT].value = 0; MeasureTime::run(overheadInOut, "vocal filter", [&an]() { an.inputs[Shifter::AUDIO_INPUT].value = TestBuffers::get(); an.step(); return an.outputs[Shifter::SIN_OUTPUT].value; }, 1); } static void testColors() { Colors co; co.setSampleRate(44100); co.init(); MeasureTime::run(overheadInOut, "colors", [&co]() { co.step(); return co.outputs[Colors::AUDIO_OUTPUT].value; }, 1); } static void testTremolo() { Trem tr; tr.setSampleRate(44100); tr.init(); MeasureTime::run(overheadInOut, "trem", [&tr]() { tr.inputs[Trem::AUDIO_INPUT].value = TestBuffers::get(); tr.step(); return tr.outputs[Trem::AUDIO_OUTPUT].value; }, 1); } static void testLFN() { LFN lfn; lfn.setSampleTime(1.0f / 44100.f); lfn.init(); MeasureTime::run(overheadOutOnly, "lfn", [&lfn]() { lfn.step(); return lfn.outputs[LFN::OUTPUT].value; }, 1); } #if 0 static void testEvenOrig() { EvenVCO_orig lfn; lfn.outputs[EvenVCO_orig::EVEN_OUTPUT].active = true; lfn.outputs[EvenVCO_orig::SINE_OUTPUT].active = true; lfn.outputs[EvenVCO_orig::TRI_OUTPUT].active = true; lfn.outputs[EvenVCO_orig::SQUARE_OUTPUT].active = true; lfn.outputs[EvenVCO_orig::SAW_OUTPUT].active = true; for (int i = 0; i < 100; ++i) lfn.step(); MeasureTime::run(overheadOutOnly, "Even orig", [&lfn]() { lfn.inputs[EvenVCO_orig::PITCH1_INPUT].value = TestBuffers::get(); lfn.step(); return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; }, 1); } #endif static void testEven() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = true; lfn.outputs[EvenVCO::SINE_OUTPUT].active = true; lfn.outputs[EvenVCO::TRI_OUTPUT].active = true; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; MeasureTime::run(overheadOutOnly, "Even, all outs", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; }, 1); } static void testEvenEven() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = true; lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; MeasureTime::run(overheadOutOnly, "Even, even only", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::EVEN_OUTPUT].value; }, 1); } static void testEvenSin() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; lfn.outputs[EvenVCO::SINE_OUTPUT].active = true; lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; MeasureTime::run(overheadOutOnly, "Even, sin only", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::SAW_OUTPUT].value; }, 1); } static void testEvenSaw() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; for (int i = 0; i < 100; ++i) lfn.step(); MeasureTime::run(overheadOutOnly, "Even, saw only", [&lfn]() { lfn.inputs[EvenVCO::PITCH1_INPUT].value = TestBuffers::get(); lfn.step(); return lfn.outputs[EvenVCO::SAW_OUTPUT].value; }, 1); } static void testEvenTri() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; lfn.outputs[EvenVCO::TRI_OUTPUT].active = true; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = false; lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; MeasureTime::run(overheadOutOnly, "Even, tri only", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::TRI_OUTPUT].value; }, 1); } static void testEvenSq() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; lfn.outputs[EvenVCO::SAW_OUTPUT].active = false; MeasureTime::run(overheadOutOnly, "Even, Sq only", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::TRI_OUTPUT].value; }, 1); } static void testEvenSqSaw() { EvenVCO lfn; lfn.outputs[EvenVCO::EVEN_OUTPUT].active = false; lfn.outputs[EvenVCO::SINE_OUTPUT].active = false; lfn.outputs[EvenVCO::TRI_OUTPUT].active = false; lfn.outputs[EvenVCO::SQUARE_OUTPUT].active = true; lfn.outputs[EvenVCO::SAW_OUTPUT].active = true; MeasureTime::run(overheadOutOnly, "Even, Sq Saw", [&lfn]() { lfn.step(); return lfn.outputs[EvenVCO::TRI_OUTPUT].value; }, 1); } static void testFun() { FunVCOComposite lfn; for (int i = 0; i < lfn.NUM_OUTPUTS; ++i) { lfn.outputs[i].active = true; } lfn.setSampleRate(44100.f); const bool isAnalog = false; lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; MeasureTime::run(overheadOutOnly, "Fun all on, digital", [&lfn]() { lfn.step(); return lfn.outputs[FunVCOComposite::TRI_OUTPUT].value + lfn.outputs[FunVCOComposite::SAW_OUTPUT].value + lfn.outputs[FunVCOComposite::SIN_OUTPUT].value + lfn.outputs[FunVCOComposite::SQR_OUTPUT].value; }, 1); } static void testFunNone() { FunVCOComposite lfn; for (int i = 0; i < lfn.NUM_OUTPUTS; ++i) { lfn.outputs[i].active = false; } lfn.setSampleRate(44100.f); MeasureTime::run(overheadOutOnly, "Fun all off", [&lfn]() { lfn.step(); return lfn.outputs[FunVCOComposite::TRI_OUTPUT].value; }, 1); } static void testFunSaw(bool isAnalog) { FunVCOComposite lfn; lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = false; lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = false; lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = true; // oscillator.analog = TBase::params[MODE_PARAM].value > 0.0f; lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; lfn.setSampleRate(44100.f); std::string title = isAnalog ? "Fun Saw Analog" : "Fun Saw Digital"; MeasureTime::run(overheadOutOnly, title.c_str(), [&lfn]() { lfn.step(); return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; }, 1); } static void testFunSin(bool isAnalog) { FunVCOComposite lfn; lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = true; lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = false; lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = false; lfn.params[FunVCOComposite::MODE_PARAM].value = isAnalog ? 1.0f : 0.f; lfn.setSampleRate(44100.f); std::string title = isAnalog ? "Fun Sin Analog" : "Fun Sin Digital"; MeasureTime::run(overheadOutOnly, title.c_str(), [&lfn]() { lfn.step(); return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; }, 1); } static void testFunSq() { FunVCOComposite lfn; lfn.outputs[FunVCOComposite::SIN_OUTPUT].active = false; lfn.outputs[FunVCOComposite::TRI_OUTPUT].active = false; lfn.outputs[FunVCOComposite::SQR_OUTPUT].active = true; lfn.outputs[FunVCOComposite::SAW_OUTPUT].active = false; lfn.setSampleRate(44100.f); MeasureTime::run(overheadOutOnly, "Fun sq", [&lfn]() { lfn.step(); return lfn.outputs[FunVCOComposite::SAW_OUTPUT].value; }, 1); } static void testCHBdef() { CHB chb; std::string name = "chbdef "; MeasureTime::run(overheadOutOnly, name.c_str(), [&chb]() { chb.step(); return chb.outputs[CHB::MIX_OUTPUT].value; }, 1); } static void testEV3() { EV3 ev3; MeasureTime::run(overheadOutOnly, "ev3", [&ev3]() { ev3.step(); return ev3.outputs[EV3::MIX_OUTPUT].value; }, 1); } static void testGMR() { GMR gmr; gmr.setSampleRate(44100); gmr.init(); MeasureTime::run(overheadOutOnly, "gmr", [&gmr]() { gmr.step(); return gmr.outputs[GMR::TRIGGER_OUTPUT].value; }, 1); } static void testDG() { Daveguide gmr; // gmr.setSampleRate(44100); // gmr.init(); MeasureTime::run(overheadOutOnly, "dg", [&gmr]() { gmr.step(); return gmr.outputs[GMR::TRIGGER_OUTPUT].value; }, 1); } // 95 // down to 67 for just the oversampler. static void testShaper1a() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_OVERSAMPLE].value = 0; gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; MeasureTime::run(overheadOutOnly, "shaper fw 16X", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } static void testShaper1b() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; gmr.params[Shaper::PARAM_OVERSAMPLE].value = 1; MeasureTime::run(overheadOutOnly, "shaper fw 4X", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } static void testSuper() { Super super; MeasureTime::run(overheadOutOnly, "super", [&super]() { super.step(); return super.outputs[Super::MAIN_OUTPUT].value; }, 1); } static void testSuper2() { Super super; super.params[Super::CLEAN_PARAM].value = 1; MeasureTime::run(overheadOutOnly, "super clean", [&super]() { super.step(); return super.outputs[Super::MAIN_OUTPUT].value; }, 1); } static void testSuper3() { Super super; super.params[Super::CLEAN_PARAM].value = 2; MeasureTime::run(overheadOutOnly, "super clean 2", [&super]() { super.step(); return super.outputs[Super::MAIN_OUTPUT].value; }, 1); } #if 0 static void testSuper2() { Super super; int counter = 1; bool flip = false; float cv = 0; MeasureTime::run(overheadOutOnly, "super pitch change", [&]() { if (--counter == 0) { cv = flip ? 1.f : -1.f; super.inputs[Super::CV_INPUT].value = cv; counter = 64; flip = !flip; } super.step(); return super.outputs[Super::MAIN_OUTPUT].value; }, 1); } #endif static void testKS() { KSComposite gmr; MeasureTime::run(overheadOutOnly, "ks", [&gmr]() { // gmr.inputs[KSComposite::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[KSComposite::SQR_OUTPUT].value; }, 1); } static void testShaper1c() { Shaper gmr; gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::FullWave; gmr.params[Shaper::PARAM_OVERSAMPLE].value = 2; MeasureTime::run(overheadOutOnly, "shaper fw 1X", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } // 284 static void testShaper2() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Crush; MeasureTime::run(overheadOutOnly, "shaper crush", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } // 143 static void testShaper3() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::AsymSpline; MeasureTime::run(overheadOutOnly, "shaper asy", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } static void testShaper4() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Fold; MeasureTime::run(overheadOutOnly, "folder", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } static void testShaper5() { Shaper gmr; // gmr.setSampleRate(44100); // gmr.init(); gmr.params[Shaper::PARAM_SHAPE].value = (float) Shaper::Shapes::Fold2; MeasureTime::run(overheadOutOnly, "folder II", [&gmr]() { gmr.inputs[Shaper::INPUT_AUDIO].value = TestBuffers::get(); gmr.step(); return gmr.outputs[Shaper::OUTPUT_AUDIO].value; }, 1); } #if 0 static void testAttenuverters() { auto scaler = AudioMath::makeLinearScaler(-2, 2); MeasureTime::run("linear scaler", [&scaler]() { float cv = TestBuffers::get(); float knob = TestBuffers::get(); float trim = TestBuffers::get(); return scaler(cv, knob, trim); }, 1); LookupTableParams lookup; LookupTableFactory::makeBipolarAudioTaper(lookup); MeasureTime::run("bipolar lookup", [&lookup]() { float x = TestBuffers::get(); return LookupTable::lookup(lookup, x); }, 1); // auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory::audioTaperKnee()); { auto bipolarScaler = [&lookup, &scaler](float cv, float knob, float trim) { float scaledTrim = LookupTable::lookup(lookup, cv); return scaler(cv, knob, scaledTrim); }; MeasureTime::run("bipolar scaler", [&bipolarScaler]() { float cv = TestBuffers::get(); float knob = TestBuffers::get(); float trim = TestBuffers::get(); return bipolarScaler(cv, knob, trim); }, 1); } } #endif #if 0 static void testNoise(bool useDefault) { std::default_random_engine defaultGenerator{99}; std::random_device rd{}; std::mt19937 gen{rd()}; std::normal_distribution distribution{0, 1.0}; std::string title = useDefault ? "default random" : "fancy random"; MeasureTime::run(overheadOutOnly, title.c_str(), [useDefault, &distribution, &defaultGenerator, &gen]() { if (useDefault) return distribution(defaultGenerator); else return distribution(gen); }, 1); } static uint64_t xoroshiro128plus_state[2] = {}; static uint64_t rotl(const uint64_t x, int k) { return (x << k) | (x >> (64 - k)); } static uint64_t xoroshiro128plus_next(void) { const uint64_t s0 = xoroshiro128plus_state[0]; uint64_t s1 = xoroshiro128plus_state[1]; const uint64_t result = s0 + s1; s1 ^= s0; xoroshiro128plus_state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b xoroshiro128plus_state[1] = rotl(s1, 36); // c return result; } float randomUniformX() { // 24 bits of granularity is the best that can be done with floats while ensuring that the return value lies in [0.0, 1.0). return (xoroshiro128plus_next() >> (64 - 24)) / powf(2, 24); } float randomNormalX() { // Box-Muller transform float radius = sqrtf(-2.f * logf(1.f - randomUniformX())); float theta = 2.f * M_PI * randomUniformX(); return radius * sinf(theta); // // Central Limit Theorem // const int n = 8; // float sum = 0.0; // for (int i = 0; i < n; i++) { // sum += randomUniform(); // } // return (sum - n / 2.f) / sqrtf(n / 12.f); } static void testNormal() { MeasureTime::run(overheadOutOnly, "normal", []() { return randomNormalX(); }, 1); } #endif void dummy() { Seq s; } void perfTest() { printf("starting perf test\n"); fflush(stdout); setup(); #if 0 testColors(); testVocalFilter(); testAnimator(); testTremolo(); testLFN(); testShifter(); testGMR(); #endif testCHBdef(); testSuper(); testSuper2(); testSuper3(); // testKS(); // testShaper1a(); #if 0 testShaper1b(); testShaper1c(); testShaper2(); testShaper3(); testShaper4(); testShaper5(); #endif testEV3(); testFunSaw(true); #if 0 testFunSaw(false); testFunSin(true); testFunSin(false); testFunSq(); testFun(); testFunNone(); #endif // testEvenOrig(); testEvenSaw(); #if 0 testEven(); testEvenEven(); testEvenSin(); testEvenSaw(); testEvenTri(); testEvenSq(); testEvenSqSaw(); #endif // test1(); #if 0 testHilbert(); testHilbert(); #endif }