|  | #include "asserts.h"
#include "ExtremeTester.h"
#include "VocalAnimator.h"
#include "TestComposite.h"
#include "VocalFilter.h"
#include "FormantTables2.h"
using Animator = VocalAnimator<TestComposite>;
/**
 * Verify no output with no input.
 */
static void test0()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    anim.outputs[Animator::AUDIO_OUTPUT].value = 0;
    anim.step();                // prime it
                                // with no input, should have no output
    for (int i = 0; i < 50; ++i) {
        anim.step();
        assert(anim.outputs[Animator::AUDIO_OUTPUT].value == 0);
    }
}
/**
 * Verify output with input.
 */
static void test1()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    anim.outputs[Animator::AUDIO_OUTPUT].value = 0;
    anim.inputs[Animator::AUDIO_INPUT].value = 1;
    anim.step();                // prime it
                                // with  input, should have  output
    for (int i = 0; i < 50; ++i) {
        anim.step();
        assert(anim.outputs[Animator::AUDIO_OUTPUT].value != 0);
    }
}
/**
 * Verify filter settings with no mod.
 */
static void test2()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    for (int i = 0; i < 4; ++i) {
        float freq = anim.normalizedFilterFreq[i] * 44100;
        assertEQ(freq, anim.nominalFilterCenterHz[i]);
    }
}
/**
* Verify filter settings respond to Fc.
*/
static void test3()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.step();
    for (int i = 0; i < 4; ++i) {
       // assert(anim.filterFrequency[i] == anim.nominalFilterCenter[i]);
        float freq = anim.normalizedFilterFreq[i] * 44100;
        assertClose(freq, anim.nominalFilterCenterHz[i], 1);
    }
    anim.params[anim.FILTER_FC_PARAM].value = 1;
    anim.step();
    // assert that when we shift up, the expected values shift up
    for (int i = 0; i < 4; ++i) {
        float freq = anim.normalizedFilterFreq[i] * 44100;
        //printf("i=%d, freq=%f, nominal=%f\n", i, freq, anim.nominalFilterCenterHz[i]);
        if (i == 3) {
            assertClose(freq, anim.nominalFilterCenterHz[i], 1);
        } else
            assert(freq > anim.nominalFilterCenterHz[i]);
    }
#if 0
    anim.params[anim.FILTER_FC_PARAM].value = -1;
    anim.step();
    for (int i = 0; i < 4; ++i) {
        if (i == 3)
            assert(anim.filterFrequency[i] == anim.nominalFilterCenter[i]);
        else
            assert(anim.filterFrequency[i] < anim.nominalFilterCenter[i]);
    }
#endif
}
static void testScalers()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    // cv/knob, trim
    // cases with no CV
    assertClose(.5, anim.scale0_1(0, 0, 1), .001);              // knob half, full trim
    assertClose(.5, anim.scale0_1(0, 0, -1), .001);             // knob half, full neg trim
    assertClose(1, anim.scale0_1(0, 5, 0), .001);               // knob full
    assertClose(0, anim.scale0_1(0, -5, 0), .001);              // knob down full
    assertClose(.75, anim.scale0_1(0, (5.0f * .5f), 0), .001); // knob 3/4
    // CV, no knob
    assertClose(1, anim.scale0_1(5, 0, 1), .001);              // full cv, untrimmed
    assertClose(0, anim.scale0_1(-5, 0, 1), .001);              // full cv, untrimmed
    assertClose(.25, anim.scale0_1((-5.0f * .5f), 0, 1), .001);       // 3/4 cv, untrimmed
   // assertClose(.75, anim.scale0_1(5, 0, .5f), .001);           // full cv, half trim
    assertClose(0, anim.scale0_1(5, 0, -1), .001);              // full cv, full neg trim
}
#if 0
static void dump(const char * msg, const Animator& anim)
{
    std::cout << "dumping " << msg << "\nfiltFreq"
        << " " << std::pow(2, anim.filterFrequencyLog[0])
        << " " << std::pow(2, anim.filterFrequencyLog[1])
        << " " << std::pow(2, anim.filterFrequencyLog[2])
        << " " << std::pow(2, anim.filterFrequencyLog[3])
        << std::endl;
}
static void x()
{
    Animator anim;
    anim.setSampleRate(44100);
    anim.init();
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.step();
    dump("init", anim);
    // TODO: assert here
    anim.params[anim.FILTER_FC_PARAM].value = 5;
    anim.step();
    dump("fc 5", anim);
    anim.params[anim.FILTER_FC_PARAM].value = -5;
    anim.step();
    dump("fc -5", anim);
    std::cout << "\nabout to modulate up. maxLFO, def depth\n";
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 0;
    anim.jamModForTest = true;
    anim.modValueForTest = 5;
    anim.step();
    dump("max up def", anim);
    std::cout << "\nabout to modulate up. minLFO, def depth\n";
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 0;
    anim.jamModForTest = true;
    anim.modValueForTest = -5;
    anim.step();
    dump("max down def", anim);
    std::cout << "\nabout to modulate up. maxLFO, max depth\n";
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
    anim.jamModForTest = true;
    anim.modValueForTest = 5;
    anim.step();
    dump(" modulate up. maxLFO, max depthf", anim);
    std::cout << "\nabout to modulate down. minLFO, max depth\n";
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
    anim.jamModForTest = true;
    anim.modValueForTest = -5;
    anim.step();
    dump(" modulate up. maxLFO, max depthf", anim);
#if 0
    // TODO: would be nice to be able to inject an LFO voltage
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = 5;
    for (int i = 0; i < 40000; ++i) {
        anim.step();
    }
    dump("fc 0 depth 1", anim);
    std::cout << "about to to depth -\n";
    // TODO: would be nice to be able to inject an LFO voltage
    anim.params[anim.FILTER_FC_PARAM].value = 0;
    anim.params[anim.FILTER_MOD_DEPTH_PARAM].value = -5;
    for (int i = 0; i < 4000; ++i) {
        anim.step();
    }
    dump("fc 0 depth -5", anim);
#endif
}
#endif
/**
*Interpolates the frequency using lookups
* @param model = 0(bass) 1(tenor) 2(countertenor) 3(alto) 4(soprano)
* @param index = 0..4 (formant F1..F5)
* @param vowel is the continuous index into the per / vowel lookup tables(0..4)
* 0 = a, 1 = e, 2 = i, 3 = o 4 = u
*/
//float getLogFrequency(int model, int index, float vowel)
static void testFormantTables()
{
    FormantTables2 ff;
    float x = ff.getLogFrequency(0, 0, 0);
    assert(x > 0);
    x = ff.getNormalizedBandwidth(0, 0, 0);
    assert(x > 0);
    x = ff.getGain(0, 0, 0);
#if 1 // store DB, not gain
    assert(x <= 0);
    assert(x >= -62);
#else
    assert(x > 0)
#endif
    // spot check a few freq
    // formant F2 of alto, 'u' 
        x = ff.getLogFrequency(3, 1, 4);
    assertClose(x, std::log2(700), .0001);
    // formant F3 of soprano, 'o' 
    x = ff.getLogFrequency(4, 2, 3);
    assertClose(x, std::log2(2830), .0001);
}
static void testFormantTables2()
{
    FormantTables2 ff;
    for (int model = 0; model < FormantTables2::numModels; ++model) {
        for (int formantBand = 0; formantBand < FormantTables2::numFormantBands; ++formantBand) {
            for (int vowel = 0; vowel < FormantTables2::numVowels; ++vowel) {
                const float f = ff.getLogFrequency(model, formantBand, float(vowel));
                // check that the frequencies are possible formants
                assert(std::pow(2, f) > 100);
                assert(std::pow(2, f) < 5500);
                const float nBw = ff.getNormalizedBandwidth(model, formantBand, float(vowel));
                assert(nBw < .5);
                assert(nBw > .01);
                // db now
                const float gain = ff.getGain(model, formantBand, float(vowel));
                assertLE(gain, 0);
                assertGT(gain, -70);
               // assertLE(gain, 1);
              //  assert(gain > 0);
            }
        }
    }
}
static void testVocalFilter()
{
    VocalFilter<TestComposite> vf;
    vf.setSampleRate(44100);
    vf.init();
    vf.outputs[VocalFilter<TestComposite>::AUDIO_OUTPUT].value = 0;
    vf.inputs[VocalFilter<TestComposite>::AUDIO_INPUT].value = 1;
    vf.step();                // prime it
                                // with  input, should have  output
    for (int i = 0; i < 50; ++i) {
        vf.step();
        assert(vf.outputs[VocalFilter<TestComposite>::AUDIO_OUTPUT].value != 0);
    }
}
static void testInputExtremes()
{
    VocalAnimator<TestComposite> va;
    va.setSampleRate(44100);
    va.init();
    using fp = std::pair<float, float>;
    std::vector< std::pair<float, float> > paramLimits;
    paramLimits.resize(va.NUM_PARAMS);
    paramLimits[va.LFO_RATE_PARAM] = fp(-5.0f, 5.0f);
  //  paramLimits[va.LFO_SPREAD_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.FILTER_FC_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.FILTER_Q_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.FILTER_MOD_DEPTH_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.LFO_RATE_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.FILTER_Q_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.FILTER_FC_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.FILTER_MOD_DEPTH_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.BASS_EXP_PARAM] = fp(0.f, 1.0f);
    paramLimits[va.TRACK_EXP_PARAM] = fp(0.f, 2.0f);
    paramLimits[va.LFO_MIX_PARAM] = fp(0.f, 1.0f);
    // TODO: why is output going so high?
    ExtremeTester< VocalAnimator<TestComposite>>::test(va, paramLimits, false, "vocal animator");
}
static void testVocalExtremes()
{
    VocalFilter<TestComposite> va;
    va.setSampleRate(44100);
    va.init();
    using fp = std::pair<float, float>;
    std::vector< std::pair<float, float> > paramLimits;
    paramLimits.resize(va.NUM_PARAMS);
    paramLimits[va.FILTER_Q_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.FILTER_Q_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.FILTER_FC_PARAM] = fp(-5.0f, 5.0f);
    paramLimits[va.FILTER_FC_TRIM_PARAM] = fp(-1.0f, 1.0f);
    paramLimits[va.FILTER_VOWEL_PARAM] = fp(-5.f, 5.0f);
    paramLimits[va.FILTER_VOWEL_TRIM_PARAM] = fp(-1.f, 1.0f);
    paramLimits[va.FILTER_MODEL_SELECT_PARAM] = fp(0.f, 4.0f);
    paramLimits[va.FILTER_BRIGHTNESS_PARAM] = fp(-5.f, 5.0f);
    paramLimits[va.FILTER_BRIGHTNESS_TRIM_PARAM] = fp(-1.0f, 1.0f);
    ExtremeTester< VocalFilter<TestComposite>>::test(va, paramLimits, false, "vocal filter");
}
void testVocalAnimator()
{
    test0();
    test1();
    test2();
    test3();
    testScalers();
    testFormantTables();
    testFormantTables2();
    testVocalFilter();
#if defined(_DEBUG) && true
    printf("skipping extremes\n");
#else
    testVocalExtremes();
    testInputExtremes();
#endif
}
 |