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.

382 lines
9.5KB

  1. #include <map>
  2. #include <vector>
  3. #include "asserts.h"
  4. #include "LookupTable.h"
  5. #include "AsymWaveShaper.h"
  6. #include "ExtremeTester.h"
  7. #include "Shaper.h"
  8. #include "SinOscillator.h"
  9. #include "TestComposite.h"
  10. #include "TestSignal.h"
  11. using Spline = std::vector< std::pair<double, double> >;
  12. static void gen()
  13. {
  14. for (int i = 0; i < AsymWaveShaper::iSymmetryTables; ++i) {
  15. float symmetry = float(i) / float(AsymWaveShaper::iSymmetryTables - 1);
  16. AsymWaveShaper::genTable(i, symmetry);
  17. }
  18. }
  19. static void testLook0()
  20. {
  21. NonUniformLookup l;
  22. l.add(0, 0);
  23. l.add(1, 1);
  24. l.add(2, 2);
  25. l.add(3, 3);
  26. double x = l.lookup(2.5);
  27. assertClose(x, 2.5, .0001);
  28. }
  29. static void testLook1()
  30. {
  31. NonUniformLookup l;
  32. l.add(0, 0);
  33. l.add(1, 1);
  34. l.add(2, 4);
  35. l.add(3, 3);
  36. double x = l.lookup(1.5);
  37. assertClose(x, 2.5, .0001);
  38. }
  39. static void testLook2()
  40. {
  41. NonUniformLookup l;
  42. l.add(0, 0);
  43. l.add(1, 1);
  44. l.add(2, 2);
  45. l.add(3, 3);
  46. double x = l.lookup(.5);
  47. assertClose(x, .5, .0001);
  48. }
  49. static void testLook3()
  50. {
  51. NonUniformLookup l;
  52. l.add(0, 0);
  53. l.add(1, 1);
  54. l.add(2, 2);
  55. l.add(3, 3);
  56. double x = l.lookup(2.5);
  57. assertClose(x, 2.5, .0001);
  58. }
  59. static void testLook4()
  60. {
  61. NonUniformLookup l;
  62. l.add(0, 0);
  63. l.add(1, 1);
  64. l.add(2, 2);
  65. l.add(3, 3);
  66. double x = l.lookup(0);
  67. assertClose(x, 0, .0001);
  68. }
  69. static void testGen0()
  70. {
  71. AsymWaveShaper g;
  72. const int index = 3;// symmetry
  73. float x = g.lookup(0, index);
  74. x = g.lookup(1, index);
  75. x = g.lookup(-1, index);
  76. }
  77. static void testDerivativeSub(int index, float delta)
  78. {
  79. AsymWaveShaper ws;
  80. const float ya = ws.lookup(-delta, index);
  81. const float y0 = ws.lookup(0, index);
  82. const float yb = ws.lookup(delta, index);
  83. const float slopeLeft = -ya / delta;
  84. const float slopeRight = yb / delta;
  85. // since I changed AsymWaveShaper to be points-1 this is worse
  86. assertClose(y0, 0, .00001);
  87. assertClose(slopeRight, 2, .01);
  88. if (index != 0) {
  89. assertClose(slopeLeft, 2, .3
  90. );
  91. }
  92. }
  93. static void testDerivative()
  94. {
  95. // 6 with .1
  96. for (int i = AsymWaveShaper::iSymmetryTables - 1; i >= 0; --i) {
  97. testDerivativeSub(i, .0001f);
  98. }
  99. }
  100. static void testShaper0()
  101. {
  102. Shaper<TestComposite> gmr;
  103. int shapeMax = (int) Shaper<TestComposite>::Shapes::Invalid;
  104. for (int i = 0; i < shapeMax; ++i) {
  105. gmr.params[Shaper<TestComposite>::PARAM_SHAPE].value = (float) i;
  106. std::string s = gmr.getString(Shaper<TestComposite>::Shapes(i));
  107. assertGT(s.length(), 0);
  108. assertLT(s.length(), 20);
  109. gmr.params[Shaper<TestComposite>::PARAM_OFFSET].value = -5;
  110. for (int i = 0; i < 50; ++i) gmr.step();
  111. const float x = gmr.outputs[Shaper<TestComposite>::OUTPUT_AUDIO].value;
  112. gmr.params[Shaper<TestComposite>::PARAM_OFFSET].value = 5;
  113. for (int i = 0; i < 50; ++i) gmr.step();
  114. const float y = gmr.outputs[Shaper<TestComposite>::OUTPUT_AUDIO].value;
  115. assertLT(x, 10);
  116. assertLT(y, 10);
  117. assertGT(x, -10);
  118. assertGT(y, -10);
  119. }
  120. }
  121. static void testShaper1Sub(int shape, float gain, float targetRMS)
  122. {
  123. Shaper<TestComposite> gmr;
  124. gmr.params[Shaper<TestComposite>::PARAM_SHAPE].value = (float) shape;
  125. gmr.params[Shaper<TestComposite>::PARAM_GAIN].value = gain; // max gain
  126. const int buffSize = 1 * 1024;
  127. float buffer[buffSize];
  128. TestSignal<float>::generateSin(buffer, buffSize, 1.f / 40);
  129. double rms = TestSignal<float>::getRMS(buffer, buffSize);
  130. for (int i = 0; i < buffSize; ++i) {
  131. const float x = buffer[i];
  132. gmr.inputs[Shaper<TestComposite>::INPUT_AUDIO].value = buffer[i];
  133. gmr.step();
  134. buffer[i] = gmr.outputs[Shaper<TestComposite>::OUTPUT_AUDIO].value;
  135. }
  136. rms = TestSignal<float>::getRMS(buffer, buffSize);
  137. const char* p = gmr.getString(Shaper<TestComposite>::Shapes(shape));
  138. //printf("rms[%s] = %f target = %f ratio=%f\n", p, rms, targetRMS, targetRMS / rms);
  139. if (targetRMS > .01) {
  140. assertClose(rms, targetRMS, .5);
  141. }
  142. }
  143. static void testShaper1()
  144. {
  145. int shapeMax = (int) Shaper<TestComposite>::Shapes::Invalid;
  146. for (int i = 0; i < shapeMax; ++i) {
  147. const float targetOutput = (i == (int) Shaper<TestComposite>::Shapes::Crush) ? 0 : 5 * .707f;
  148. testShaper1Sub(i, 5, targetOutput);
  149. testShaper1Sub(i, 0, 0);
  150. }
  151. }
  152. static void testSplineExtremes()
  153. {
  154. Shaper<TestComposite> sp;
  155. using fp = std::pair<float, float>;
  156. std::vector< std::pair<float, float> > paramLimits;
  157. paramLimits.resize(sp.NUM_PARAMS);
  158. paramLimits[sp.PARAM_SHAPE] = fp(0.f, float(Shaper<TestComposite>::Shapes::Invalid) - 1);
  159. paramLimits[sp.PARAM_GAIN] = fp(-5.0f, 5.0f);
  160. paramLimits[sp.PARAM_GAIN_TRIM] = fp(-1.f, 1.f);
  161. paramLimits[sp.PARAM_OFFSET] = fp(-5.f, 5.f);
  162. paramLimits[sp.PARAM_OFFSET_TRIM] = fp(-1.f, 1.f);
  163. paramLimits[sp.PARAM_OVERSAMPLE] = fp(0.f, 2.f);
  164. paramLimits[sp.PARAM_ACDC] = fp(0.f, 1.f);
  165. ExtremeTester< Shaper<TestComposite>>::test(sp, paramLimits, true, "shaper");
  166. }
  167. static void testShaper2d(Shaper<TestComposite>::Shapes shape, float gain, float offset, float input)
  168. {
  169. Shaper<TestComposite> sh;
  170. sh.params[Shaper<TestComposite>::PARAM_SHAPE].value = (float) shape;
  171. sh.params[Shaper<TestComposite>::PARAM_GAIN].value = gain;
  172. sh.params[Shaper<TestComposite>::PARAM_OFFSET].value = offset;
  173. for (int i = 0; i < 100; ++i) {
  174. sh.inputs[Shaper<TestComposite>::INPUT_AUDIO].value = input;
  175. sh.step();
  176. const float out = sh.outputs[Shaper<TestComposite>::OUTPUT_AUDIO].value;
  177. // brief ringing goes > 10
  178. assert(out < 20 && out > -20);
  179. }
  180. }
  181. static void testShaper2c(Shaper<TestComposite>::Shapes shape, float gain, float offset)
  182. {
  183. testShaper2d(shape, gain, offset, 0);
  184. testShaper2d(shape, gain, offset, 5);
  185. testShaper2d(shape, gain, offset, -5);
  186. }
  187. static void testShaper2b(Shaper<TestComposite>::Shapes shape, float gain)
  188. {
  189. testShaper2c(shape, gain, 0.f);
  190. testShaper2c(shape, gain, 5.f);
  191. testShaper2c(shape, gain, -5.f);
  192. }
  193. static void testShaper2a(Shaper<TestComposite>::Shapes shape)
  194. {
  195. testShaper2b(shape, 0);
  196. testShaper2b(shape, -5);
  197. testShaper2b(shape, 5);
  198. }
  199. const int shapeMax = (int) Shaper<TestComposite>::Shapes::Invalid;
  200. static void testShaper2()
  201. {
  202. for (int i = 0; i < shapeMax; ++i) {
  203. testShaper2a(Shaper<TestComposite>::Shapes(i));
  204. }
  205. }
  206. // test for DC shift
  207. static void testShaper3Sub(Shaper<TestComposite>::Shapes shape)
  208. {
  209. Shaper<TestComposite> sh;
  210. sh.params[Shaper<TestComposite>::PARAM_OVERSAMPLE].value = 2; // turn off oversampling
  211. sh.params[Shaper<TestComposite>::PARAM_SHAPE].value = (float) shape;
  212. sh.params[Shaper<TestComposite>::PARAM_GAIN].value = -3; // gain up a bit
  213. sh.params[Shaper<TestComposite>::PARAM_OFFSET].value = 0; // no offset
  214. sh.inputs[Shaper<TestComposite>::INPUT_AUDIO].value = 0;
  215. for (int i = 0; i < 100; ++i) {
  216. sh.step();
  217. }
  218. const float out = sh.outputs[Shaper<TestComposite>::OUTPUT_AUDIO].value;
  219. if (shape != Shaper<TestComposite>::Shapes::Crush) {
  220. assertEQ(out, 0);
  221. } else {
  222. // crash had a dc offset issue
  223. assertLT(out, 1);
  224. assertGT(out, -1);
  225. }
  226. }
  227. static void testShaper3()
  228. {
  229. // testShaper3Sub(Shaper<TestComposite>::Shapes::Crush);
  230. for (int i = 0; i < shapeMax; ++i) {
  231. testShaper3Sub(Shaper<TestComposite>::Shapes(i));
  232. }
  233. }
  234. static void testDC()
  235. {
  236. using Sh = Shaper<TestComposite>;
  237. Sh sh;
  238. sh.params[Sh::PARAM_SHAPE].value = float(Sh::Shapes::FullWave);
  239. // Will generate sine at fs * .01 (around 400 Hz).
  240. SinOscillatorParams<float> sinp;
  241. SinOscillatorState<float> sins;
  242. SinOscillator<float, false>::setFrequency(sinp, .01f);
  243. // Run sin through Chebyshevs at specified gain
  244. auto func = [&sins, &sinp, &sh]() {
  245. const float sin = SinOscillator<float, false>::run(sins, sinp);
  246. sh.inputs[Sh::INPUT_AUDIO].value = sin;
  247. sh.step();
  248. return sh.outputs[Sh::OUTPUT_AUDIO].value;
  249. };
  250. const int bufferSize = 16 * 1024;
  251. float buffer[bufferSize];
  252. for (int i = 0; i < bufferSize; ++i) {
  253. buffer[i] = func();
  254. }
  255. double dc = TestSignal<float>::getDC(buffer, bufferSize);
  256. assertClose(dc, 0, .001);
  257. }
  258. float cf(float x, float fold_gain)
  259. {
  260. // x = inputs[IN_INPUT].value*5.0f*gain_gain;
  261. x *= 5;
  262. // if (abs(x) > 5) y = clamp((abs(x) - 5) / 2.2f, 0.0f, 58.0f); else y = 0;
  263. for (int i = 0; i < 100; i++) {
  264. if (x < -5.0f) x = -5.0f + (-x - 5.0f)*fold_gain / 5.0f;
  265. if (x > 5.0f) x = 5.0f - (x - 5.0f)*fold_gain / 5.0f;
  266. if ((x >= -5.0) & (x <= 5.0)) i = 1000; if (i == 99) x = 0;
  267. }
  268. // return clamp(x, -5.0f, 5.0f);
  269. return x;
  270. }
  271. float sq(float x)
  272. {
  273. return 5 * AudioMath::fold(x);
  274. }
  275. static void testCf()
  276. {
  277. printf("HWY IS testCf failing now?\n");
  278. #if 0
  279. const float fold_gain = 1;
  280. for (float x = 0; x < 2; x += .05f) {
  281. const float c = cf(x, fold_gain);
  282. const float s = sq(x);
  283. printf("x=%.2f c=%.2f s=%.2f\n", x, c, s);
  284. assertClose(s, c, .01);
  285. }
  286. #endif
  287. }
  288. void testSpline(bool doEmit)
  289. {
  290. testCf();
  291. if (doEmit) {
  292. gen();
  293. return;
  294. }
  295. testLook0();
  296. testLook1();
  297. testLook2();
  298. testLook3();
  299. testLook4();
  300. testGen0();
  301. testDerivative();
  302. testDC();
  303. testShaper0();
  304. testShaper1();
  305. testShaper2();
  306. testShaper3();
  307. testSplineExtremes();
  308. }