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.

505 lines
14KB

  1. #include "NonUniformLookupTable.h"
  2. #include "asserts.h"
  3. #include "AudioMath.h"
  4. #include "LookupTable.h"
  5. #include "LookupTableFactory.h"
  6. #include "NonUniformLookupTable.h"
  7. #include "ObjectCache.h"
  8. #include "Super.h"
  9. #include <assert.h>
  10. #include <iostream>
  11. using namespace std;
  12. // test that we can call all the functions
  13. template<typename T>
  14. static void test0()
  15. {
  16. LookupTableParams<T> p;
  17. const int tableSize = 512;
  18. std::function<double(double)> f = [](double d) {
  19. return 0;
  20. };
  21. LookupTable<T>::init(p, tableSize, 0, 1, f);
  22. LookupTable<T>::lookup(p, 0);
  23. }
  24. // test that simple lookup works
  25. template<typename T>
  26. static void test1()
  27. {
  28. LookupTableParams<T> p;
  29. const int tableSize = 512;
  30. std::function<double(double)> f = [](double d) {
  31. return 100;
  32. };
  33. LookupTable<T>::init(p, tableSize, 0, 1, f);
  34. assert(LookupTable<T>::lookup(p, 0) == 100);
  35. assert(LookupTable<T>::lookup(p, 1) == 100);
  36. assert(LookupTable<T>::lookup(p, T(.342)) == 100);
  37. }
  38. // test that sin works
  39. template<typename T>
  40. static void test2()
  41. {
  42. LookupTableParams<T> p;
  43. const int tableSize = 512;
  44. std::function<double(double)> f = [](double d) {
  45. return std::sin(d);
  46. };
  47. LookupTable<T>::init(p, tableSize, 0, 1, f);
  48. const T tolerance = T(0.000001);
  49. for (double d = 0; d < 1; d += .0001) {
  50. T output = LookupTable<T>::lookup(p, T(d));
  51. const bool t = AudioMath::closeTo(output, std::sin(d), tolerance);
  52. if (!t) {
  53. cout << "failing with expected=" << std::sin(d) << " actual=" << output << " delta=" << std::abs(output - std::sin(d));
  54. assert(false);
  55. }
  56. }
  57. }
  58. // test that sin works on domain 10..32
  59. template<typename T>
  60. static void test3()
  61. {
  62. LookupTableParams<T> p;
  63. const int tableSize = 16;
  64. std::function<double(double)> f = [](double d) {
  65. const double s = (d - 10) / 3;
  66. return std::sin(s);
  67. };
  68. LookupTable<T>::init(p, tableSize, 10, 13, f);
  69. const T tolerance = T(0.01);
  70. for (double d = 10; d < 13; d += .0001) {
  71. const T output = LookupTable<T>::lookup(p, T(d));
  72. const T expected = (T) std::sin((d - 10.0) / 3);
  73. const bool t = AudioMath::closeTo(output, expected, tolerance);
  74. if (!t) {
  75. cout << "failing with d=" << d << " expected=" << expected << " actual=" << output << " delta=" << std::abs(output - std::sin(d));
  76. assert(false);
  77. }
  78. }
  79. }
  80. // test that sin at extremes works
  81. template<typename T>
  82. static void test4()
  83. {
  84. LookupTableParams<T> exponential;
  85. const T xMin = -5;
  86. const T xMax = 5;
  87. std::function<double(double)> expFunc = AudioMath::makeFunc_Exp(-5, 5, 2, 2000);
  88. LookupTable<T>::init(exponential, 128, -5, 5, expFunc);
  89. // Had to loosen tolerance to pass with windows gcc. Is there a problem
  90. // with precision, or is this expected with fast math?
  91. const T tolerance = T(0.0003);
  92. T outputLow = LookupTable<T>::lookup(exponential, xMin);
  93. T outputHigh = LookupTable<T>::lookup(exponential, xMax);
  94. bool t = AudioMath::closeTo(outputLow, 2, tolerance);
  95. if (!t) {
  96. cout << "failing l with expected=" << 2 << " actual=" << outputLow << " delta=" << std::abs(outputLow - 2) << std::endl;
  97. assert(false);
  98. }
  99. t = AudioMath::closeTo(outputHigh, 2000, tolerance);
  100. if (!t) {
  101. cout << "failing h with expected=" << 2000 << " actual=" << outputHigh << " delta=" << std::abs(outputHigh - 2000) << std::endl;
  102. assert(false);
  103. }
  104. }
  105. template<typename T>
  106. static void testDiscrete1()
  107. {
  108. LookupTableParams<T> lookup;
  109. T y[] = {0, 10};
  110. LookupTable<T>::initDiscrete(lookup, 2, y);
  111. assertEQ(LookupTable<T>::lookup(lookup, 0), 0);
  112. assertEQ(LookupTable<T>::lookup(lookup, .5), 5);
  113. assertEQ(LookupTable<T>::lookup(lookup, 1), 10);
  114. assertEQ(LookupTable<T>::lookup(lookup, T(.1)), 1);
  115. assertClose(LookupTable<T>::lookup(lookup, T(.01)), T(.1), .00001);
  116. }
  117. template<typename T>
  118. static void testDiscrete2()
  119. {
  120. LookupTableParams<T> lookup;
  121. T y[] = {100, 100.5, 2000, -10};
  122. LookupTable<T>::initDiscrete(lookup, 4, y);
  123. assertEQ(LookupTable<T>::lookup(lookup, 0), 100);
  124. assertEQ(LookupTable<T>::lookup(lookup, 1), 100.5);
  125. assertEQ(LookupTable<T>::lookup(lookup, 2), 2000);
  126. assertEQ(LookupTable<T>::lookup(lookup, 3), -10);
  127. assertEQ(LookupTable<T>::lookup(lookup, 2.5), 1000 - 5);
  128. }
  129. template<typename T>
  130. static void testExpSimpleLookup()
  131. {
  132. LookupTableParams<T> lookup;
  133. LookupTableFactory<T>::makeExp2(lookup);
  134. const double xMin = LookupTableFactory<T>::exp2XMin();
  135. const double xMax = LookupTableFactory<T>::exp2XMax();
  136. assert(5 > xMin);
  137. assert(11 < xMax);
  138. assertClose(LookupTable<T>::lookup(lookup, 5), std::pow(2, 5), .01);
  139. assertClose(LookupTable<T>::lookup(lookup, 11), std::pow(2, 11), 2); // TODO: tighten
  140. }
  141. // test that extreme inputs is clamped
  142. template<typename T>
  143. static void testExpRange()
  144. {
  145. LookupTableParams<T> lookup;
  146. LookupTable<T>::makeExp2(lookup);
  147. auto k1 = LookupTable<T>::lookup(lookup, -1);
  148. auto k2 = LookupTable<T>::lookup(lookup, 11);
  149. assertClose(LookupTable<T>::lookup(lookup, -1), LookupTable<T>::lookup(lookup, 0), .01);
  150. assertClose(LookupTable<T>::lookup(lookup, 11), LookupTable<T>::lookup(lookup, 10), .01);
  151. assertClose(LookupTable<T>::lookup(lookup, -100), LookupTable<T>::lookup(lookup, 0), .01);
  152. assertClose(LookupTable<T>::lookup(lookup, 1100), LookupTable<T>::lookup(lookup, 10), .01);
  153. }
  154. template<typename T>
  155. static void testExpTolerance(T centsTolerance)
  156. {
  157. const T xMin = (T) LookupTableFactory<T>::exp2XMin();
  158. const T xMax = (T) LookupTableFactory<T>::exp2XMax();
  159. LookupTableParams<T> table;
  160. LookupTableFactory<T>::makeExp2(table);
  161. for (T x = xMin; x <= xMax; x += T(.001)) {
  162. T y = LookupTable<T>::lookup(table, x); // and back
  163. double accurate = std::pow(2.0, x);
  164. double errorCents = AudioMath::acents(y, accurate);
  165. assertClose(errorCents, 0, centsTolerance);
  166. }
  167. }
  168. template<typename T>
  169. static void testExpTolerance2(T centsTolerance, T hzTolerance)
  170. {
  171. const T xMin = (T) LookupTableFactory<T>::exp2XMin();
  172. const T xMax = (T) LookupTableFactory<T>::exp2XMax();
  173. LookupTableParams<T> table;
  174. LookupTableFactory<T>::makeExp2(table);
  175. for (T x = xMin; x <= xMax; x += T(.001)) {
  176. T y = LookupTable<T>::lookup(table, x); // and back
  177. double accurate = std::pow(2.0, x);
  178. double errorCents = AudioMath::acents(y, accurate);
  179. assertClose(errorCents, 0, centsTolerance);
  180. const double errorHz = std::abs(y - accurate);
  181. assertClose(errorHz, 0, hzTolerance);
  182. }
  183. }
  184. template<typename T>
  185. static void testExp2HiTolerance(T centsTolerance, T hzTolerance)
  186. {
  187. const T xMin = (T) LookupTableFactory<T>::exp2ExHighXMin();
  188. const T xMax = (T) LookupTableFactory<T>::exp2ExHighXMax();
  189. LookupTableParams<T> table;
  190. LookupTableFactory<T>::makeExp2ExHigh(table);
  191. for (T x = xMin; x <= xMax; x += T(.001)) {
  192. T y = LookupTable<T>::lookup(table, x); // and back
  193. double accurate = std::pow(2.0, x);
  194. double errorCents = AudioMath::acents(y, accurate);
  195. assertClose(errorCents, 0, centsTolerance);
  196. const double errorHz = std::abs(y - accurate);
  197. // relax limits at high freq
  198. T curHzTol = hzTolerance;
  199. if (y > 10000) curHzTol *= 10;
  200. if (y > 5000) curHzTol *= 4;
  201. else if (y > 3000) curHzTol *= 2;
  202. if (y > 2000) curHzTol *= 1.1f;
  203. assertClose(errorHz, 0, curHzTol);
  204. }
  205. }
  206. template<typename T>
  207. static void testExp2LowTolerance(T centsTolerance, T hzTolerance)
  208. {
  209. const T xMin = (T) LookupTableFactory<T>::exp2ExLowXMin();
  210. const T xMax = (T) LookupTableFactory<T>::exp2ExLowXMax();
  211. LookupTableParams<T> table;
  212. LookupTableFactory<T>::makeExp2ExLow(table);
  213. for (T x = xMin; x <= xMax; x += T(.001)) {
  214. T y = LookupTable<T>::lookup(table, x); // and back
  215. double accurate = std::pow(2.0, x);
  216. double errorCents = AudioMath::acents(y, accurate);
  217. assertClose(errorCents, 0, centsTolerance);
  218. const double errorHz = std::abs(y - accurate);
  219. assertClose(errorHz, 0, hzTolerance);
  220. }
  221. }
  222. template<typename T>
  223. static void testExp2CombinedTolerance(T centsTolerance, T hzTolerance)
  224. {
  225. const T xMin = (T) LookupTableFactory<T>::exp2ExLowXMin();
  226. const T xMax = (T) LookupTableFactory<T>::exp2ExHighXMax();
  227. // LookupTableParams<T> table;
  228. auto exp = ObjectCache<T>::getExp2Ex();
  229. for (T x = xMin; x <= xMax; x += T(.001)) {
  230. T y = exp(x);
  231. double accurate = std::pow(2.0, x);
  232. double errorCents = AudioMath::acents(y, accurate);
  233. assertClose(errorCents, 0, centsTolerance);
  234. const double errorHz = std::abs(y - accurate);
  235. T curHzTol = hzTolerance;
  236. if (y > 10000) curHzTol *= 10;
  237. if (y > 5000) curHzTol *= 4;
  238. else if (y > 3000) curHzTol *= 2;
  239. if (y > 2000) curHzTol *= 1.1f;
  240. assertClose(errorHz, 0, curHzTol);
  241. }
  242. }
  243. template <typename T>
  244. static void testBipolarSimpleLookup()
  245. {
  246. LookupTableParams<T> lookup;
  247. LookupTableFactory<T>::makeBipolarAudioTaper(lookup);
  248. assertClose(LookupTable<T>::lookup(lookup, 0), 0, .01);
  249. assertClose(LookupTable<T>::lookup(lookup, 1), 1, .01);
  250. assertClose(LookupTable<T>::lookup(lookup, -1), -1, .01);
  251. }
  252. template <typename T>
  253. static void testAudioTaperSimpleLookup()
  254. {
  255. LookupTableParams<T> lookup;
  256. LookupTableFactory<T>::makeAudioTaper(lookup);
  257. assertClose(LookupTable<T>::lookup(lookup, 0), 0, .01);
  258. assertClose(LookupTable<T>::lookup(lookup, 1), 1, .01);
  259. }
  260. template <typename T>
  261. static void testBipolarTolerance()
  262. {
  263. LookupTableParams<T> lookup;
  264. LookupTableFactory<T>::makeBipolarAudioTaper(lookup);
  265. const double toleratedError = 1 - AudioMath::gainFromDb(-.1);// let's go for one db.
  266. assert(toleratedError > 0);
  267. auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory<T>::audioTaperKnee());
  268. auto refFuncNeg = [refFuncPos](double x) {
  269. assert(x <= 0);
  270. return -refFuncPos(-x);
  271. };
  272. for (double x = -1; x < 1; x += .001) {
  273. const T test = LookupTable<T>::lookup(lookup, (T) x);
  274. T ref = 1234;
  275. if (x < 0) {
  276. ref = (T) refFuncNeg(x);
  277. } else {
  278. ref = (T) refFuncPos(x);
  279. }
  280. assertClose(test, ref, toleratedError);
  281. }
  282. }
  283. template <typename T>
  284. static void testAudioTaperTolerance()
  285. {
  286. LookupTableParams<T> lookup;
  287. LookupTableFactory<T>::makeAudioTaper(lookup);
  288. const double toleratedError = 1 - AudioMath::gainFromDb(-.1);// let's go for one db.
  289. assert(toleratedError > 0);
  290. auto refFuncPos = AudioMath::makeFunc_AudioTaper(LookupTableFactory<T>::audioTaperKnee());
  291. for (double x = 0; x < 1; x += .001) {
  292. const T test = LookupTable<T>::lookup(lookup, (T) x);
  293. T ref = (T) refFuncPos(x);
  294. assertClose(test, ref, toleratedError);
  295. }
  296. }
  297. /**
  298. * Tests for non-uniform lookup
  299. */
  300. template <typename T>
  301. static void testNonUniform1()
  302. {
  303. NonUniformLookupTableParams<T> params;
  304. NonUniformLookupTable<T>::addPoint(params, 0, 0);
  305. NonUniformLookupTable<T>::addPoint(params, 1, 1);
  306. NonUniformLookupTable<T>::addPoint(params, 2, 2);
  307. NonUniformLookupTable<T>::finalize(params);
  308. const T result = NonUniformLookupTable<T>::lookup(params, 1);
  309. assertClose(result, 1, .000001);
  310. }
  311. template <typename T>
  312. static void testNonUniform2()
  313. {
  314. NonUniformLookupTableParams<T> params;
  315. NonUniformLookupTable<T>::addPoint(params, 0, 0);
  316. NonUniformLookupTable<T>::addPoint(params, 1, 1);
  317. NonUniformLookupTable<T>::addPoint(params, 2, 2);
  318. NonUniformLookupTable<T>::finalize(params);
  319. const T result = NonUniformLookupTable<T>::lookup(params, 1.1f);
  320. assertClose(result, 1.1f, .000001);
  321. }
  322. template <typename T>
  323. static void testNonUniform3()
  324. {
  325. NonUniformLookupTableParams<T> params;
  326. NonUniformLookupTable<T>::addPoint(params, 0, 0);
  327. NonUniformLookupTable<T>::addPoint(params, 1, 1);
  328. NonUniformLookupTable<T>::addPoint(params, 2, 2);
  329. NonUniformLookupTable<T>::finalize(params);
  330. T result = NonUniformLookupTable<T>::lookup(params, -100);
  331. assertClose(result, 0, .000001);
  332. result = NonUniformLookupTable<T>::lookup(params, -.1f);
  333. assertClose(result, 0, .000001);
  334. result = NonUniformLookupTable<T>::lookup(params, 100);
  335. assertClose(result, 2, .000001);
  336. result = NonUniformLookupTable<T>::lookup(params, 2.1f);
  337. assertClose(result, 2, .000001);
  338. result = NonUniformLookupTable<T>::lookup(params, 2);
  339. assertClose(result, 2, .000001);
  340. result = NonUniformLookupTable<T>::lookup(params, 0);
  341. assertClose(result, 0, .000001);
  342. result = NonUniformLookupTable<T>::lookup(params, .1f);
  343. assertClose(result, .1f, .000001);
  344. result = NonUniformLookupTable<T>::lookup(params, 1);
  345. assertClose(result, 1, .000001);
  346. result = NonUniformLookupTable<T>::lookup(params, 1.9f);
  347. assertClose(result, 1.9f, .000001);
  348. }
  349. template <typename T>
  350. static void testNonUniform4()
  351. {
  352. NonUniformLookupTableParams<T> params;
  353. NonUniformLookupTable<T>::addPoint(params, 0, 0);
  354. NonUniformLookupTable<T>::addPoint(params, 1, 1);
  355. NonUniformLookupTable<T>::addPoint(params, 2, 21);
  356. NonUniformLookupTable<T>::finalize(params);
  357. const T result = NonUniformLookupTable<T>::lookup(params, 1.5f);
  358. assertClose(result, 11.f, .000001);
  359. }
  360. static void testDetune()
  361. {
  362. SawtoothDetuneCurve curve;
  363. float x = curve.getDetuneFactor(0);
  364. assertEQ(x, 0);
  365. x = curve.getDetuneFactor(1);
  366. assertEQ(x, 1);
  367. x = curve.getDetuneFactor(.5);
  368. assertLT(x, .1);
  369. float last = - .00001f;
  370. for (float f = 0; f <= 1; f += .05f) {
  371. x = curve.getDetuneFactor(f);
  372. assertGT(x, last);
  373. last = x;
  374. }
  375. }
  376. template<typename T>
  377. static void test()
  378. {
  379. test0<T>();
  380. test1<T>();
  381. test2<T>();
  382. test3<T>();
  383. test4<T>();
  384. testDiscrete1<T>();
  385. testDiscrete2<T>();
  386. testExpSimpleLookup<T>();
  387. testExpTolerance<T>(1); // 1 cent
  388. testExp2HiTolerance<T>(.125, T(.05));
  389. testExp2LowTolerance<T>(.125, T(.05));
  390. testExp2CombinedTolerance<T>(.125, T(.5));
  391. testBipolarSimpleLookup<T>();
  392. testBipolarTolerance<T>();
  393. testAudioTaperSimpleLookup<T>();
  394. testAudioTaperTolerance<T>();
  395. testNonUniform1<T>();
  396. testNonUniform2<T>();
  397. testNonUniform3<T>();
  398. testNonUniform4<T>();
  399. }
  400. void testLookupTable()
  401. {
  402. test<double>();
  403. test<float>();
  404. testDetune();
  405. }