You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
5.4KB

  1. #pragma once
  2. #include <assert.h>
  3. #include <stdio.h>
  4. #include <functional>
  5. #include "AudioMath.h"
  6. /***********************************************
  7. ********** rhythmic grouping codes *************
  8. ************************************************/
  9. typedef unsigned short GKEY;
  10. /* Rules for keys - super important!
  11. *
  12. * There are two kinds of keys: terminal keys and non-terminal
  13. *
  14. * Terminal keys either directly generate themselves, or may be divided if there is a
  15. * specific production rule to divide them. As an example, sg_q (quarter note) is a terminal key.
  16. * It will always generate itself unless a production rule divides it.
  17. *
  18. * On the other hand sg_w2 is a non-terminal representing all the time in two bars of 4/4.
  19. * sg_w2 will NEVER generate itself. Lacking a specific rule, it will auto generate two whole notes
  20. *
  21. * programmer must be aware of the difference in two places:
  22. * when making production rules for a specific grammar
  23. * when implementing ProductionRuleKeys::breakDown
  24. */
  25. const GKEY sg_invalid = 0; // either uninitialized rule, or return value that stops recursion.
  26. // Note that this means table of production rules must have a dummy entry up front
  27. const GKEY sg_w2 = 1; // duration of two whole notes
  28. const GKEY sg_w = 2; // whole
  29. const GKEY sg_ww = 3; // w,w
  30. const GKEY sg_h = 4;
  31. const GKEY sg_hh = 5;
  32. const GKEY sg_q = 6;
  33. const GKEY sg_qq = 7;
  34. const GKEY sg_e = 8;
  35. const GKEY sg_ee = 9;
  36. // triplets
  37. const GKEY sg_e3e3e3 = 10; // three trip eights
  38. const GKEY sg_e3 = 11; // trip eight
  39. const GKEY sg_sx = 12;
  40. const GKEY sg_sxsx = 13;
  41. // crazy stuff for syncopation (unequal measure divisions)
  42. // Note that there are not "tuples", they are just straight durations
  43. // of a group of notes.
  44. const GKEY sg_68 = 14; // the time duration of 6/8
  45. const GKEY sg_78 = 15; // the time duration of 7/8
  46. const GKEY sg_98 = 16; // the time duration of 9/8
  47. const GKEY sg_798 = 17; // 7/8 + 9/8 = 2w
  48. // dotted notes
  49. const GKEY sg_dq = 18; // dotted quarter
  50. const GKEY sg_dh = 19; // dotted half
  51. const GKEY sg_de = 20; // dotted eighth
  52. // odd groupings
  53. const GKEY sg_hdq = 21; // half + dotted Q
  54. const GKEY sg_qhe = 22; // q,h,e
  55. const GKEY sg_hq = 23; // h,q
  56. const GKEY sg_qh = 24; // h,q
  57. const GKEY sg_q78 = 25; // q + 7x8
  58. const GKEY sg_qe68 = 26; // q+e+6x8
  59. const GKEY sg_first = 1; // first valid one
  60. const GKEY sg_last = 26;
  61. const int fullRuleTableSize = sg_last + 1;
  62. // Do we really want to use something this coarse?
  63. const int PPQ = 24;
  64. /* class ProductionRuleKeys
  65. * collection of utility functions around rule keys
  66. */
  67. class ProductionRuleKeys
  68. {
  69. public:
  70. static const int bufferSize = 6; // size of a buffer that must be passed to breakDown
  71. /**
  72. * Turn a key into a 0 terminated list of keys for individual notes.
  73. * If called with a terminal key, just returns itself.
  74. */
  75. static void breakDown(GKEY key, GKEY * outKeys);
  76. /**
  77. * get the duration in clocks for a key
  78. */
  79. static int getDuration(GKEY key);
  80. /**
  81. * Get a human readable string representation
  82. */
  83. static const char * toString(GKEY key);
  84. };
  85. /* class ProductionRuleEntry
  86. * A single entry in a production rule.
  87. * if A -> B or A -> C, then each of these would be a separate rule entry
  88. */
  89. class ProductionRuleEntry
  90. {
  91. public:
  92. ProductionRuleEntry() : probability(0), code(sg_invalid)
  93. {
  94. }
  95. float probability; // 0 to 1
  96. GKEY code; // what to do if this one fires
  97. };
  98. inline bool operator == (const ProductionRuleEntry& a, const ProductionRuleEntry& b)
  99. {
  100. return a.probability == b.probability && a.code == b.code;
  101. }
  102. /* class ProductionRule
  103. * A production rule encapsulates every way that a starting symbol
  104. * can produce others.
  105. * if A -> B or A -> C, then a single production rule could represent this
  106. *
  107. */
  108. class ProductionRule
  109. {
  110. public:
  111. static const int numEntries = 3;
  112. class EvaluationState
  113. {
  114. public:
  115. EvaluationState(AudioMath::RandomUniformFunc xr) : r(xr)
  116. {
  117. }
  118. const ProductionRule * rules;
  119. int numRules;
  120. AudioMath::RandomUniformFunc r; //random number generator to use
  121. virtual void writeSymbol(GKEY)
  122. {
  123. }
  124. };
  125. ProductionRule()
  126. {
  127. }
  128. void makeTerminal()
  129. {
  130. entries[0].code = sg_invalid;
  131. entries[0].probability = 1.0f;
  132. }
  133. /* the data */
  134. // each possible production rule for this state
  135. ProductionRuleEntry entries[numEntries];
  136. static void evaluate(EvaluationState& es, int ruleToEval);
  137. #ifdef _DEBUG
  138. static bool isGrammarValid(const ProductionRule * rules, int numRules, GKEY firstRule);
  139. #endif
  140. private:
  141. static int _evaluateRule(const ProductionRule& rule, float random);
  142. #ifdef _DEBUG
  143. bool _isValid(int index) const;
  144. #endif
  145. };
  146. /* class StochasticGrammarDictionary
  147. *
  148. * just a collection of pre-made grammars
  149. *
  150. * 0: simple test
  151. * 1: mixed duration, with some trips
  152. * 2: some syncopation, no trips
  153. */
  154. class StochasticGrammarDictionary
  155. {
  156. public:
  157. class Grammar
  158. {
  159. public:
  160. const ProductionRule * rules;
  161. int numRules;
  162. GKEY firstRule;
  163. };
  164. static Grammar getGrammar(int index);
  165. static int getNumGrammars();
  166. private:
  167. static bool _didInitRules;
  168. static void initRules();
  169. static void initRule0(ProductionRule * rules);
  170. static void initRule1(ProductionRule * rules);
  171. static void initRule2(ProductionRule * rules);
  172. static void initRule3(ProductionRule * rules);
  173. };