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
6.4KB

  1. #include "Core.hpp"
  2. #include <iostream>
  3. namespace rack_plugin_AmalgamatedHarmonics {
  4. float Core::getPitchFromVolts(float inVolts, float inRoot, float inScale, int *outRoot, int *outScale, int *outNote, int *outDegree) {
  5. // get the root note and scale
  6. int currRoot = getKeyFromVolts(inRoot);
  7. int currScale = getScaleFromVolts(inScale);
  8. if (debug && stepX % poll == 0) {
  9. std::cout << "QUANT " << stepX << " Root in: " << inRoot << " Root out: " << currRoot<< " Scale in: " << inScale << " Scale out: " << currScale << std::endl;
  10. }
  11. float outVolts = getPitchFromVolts(inVolts, currRoot, currScale, outNote, outDegree);
  12. *outRoot = currRoot;
  13. *outScale = currScale;
  14. return outVolts;
  15. }
  16. float Core::getPitchFromVolts(float inVolts, int currRoot, int currScale, int *outNote, int *outDegree) {
  17. int *curScaleArr;
  18. int notesInScale = 0;
  19. switch (currScale){
  20. case SCALE_CHROMATIC: curScaleArr = ASCALE_CHROMATIC; notesInScale=LENGTHOF(ASCALE_CHROMATIC); break;
  21. case SCALE_IONIAN: curScaleArr = ASCALE_IONIAN; notesInScale=LENGTHOF(ASCALE_IONIAN); break;
  22. case SCALE_DORIAN: curScaleArr = ASCALE_DORIAN; notesInScale=LENGTHOF(ASCALE_DORIAN); break;
  23. case SCALE_PHRYGIAN: curScaleArr = ASCALE_PHRYGIAN; notesInScale=LENGTHOF(ASCALE_PHRYGIAN); break;
  24. case SCALE_LYDIAN: curScaleArr = ASCALE_LYDIAN; notesInScale=LENGTHOF(ASCALE_LYDIAN); break;
  25. case SCALE_MIXOLYDIAN: curScaleArr = ASCALE_MIXOLYDIAN; notesInScale=LENGTHOF(ASCALE_MIXOLYDIAN); break;
  26. case SCALE_AEOLIAN: curScaleArr = ASCALE_AEOLIAN; notesInScale=LENGTHOF(ASCALE_AEOLIAN); break;
  27. case SCALE_LOCRIAN: curScaleArr = ASCALE_LOCRIAN; notesInScale=LENGTHOF(ASCALE_LOCRIAN); break;
  28. case SCALE_MAJOR_PENTA: curScaleArr = ASCALE_MAJOR_PENTA; notesInScale=LENGTHOF(ASCALE_MAJOR_PENTA); break;
  29. case SCALE_MINOR_PENTA: curScaleArr = ASCALE_MINOR_PENTA; notesInScale=LENGTHOF(ASCALE_MINOR_PENTA); break;
  30. case SCALE_HARMONIC_MINOR: curScaleArr = ASCALE_HARMONIC_MINOR; notesInScale=LENGTHOF(ASCALE_HARMONIC_MINOR); break;
  31. case SCALE_BLUES: curScaleArr = ASCALE_BLUES; notesInScale=LENGTHOF(ASCALE_BLUES); break;
  32. default: curScaleArr = ASCALE_CHROMATIC; notesInScale=LENGTHOF(ASCALE_CHROMATIC);
  33. }
  34. // get the octave
  35. int octave = floor(inVolts);
  36. float closestVal = 10.0;
  37. float closestDist = 10.0;
  38. int noteFound = 0;
  39. if (debug && stepX % poll == 0) {
  40. std::cout << "QUANT Octave: " << octave << " Scale: " << scaleNames[currScale] << " Root: " << noteNames[currRoot] << std::endl;
  41. }
  42. float octaveOffset = 0;
  43. if (currRoot != 0) {
  44. octaveOffset = (12 - currRoot) / 12.0;
  45. }
  46. if (debug && stepX % poll == 0) {
  47. std::cout << "QUANT Octave: " << octave << " currRoot: " << currRoot << " -> Offset: " << octaveOffset << " inVolts: " << inVolts << std::endl;
  48. }
  49. float fOctave = (float)octave - octaveOffset;
  50. int scaleIndex = 0;
  51. int searchOctave = 0;
  52. do {
  53. int degree = curScaleArr[scaleIndex]; // 0 - 11!
  54. float fVoltsAboveOctave = searchOctave + degree / 12.0;
  55. float fScaleNoteInVolts = fOctave + fVoltsAboveOctave;
  56. float distAway = fabs(inVolts - fScaleNoteInVolts);
  57. if (debug && stepX % poll == 0) {
  58. std::cout << "QUANT input: " << inVolts
  59. << " index: " << scaleIndex
  60. << " root: " << currRoot
  61. << " octave: " << fOctave
  62. << " degree: " << degree
  63. << " V above O: " << fVoltsAboveOctave
  64. << " note in V: " << fScaleNoteInVolts
  65. << " distance: " << distAway
  66. << std::endl;
  67. }
  68. // Assume that the list of notes is ordered, so there is an single inflection point at the minimum value
  69. if (distAway >= closestDist){
  70. break;
  71. } else {
  72. // Let's remember this
  73. closestVal = fScaleNoteInVolts;
  74. closestDist = distAway;
  75. }
  76. scaleIndex++;
  77. if (scaleIndex == notesInScale - 1) {
  78. scaleIndex = 0;
  79. searchOctave++;
  80. }
  81. } while (true);
  82. if(scaleIndex == 0) {
  83. noteFound = notesInScale - 2; // NIS is a count, not index
  84. } else {
  85. noteFound = scaleIndex - 1;
  86. }
  87. if (debug && stepX % poll == 0) {
  88. std::cout << "QUANT NIS: " << notesInScale << " scaleIndex: " << scaleIndex << " NF: " << noteFound << std::endl;
  89. }
  90. int currNote = (currRoot + curScaleArr[noteFound]) % 12; // So this is the nth note of the scale;
  91. // case in point, V=0, Scale = F#m returns the 6th note, which should be C#
  92. if (debug && stepX % poll == 0) {
  93. // Dump the note and degree, mod the size in case where we have wrapped round
  94. std::cout << "QUANT Found index in scale: " << noteFound << ", currNote: " << currNote;
  95. std::cout << " This is scale note: " << curScaleArr[noteFound] << " (Interval: " << intervalNames[curScaleArr[noteFound]] << ")";
  96. std::cout << ": " << inVolts << " -> " << closestVal << std::endl;
  97. }
  98. *outNote = currNote;
  99. *outDegree = curScaleArr[noteFound];
  100. return closestVal;
  101. }
  102. void Core::getRootFromMode(int inMode, int inRoot, int inTonic, int *currRoot, int *quality) {
  103. *quality = ModeQuality[inMode][inTonic];
  104. int positionRelativeToStartOfScale = tonicIndex[inMode + inTonic];
  105. int positionStartOfScale = scaleIndex[inMode];
  106. // FIXME should be mapped into the Circle of Fifths??
  107. *currRoot = inRoot + noteIndex[positionStartOfScale + positionRelativeToStartOfScale];
  108. if (*currRoot < 0) {
  109. *currRoot += 12;
  110. }
  111. if (*currRoot > 11) {
  112. *currRoot -= 12;
  113. }
  114. // Quantizer q;
  115. //
  116. // std::cout << "Mode: " << inMode
  117. // << " Root: " << q.noteNames[inRoot]
  118. // << " Tonic: " << q.tonicNames[inTonic]
  119. // << " Scale Pos: " << positionStartOfScale
  120. // << " Rel Pos: " << positionRelativeToStartOfScale
  121. // << " Note Index: " << positionStartOfScale + positionRelativeToStartOfScale
  122. // << " Note: " << noteIndex[positionStartOfScale + positionRelativeToStartOfScale]
  123. // << " Offset: " << ModeOffset[inMode][inTonic]
  124. // << " Output: " << *currRoot
  125. // << " " << q.noteNames[*currRoot]
  126. // << std::endl;
  127. }
  128. Core & CoreUtil() {
  129. static Core core;
  130. return core;
  131. }
  132. // http://c-faq.com/lib/gaussian.html
  133. double Core::gaussrand() {
  134. static double U, V;
  135. static int phase = 0;
  136. double Z;
  137. if(phase == 0) {
  138. U = (rand() + 1.) / (RAND_MAX + 2.);
  139. V = rand() / (RAND_MAX + 1.);
  140. Z = sqrt(-2 * log(U)) * sin(2 * M_PI * V);
  141. } else
  142. Z = sqrt(-2 * log(U)) * cos(2 * M_PI * V);
  143. phase = 1 - phase;
  144. return Z;
  145. }
  146. int Core::ipow(int base, int exp) {
  147. int result = 1;
  148. while (exp)
  149. {
  150. if (exp & 1)
  151. result *= base;
  152. exp >>= 1;
  153. base *= base;
  154. }
  155. return result;
  156. }
  157. } // namespace rack_plugin_AmalgamatedHarmonics