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.

397 lines
11KB

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