#pragma once #include #include #include #include "AudioMath.h" /*********************************************** ********** rhythmic grouping codes ************* ************************************************/ typedef unsigned short GKEY; /* Rules for keys - super important! * * There are two kinds of keys: terminal keys and non-terminal * * Terminal keys either directly generate themselves, or may be divided if there is a * specific production rule to divide them. As an example, sg_q (quarter note) is a terminal key. * It will always generate itself unless a production rule divides it. * * On the other hand sg_w2 is a non-terminal representing all the time in two bars of 4/4. * sg_w2 will NEVER generate itself. Lacking a specific rule, it will auto generate two whole notes * * programmer must be aware of the difference in two places: * when making production rules for a specific grammar * when implementing ProductionRuleKeys::breakDown */ const GKEY sg_invalid = 0; // either uninitialized rule, or return value that stops recursion. // Note that this means table of production rules must have a dummy entry up front const GKEY sg_w2 = 1; // duration of two whole notes const GKEY sg_w = 2; // whole const GKEY sg_ww = 3; // w,w const GKEY sg_h = 4; const GKEY sg_hh = 5; const GKEY sg_q = 6; const GKEY sg_qq = 7; const GKEY sg_e = 8; const GKEY sg_ee = 9; // triplets const GKEY sg_e3e3e3 = 10; // three trip eights const GKEY sg_e3 = 11; // trip eight const GKEY sg_sx = 12; const GKEY sg_sxsx = 13; // crazy stuff for syncopation (unequal measure divisions) // Note that there are not "tuples", they are just straight durations // of a group of notes. const GKEY sg_68 = 14; // the time duration of 6/8 const GKEY sg_78 = 15; // the time duration of 7/8 const GKEY sg_98 = 16; // the time duration of 9/8 const GKEY sg_798 = 17; // 7/8 + 9/8 = 2w // dotted notes const GKEY sg_dq = 18; // dotted quarter const GKEY sg_dh = 19; // dotted half const GKEY sg_de = 20; // dotted eighth // odd groupings const GKEY sg_hdq = 21; // half + dotted Q const GKEY sg_qhe = 22; // q,h,e const GKEY sg_hq = 23; // h,q const GKEY sg_qh = 24; // h,q const GKEY sg_q78 = 25; // q + 7x8 const GKEY sg_qe68 = 26; // q+e+6x8 const GKEY sg_first = 1; // first valid one const GKEY sg_last = 26; const int fullRuleTableSize = sg_last + 1; // Do we really want to use something this coarse? const int PPQ = 24; /* class ProductionRuleKeys * collection of utility functions around rule keys */ class ProductionRuleKeys { public: static const int bufferSize = 6; // size of a buffer that must be passed to breakDown /** * Turn a key into a 0 terminated list of keys for individual notes. * If called with a terminal key, just returns itself. */ static void breakDown(GKEY key, GKEY * outKeys); /** * get the duration in clocks for a key */ static int getDuration(GKEY key); /** * Get a human readable string representation */ static const char * toString(GKEY key); }; /* class ProductionRuleEntry * A single entry in a production rule. * if A -> B or A -> C, then each of these would be a separate rule entry */ class ProductionRuleEntry { public: ProductionRuleEntry() : probability(0), code(sg_invalid) { } float probability; // 0 to 1 GKEY code; // what to do if this one fires }; inline bool operator == (const ProductionRuleEntry& a, const ProductionRuleEntry& b) { return a.probability == b.probability && a.code == b.code; } /* class ProductionRule * A production rule encapsulates every way that a starting symbol * can produce others. * if A -> B or A -> C, then a single production rule could represent this * */ class ProductionRule { public: static const int numEntries = 3; class EvaluationState { public: EvaluationState(AudioMath::RandomUniformFunc xr) : r(xr) { } const ProductionRule * rules; int numRules; AudioMath::RandomUniformFunc r; //random number generator to use virtual void writeSymbol(GKEY) { } }; ProductionRule() { } void makeTerminal() { entries[0].code = sg_invalid; entries[0].probability = 1.0f; } /* the data */ // each possible production rule for this state ProductionRuleEntry entries[numEntries]; static void evaluate(EvaluationState& es, int ruleToEval); #ifdef _DEBUG static bool isGrammarValid(const ProductionRule * rules, int numRules, GKEY firstRule); #endif private: static int _evaluateRule(const ProductionRule& rule, float random); #ifdef _DEBUG bool _isValid(int index) const; #endif }; /* class StochasticGrammarDictionary * * just a collection of pre-made grammars * * 0: simple test * 1: mixed duration, with some trips * 2: some syncopation, no trips */ class StochasticGrammarDictionary { public: class Grammar { public: const ProductionRule * rules; int numRules; GKEY firstRule; }; static Grammar getGrammar(int index); static int getNumGrammars(); private: static bool _didInitRules; static void initRules(); static void initRule0(ProductionRule * rules); static void initRule1(ProductionRule * rules); static void initRule2(ProductionRule * rules); static void initRule3(ProductionRule * rules); };