#include #include "Analyzer.h" #include "asserts.h" #include "CHB.h" #include "EvenVCO.h" #include "FunVCO.h" #include "SawOscillator.h" #include "TestComposite.h" using EVCO = EvenVCO ; //using FUN = VoltageControlledOscillator<16, 16>; using CH = CHB; float desiredPitch(float octave, float tune, float cv1, float cv2, float mod) { float pitch = 1.0f + roundf(octave) + tune / 12.0f; pitch += cv1 + cv2; pitch += mod / 4.0f; float freq = 261.626f * powf(2.0f, pitch); // printf("theirs: pitch = %f exp = %f\n", pitch, freq); return freq; } float desiredPitchEv(const EVCO& vco) { #if 1 return desiredPitch( vco.params[(int) EVCO::OCTAVE_PARAM].value, vco.params[(int) EVCO::TUNE_PARAM].value, vco.inputs[(int) EVCO::PITCH1_INPUT].value, vco.inputs[(int) EVCO::PITCH2_INPUT].value, vco.inputs[(int) EVCO::FM_INPUT].value ); #else // This is just the original code as reference float pitch = 1.0f + roundf(vco.params[(int) EVCO::OCTAVE_PARAM].value) + vco.params[(int) EVCO::TUNE_PARAM].value / 12.0f; pitch += vco.inputs[(int) EVCO::PITCH1_INPUT].value + vco.inputs[(int) EVCO::PITCH2_INPUT].value; pitch += vco.inputs[(int) EVCO::FM_INPUT].value / 4.0f; float freq = 261.626f * powf(2.0f, pitch); // printf("theirs: pitch = %f exp = %f\n", pitch, freq); return freq; #endif } float desiredPitchCh(const CH& vco) { return desiredPitch( vco.params[(int) CH::PARAM_OCTAVE].value, vco.params[(int) CH::PARAM_TUNE].value, vco.inputs[(int) CH::CV_INPUT].value, 0, vco.inputs[(int) CH::PITCH_MOD_INPUT].value ); #if 0 float pitch = 1.0f + roundf(vco.params[(int) CH::PARAM_OCTAVE].value) + vco.params[(int) CH::PARAM_TUNE].value / 12.0f; pitch += vco.inputs[(int) CH::CV_INPUT].value; pitch += vco.inputs[(int) CH::PITCH_MOD_INPUT].value / 4.0f; // TODO: atenuverter on FM float freq = 261.626f * powf(2.0f, pitch); // printf("theirs: pitch = %f exp = %f\n", pitch, freq); printf("in desiredPitchCh oct=%f, tune=%f cv=%f, mode=%f\n", vco.params[(int) CH::PARAM_OCTAVE].value, vco.params[(int) CH::PARAM_TUNE].value, vco.inputs[(int) CH::CV_INPUT].value, vco.inputs[(int) CH::PITCH_MOD_INPUT].value); printf(" freq = %f\n", freq); return freq; #endif } static void testxEv(float octave, float tune = 0, float pitch1 = 0, float pitch2 = 0, float fm = 0) { EVCO vco; vco.params[(int) EVCO::OCTAVE_PARAM].value = octave; vco.params[(int) EVCO::TUNE_PARAM].value = tune; vco.inputs[(int) EVCO::PITCH1_INPUT].value = pitch1; vco.inputs[(int) EVCO::PITCH2_INPUT].value = pitch2; vco.inputs[(int) EVCO::FM_INPUT].value = fm; vco.outputs[(int) EVCO::SAW_OUTPUT].active = true; vco.outputs[(int) EVCO::EVEN_OUTPUT].active = false; vco.outputs[(int) EVCO::TRI_OUTPUT].active = false; vco.outputs[(int) EVCO::SQUARE_OUTPUT].active = false; vco.outputs[(int) EVCO::SINE_OUTPUT].active = false; vco.step(); const float desired = desiredPitchEv(vco); // printf("test, oct=%f, freq=%.2f desired=%.2f\n", octave, vco._freq, desired); if (desired > 20000) { // lookup table doesn't go past 20k. that's fine assertGE(vco._freq, 20000 - 1); } else { assertClose(vco._freq, desired, 1.5); // todo: make better tolerance } } static void testxCh(float octave, float tune = 0, float pitch1 = 0, float pitch2 = 0, float fm = 0) { CH vco; assert(pitch2 == 0); // ch doesn't have one vco.params[(int) CH::PARAM_OCTAVE].value = octave; vco.params[(int) CH::PARAM_TUNE].value = tune; vco.inputs[(int) CH::CV_INPUT].value = pitch1; // vco.inputs[(int) CH::PITCH2_INPUT].value = pitch2; vco.inputs[(int) CH::PITCH_MOD_INPUT].value = fm; vco.step(); const float desired = desiredPitchCh(vco); // printf("test, oct=%f, freq=%.2f desired=%.2f\n", octave, vco._freq, desired); if (desired > 20000) { // lookup table doesn't go past 20k. that's fine assertGE(vco._freq, 20000 - 1); } else { assertClose(vco._freq, desired, 1.5); // todo: make better tolerance } } static void testInitEv() { EVCO vco; vco.step(); const float desired = desiredPitchEv(vco); assertClose(vco._freq, desired, 1); // todo: tighten up } static void testInitCh() { CH vco; vco.step(); const float desired = desiredPitchCh(vco); assertClose(vco._freq, desired, 1); // todo: tighten up } static void testOctavesEv() { EVCO vco; for (int octave = -5; octave <= 4; ++octave) { testxEv(float(octave)); } } static void testOctavesCh() { CH vco; for (int octave = -5; octave <= 4; ++octave) { testxCh(float(octave)); } } // test that we go up to 20k static void testMaxFreqEv() { testxEv(4, 7, 0, 0); testxEv(4, 7, 1, 0); testxEv(4, 7, 0, 1); } static void testMaxFreqCh() { testxCh(4, 7, 0, 0); testxCh(4, 7, 1, 0); } static void testMinFreqEv() { testxEv(-5, -7, 0, 0); testxEv(-5, -7, -2, 0); } static void testMinFreqCh() { testxCh(-5, -7, 0, 0); testxCh(-5, -7, -2, 0); } static void testTuneEv() { testxEv(0, -7, 0, 0); testxEv(0, 7, 0, 0); } static void testTuneCh() { testxCh(0, -7, 0, 0); testxCh(0, 7, 0, 0); } static void testClamp() { assertEQ(std::clamp(12, 0, 14), 12); assertEQ(std::clamp(12, 0, 10), 10); assertEQ(std::clamp(12, 13, 15), 13); } #if 1 void testVCO() { testInitEv(); testInitCh(); testOctavesEv(); testOctavesCh(); testMaxFreqEv(); testMaxFreqCh(); testMinFreqEv(); testMinFreqCh(); testClamp(); testTuneEv(); testTuneCh(); } #else void testVCO() { testOctavesCh(); } #endif