|
-
- #pragma once
-
- #include <assert.h>
- #include <stdio.h>
- #include <functional>
- #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);
- };
|