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.

178 lines
5.1KB

  1. #pragma once
  2. #include <cmath>
  3. #include <functional>
  4. #include <algorithm>
  5. class AudioMath
  6. {
  7. public:
  8. AudioMath() = delete; // we are only static
  9. static const double Pi;
  10. static const double Pi_2; // Pi / 2
  11. static const double Ln2;
  12. static const double Ln10;
  13. static const double E;
  14. static bool closeTo(double x, double y, double tolerance)
  15. {
  16. const bool ret = std::abs(x - y) < tolerance;
  17. return ret;
  18. }
  19. static double db(double g)
  20. {
  21. return 20 * log(g) / Ln10;
  22. }
  23. static double cents(double f1, double f2)
  24. {
  25. return 1200 * std::log2(f1 / f2);
  26. }
  27. static double acents(double f1, double f2)
  28. {
  29. return std::abs(cents(f1, f2));
  30. }
  31. static double gainFromDb(double db)
  32. {
  33. return std::exp(Ln10 * db / 20.0);
  34. }
  35. static float quadraticBipolar(float x)
  36. {
  37. float x2 = x * x;
  38. return (x >= 0.f) ? x2 : -x2;
  39. }
  40. /**
  41. * Returns a function that generates one period of sin for x = {0..1}.
  42. * Range (output) is -1 to 1.
  43. *
  44. * All makeFunc_xxx functions return functions that are not optimized.
  45. * They do not use lookup tables.
  46. */
  47. static std::function<double(double)> makeFunc_Sin();
  48. /*
  49. * Returns a function that generates an exponential defined by two points
  50. * At input = xMin, output will be yMin.
  51. * At input = xMax, output will be yMax.
  52. */
  53. static std::function<double(double)> makeFunc_Exp(double xMin, double xMax, double yMin, double yMax);
  54. /**
  55. * Returns a function for an "audio taper" attenuator gain.
  56. * function is pure exponential for x > .25, linear for x < .25
  57. * At input 1, output is 1
  58. * At input .25, output is the gain corresponding to adAtten
  59. * At input 0, the output is zero.
  60. */
  61. static std::function<double(double)> makeFunc_AudioTaper(double dbAtten);
  62. /**
  63. * ScaleFun is a function the combines CV, knob, and trim into a voltage.
  64. * Typically a ScaleFun is like an "attenuverter", where the trim input
  65. * is the attenuverter.
  66. */
  67. template <typename T>
  68. using ScaleFun = std::function<T(T cv, T knob, T trim)>;
  69. /**
  70. * Create a ScaleFun with the following properties:
  71. * 1) The values are combined with the typical formula: x = cv * trim + knob;
  72. * 2) x is clipped between -5 and 5
  73. * 3) range is then interpolated between y0, and y1.
  74. *
  75. * This particular function is used when knobs are -5..5,
  76. * and CV range is -5..5.
  77. *
  78. *
  79. * Can easily be used to add, clip and scale just a knob an a CV by
  80. * passing 1 for the trim param.
  81. */
  82. template <typename T>
  83. static ScaleFun<T> makeLinearScaler(T y0, T y1)
  84. {
  85. const T x0 = -5;
  86. const T x1 = 5;
  87. const T a = (y1 - y0) / (x1 - x0);
  88. const T b = y0 - a * x0;
  89. return [a, b](T cv, T knob, T trim) {
  90. T x = cv * trim + knob;
  91. x = std::max<T>(-5.0f, x);
  92. x = std::min(5.0f, x);
  93. return a * x + b;
  94. };
  95. }
  96. /**
  97. * Generates a scale function for an audio taper attenuverter.
  98. * Details the same as makeLinearScaler except that the CV
  99. * scaling will be exponential for most values, becoming
  100. * linear near zero.
  101. *
  102. * Note that the cv and knob will have linear taper, only the
  103. * attenuverter is audio taper.
  104. *
  105. * Implemented with a cached lookup table -
  106. * only the final scaling is done at run time
  107. */
  108. static ScaleFun<float> makeScalerWithBipolarAudioTrim(float y0, float y1);
  109. /**
  110. * SimpleScaleFun is a function the combines CV, and knob, into a voltage.
  111. * Usually in a synth module that has knob and cv, but no trim.
  112. *
  113. * cv and knob are -5 to +5. They are summed, and the sum is limited to 5, -5.
  114. * Then the sum gets an audio taper
  115. */
  116. template <typename T>
  117. using SimpleScaleFun = std::function<T(T cv, T knob)>;
  118. /**
  119. * maps +/- 5 volts from cv and knob to y0, y1
  120. * with an audio taper.
  121. */
  122. static SimpleScaleFun<float> makeSimpleScalerAudioTaper(float y0, float y1);
  123. template <typename T>
  124. static std::pair<T, T> getMinMax(const T* data, int numSamples)
  125. {
  126. T min = 1, max = -1;
  127. for (int i = 0; i < numSamples; ++i) {
  128. const T x = data[i];
  129. min = std::min(min, x);
  130. max = std::max(max, x);
  131. }
  132. return std::pair<T, T>(min, max);
  133. }
  134. /**
  135. * A random number generator function. uniform random 0..1
  136. */
  137. using RandomUniformFunc = std::function<float(void)>;
  138. static RandomUniformFunc random();
  139. /**
  140. * Folds numbers between +1 and -1
  141. */
  142. static inline float fold(float x)
  143. {
  144. float fold;
  145. const float bias = (x < 0) ? -1.f : 1.f;
  146. int phase = int((x + bias) / 2.f);
  147. bool isEven = !(phase & 1);
  148. // printf(" wrap(%f) phase=%d, isEven=%d", x, phase, isEven);
  149. if (isEven) {
  150. fold = x - 2.f * phase;
  151. } else {
  152. fold = -x + 2.f * phase;
  153. }
  154. // printf(" y=%f\n", wrap);
  155. return fold;
  156. }
  157. };