#include "GenerativeTriggerGenerator.h" #include "StochasticGrammar.h" #include "TriggerSequencer.h" #include #include #include //#include static const int numRules = fullRuleTableSize; typedef GKEY(*INITFN)(); static ProductionRule rules[numRules]; // Test basic integrity of key data static void test0() { GKEY key; GKEY outKeys[ProductionRuleKeys::bufferSize]; for (key = sg_first; key <= sg_last; ++key) { ProductionRuleKeys::breakDown(key, outKeys); for (GKEY* p = outKeys; *p != sg_invalid; ++p) { assert(*p >= sg_first); assert(*p <= sg_last); } std::string s = ProductionRuleKeys::toString(key); assert(!s.empty()); assert(s.length() < 256); int dur = ProductionRuleKeys::getDuration(key); assert(dur > 0); assert(dur <= PPQ * 8); } } // test all the gkeys void testAllKeys() { const int siz = ProductionRuleKeys::bufferSize; GKEY buffer[siz]; for (GKEY gk = sg_first; gk <= sg_last; ++gk) { // printf("testing key %d\n", gk); // printf("to string: %s\n", ProductionRuleKeys::toString(gk)); const int dur = ProductionRuleKeys::getDuration(gk); ProductionRuleKeys::breakDown(gk, buffer); int sum = 0; for (GKEY * p = buffer; *p != sg_invalid; ++p) { // printf("adding to sum %d\n", ProductionRuleKeys::getDuration(*p)); sum += ProductionRuleKeys::getDuration(*p); } // printf("dur = %d sum = %d (should be the same)\n", dur, sum); assert(dur == sum); } } /************************************************************************************** * Make some simple grammars and test them **************************************************************************************/ #ifdef _DEBUG void gdt0() { { static ProductionRule rules[numRules]; bool b = ProductionRule::isGrammarValid(rules, numRules, sg_invalid); assert(!b); } { // throw in a positive case static ProductionRule rules[numRules]; ProductionRule& r = rules[sg_w]; r.entries[0].probability = 1.0f; r.entries[0].code = sg_invalid; bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); assert(b); } { // terminal code wrong static ProductionRule rules[numRules]; ProductionRule& r = rules[sg_w]; r.entries[0].probability = 1.0f; r.entries[0].code = sg_q; bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); assert(!b); } { // bad order of probability static ProductionRule rules[numRules]; ProductionRule& r = rules[sg_w]; r.entries[0].probability = 1.0f; r.entries[0].code = sg_q; bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); assert(!b); } { // rule branches to nowhere static ProductionRule rules[numRules]; // break w2 into w,w prob 100 ProductionRule& r = rules[sg_w2]; r.entries[0].probability = 1.0f; r.entries[0].code = sg_ww; bool b = ProductionRule::isGrammarValid(rules, numRules, sg_w); assert(!b); } } #endif class TestEvaluator : public ProductionRule::EvaluationState { public: TestEvaluator(AudioMath::RandomUniformFunc xr) : ProductionRule::EvaluationState(xr) { } void writeSymbol(GKEY key) override { keys.push_back(key); } int getNumSymbols() { //printf("final keys: "); // for (size_t i = 0; i< keys.size(); ++i) printf("%s, ", ProductionRuleKeys::toString(keys[i])); // printf("\n"); return (int) keys.size(); } private: std::vector keys; }; /** * simplest possible grammar. */ static GKEY init0() { // printf("called init0\n"); // This rule always generate sg-w2 (two whole notes tied together) ProductionRule& r = rules[sg_w2]; r.entries[0].probability = 1; r.entries[0].code = sg_invalid; // terminate expansion return sg_w2; } /** * Simple grammar with a rule but no random. */ static GKEY init1() { { // start with w2 duration ProductionRule& r = rules[sg_w2]; // break into w,w prob 100 r.entries[0].probability = 1.0f; r.entries[0].code = sg_ww; } { // now need rule for w hole //printf("in init1 making 100 for %d\n", sg_w); ProductionRule& r = rules[sg_w]; r.entries[0].probability = 1.0f; r.entries[1].code = sg_invalid; } //printf("leave init 1. rule 1 p0 = %f\n", rules[sg_w2].entries[0].probability); return sg_w2; } /** * Simple grammar with randomness initializer */ static GKEY init2() { { // start with w2 duration ProductionRule& r = rules[sg_w2]; // break into w,w prob 50 r.entries[0].probability = .5f; r.entries[0].code = sg_ww; r.entries[1].probability = 1.0f; r.entries[1].code = sg_invalid; // always terminate } { // now need rule for w hole ProductionRule& r = rules[sg_w]; r.entries[1].probability = 1.0f; r.entries[1].code = sg_invalid; // always terminate } return sg_w2; } #ifdef _DEBUG static void testGrammarSub(INITFN f) { GKEY init = f(); bool b = ProductionRule::isGrammarValid(rules, numRules, init); assert(b); TestEvaluator es(AudioMath::random()); es.rules = rules; es.numRules = numRules; ProductionRule::evaluate(es, init); assert(es.getNumSymbols() > 0); } #endif /********************************************************************************************************* * TriggerSequencer **********************************************************************************************************/ // test event at zero fires at zero static void ts0() { TriggerSequencer::Event seq[] = { {TriggerSequencer::TRIGGER, 0}, {TriggerSequencer::END, 100} }; TriggerSequencer ts(seq); ts.clock(); assert(ts.getTrigger()); ts.clock(); assert(!ts.getTrigger()); } // test trigger at 1 happens at 1 static void ts1() { TriggerSequencer::Event seq[] = { {TriggerSequencer::TRIGGER, 1}, {TriggerSequencer::END, 0} }; TriggerSequencer ts(seq); ts.clock(); assert(!ts.getTrigger()); ts.clock(); assert(ts.getTrigger()); ts.clock(); assert(!ts.getTrigger()); ts.clock(); assert(!ts.getTrigger()); } // 4 clock loop: delay 4, trigger, end static void ts2() { TriggerSequencer::Event seq[] = { {TriggerSequencer::TRIGGER, 4}, {TriggerSequencer::END, 0} }; TriggerSequencer ts(seq); bool firstTime = true; // first time through, 4 clocks of nothing. then clock, 0,0,0 for (int i = 0; i < 4; ++i) { ts.clock(); if (firstTime) { assert(!ts.getTrigger()); assert(!ts.getEnd()); firstTime = false; } else { //printf("second time around, t=%d e=%d\n", ts.getTrigger(), ts.getEnd()); // second time around we finally see the trigger assert(ts.getTrigger()); // second time around, need to clock the end of the last time assert(ts.getEnd()); ts.reset(seq); // start it up again assert(!ts.getTrigger()); // resetting should not set us up for a trigger } ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); ts.clock(); assert(!ts.getTrigger()); // assert(ts.getEnd()); // ts.reset(seq); } } // test trigger seq qith // 4 clock loop: trigger, delay 4 end static void ts3() { TriggerSequencer::Event seq[] = { {TriggerSequencer::TRIGGER, 0}, {TriggerSequencer::END, 4} }; TriggerSequencer ts(seq); bool firstLoop = true; for (int i = 0; i < 4; ++i) { //printf("--- loop ----\n"); // 1 ts.clock(); if (firstLoop) { assert(ts.getTrigger()); // we just primed loop at top, so it's got a ways assert(!ts.getEnd()); firstLoop = false; } else { // second time around, need to clock the end of the last time assert(ts.getEnd()); ts.reset(seq); // start it up again assert(ts.getTrigger()); // resetting should have set us up for a trigger } // 2 ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); // 3 ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); // 4 ts.clock(); assert(!ts.getTrigger()); assert(!ts.getEnd()); } } // test trigger seq with straight ahead 4/4 as generated by a grammar static void ts4() { TriggerSequencer::Event seq[] = { {TriggerSequencer::TRIGGER, 0}, {TriggerSequencer::TRIGGER, 4}, {TriggerSequencer::TRIGGER, 4}, {TriggerSequencer::TRIGGER, 4}, {TriggerSequencer::END, 4} }; TriggerSequencer ts(seq); //bool firstLoop = true; for (int i = 0; i < 100; ++i) { bool firstTime = (i == 0); // repeating pattern of trigg, no, no, no for (int j = 0; j < 4; ++j) { for (int k = 0; k < 4; ++k) { // printf("test loop, i=%d, j=%d, k=%d\n", i, j, k); ts.clock(); bool expectEnd = (k == 0) && (j == 0) && !firstTime; assert(ts.getEnd() == expectEnd); if (ts.getEnd()) { ts.reset(seq); } assert(ts.getTrigger() == (k == 0)); } } } } /******************************************************************************* ** StochasticGrammarDictionary */ #ifdef _DEBUG void gdt1() { assert(StochasticGrammarDictionary::getNumGrammars() > 0); for (int i = 0; i < StochasticGrammarDictionary::getNumGrammars(); ++i) { StochasticGrammarDictionary::Grammar g = StochasticGrammarDictionary::getGrammar(i); bool b = ProductionRule::isGrammarValid(g.rules, g.numRules, g.firstRule); assert(b); } } #endif /******************************************************************************************** * GenerativeTriggerGenerator **********************************************************************************************/ // test that we get some clocks and some not static void gtg0() { GKEY key = init1(); GenerativeTriggerGenerator gtg(AudioMath::random(), rules, numRules, key); bool yes = false; bool no = false; for (int i = 0; i < 100000; ++i) { if (gtg.clock()) yes = true; else no = true; if (yes && no) { //printf("clocked at %d\n", i); return; } } assert(false); } // test that we get everything in even quarter notes static void gtg1() { GKEY key = init1(); std::set counts; GenerativeTriggerGenerator gtg(AudioMath::random(), rules, numRules, key); int ct = 0; for (int i = 0; i < 10000; ++i) { bool b = gtg.clock(); if (b) { //printf("clocked at %d\n", ct); counts.insert(ct); ct = 0; } ct++; } //counts.insert(50); assert(!counts.empty()); for (std::set::iterator it = counts.begin(); it != counts.end(); ++it) { int c = *it; if ((c % PPQ) != 0) { //printf("PPQ=%d, c modePPQ =%d\n", PPQ, (c % PPQ)); //printf("2ppq = %d, 4ppq=%d\n", 2 * PPQ, 4 * PPQ); assert(false); } } } void testStochasticGrammar() { test0(); testAllKeys(); #ifdef _DEBUG gdt0(); testGrammarSub(init0); testGrammarSub(init1); testGrammarSub(init2); #endif ts0(); ts1(); ts2(); ts3(); ts4(); #ifdef _DEBUG gdt1(); #endif gtg0(); gtg1(); }