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.

129 lines
3.9KB

  1. #pragma once
  2. #include <cmath>
  3. #include "SawOscillator.h"
  4. /**
  5. * A bunch of LFOs at slightly different frequencies added together in different ways.
  6. * Taken from Bernie Hutchins' ElectroNotes.
  7. */
  8. template <typename T, int NOsc, int NOut>
  9. class MultiModOsc
  10. {
  11. public:
  12. MultiModOsc() = delete;
  13. /**
  14. * Make state and params be nested classes so we don't have
  15. * to type as many template arguments.
  16. */
  17. class State
  18. {
  19. public:
  20. friend MultiModOsc;
  21. private:
  22. SawOscillatorState<T> states[NOsc];
  23. };
  24. class Params
  25. {
  26. public:
  27. friend MultiModOsc;
  28. Params();
  29. /**
  30. * @param rate is -1..1 arbitrary "low frequency" units
  31. */
  32. void setRateAndSpread(T rate, T spread, int matrixMode, T inverseSampleRate);
  33. private:
  34. SawOscillatorParams<T> params[NOsc];
  35. int matrixMode = 0;
  36. };
  37. static void run(T * output, State&, const Params&);
  38. };
  39. template <typename T, int NOsc, int NOut>
  40. inline MultiModOsc<T, NOsc, NOut>::Params::Params()
  41. {
  42. setRateAndSpread(.5, .5, 0, T(1.0 / 44100));
  43. }
  44. template <typename T, int NOsc, int NOut>
  45. inline void MultiModOsc<T, NOsc, NOut>::Params::setRateAndSpread(T rate, T spread, int inMatrixMode, T inverseSampleRate)
  46. {
  47. assert(rate >= -10 && rate <= 10); // just a sanity check
  48. assert(inverseSampleRate > (1.0 / 200000));
  49. assert(inverseSampleRate < (1.0 / 2000)); // same
  50. T BaseRate = 1.0;
  51. BaseRate *= std::pow(T(3), rate);
  52. const T dNormSpread = spread * T((3.0 / 2.0) + .5);
  53. for (int i = 0; i < NOsc; i++) {
  54. T dMult = 1;
  55. switch (i) {
  56. case 1:
  57. dMult = 1 / T(1.11);
  58. break;
  59. case 0:
  60. dMult = 1.0;
  61. break;
  62. case 2:
  63. dMult = T(1.32);
  64. break;
  65. case 3:
  66. dMult = 1.25;
  67. break;
  68. case 4:
  69. dMult = 1.5;
  70. break;
  71. default:
  72. assert(false);
  73. }
  74. dMult -= 1.0; // norm to 0
  75. dMult *= dNormSpread;
  76. dMult += 1.0;
  77. const T x = BaseRate * dMult;
  78. const T actual = x * inverseSampleRate;
  79. SawOscillator<T, false>::setFrequency(params[i], actual);
  80. this->matrixMode = inMatrixMode;
  81. }
  82. }
  83. template <typename T, int NOsc, int NOut>
  84. inline void MultiModOsc<T, NOsc, NOut>::run(T* output, State& state, const Params& params)
  85. {
  86. T modulators[NOsc];
  87. for (int i = 0; i < NOsc; ++i) {
  88. modulators[i] = SawOscillator<T, false>::runTri(state.states[i], params.params[i]);
  89. }
  90. // The old implementation had a smarter algorithm, but
  91. // for now hard-wiring it for 4/3 is OK
  92. if ((NOsc == 4) && (NOut == 3)) {
  93. switch (params.matrixMode) {
  94. case 0: // classic mix
  95. output[0] = modulators[0] + modulators[1] + modulators[2]; // not 3
  96. output[1] = modulators[0] + modulators[1] + modulators[3]; // not 2
  97. output[2] = modulators[0] + modulators[2] + modulators[3]; // not 1
  98. break;
  99. case 1:
  100. // slight variation on classic
  101. output[0] = modulators[0] + modulators[1] + modulators[2]; // not 3
  102. output[1] = modulators[1] + modulators[2] + modulators[3]; // not 0
  103. output[2] = modulators[0] + modulators[2] + modulators[3]; // not 1
  104. break;
  105. case 2:
  106. // invert some
  107. output[0] = modulators[0] + modulators[1] + modulators[2]; // not 3
  108. output[1] = -modulators[0] + modulators[2] + modulators[3]; // not 0
  109. output[2] = -modulators[1] - modulators[2] - modulators[3]; // not 1
  110. break;
  111. default:
  112. assert(false);
  113. }
  114. } else {
  115. assert(false); // need to return something
  116. }
  117. }