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.

168 lines
4.1KB

  1. #pragma once
  2. #include <memory>
  3. #include <emmintrin.h>
  4. #include <functional>
  5. template <typename T> class LookupTableParams;
  6. /* Lookup table with evenly spaced lookup "bins"
  7. * Uses linear interpolation
  8. */
  9. // TODO: templatize on size?
  10. template <typename T>
  11. class LookupTable
  12. {
  13. public:
  14. static void init(LookupTableParams<T>& params, int bins, T xMin, T xMax, std::function<double(double)> f);
  15. static T lookup(const LookupTableParams<T>& params, T input);
  16. private:
  17. static int cvtt(T *);
  18. #ifdef _DEBUG
  19. static void checkInput(const LookupTableParams<T>& params, const T *in, int sampleFrames)
  20. {
  21. for (int i = 0; i < sampleFrames; ++i) {
  22. f_t input = in[i];
  23. assert(input >= params.xMin && input <= params.xMax);
  24. }
  25. }
  26. #else
  27. #define checkInput __noop
  28. #endif
  29. };
  30. template<typename T>
  31. inline T LookupTable<T>::lookup(const LookupTableParams<T>& params, T input)
  32. {
  33. assert(params.isValid());
  34. assert(input >= params.xMin && input <= params.xMax);
  35. // need to scale by bins
  36. T scaledInput = input * params.a + params.b;
  37. assert(params.a != 0);
  38. int input_int = cvtt(&scaledInput);
  39. T input_float = scaledInput - input_int;
  40. // Unfortunately, when we use float instead of doubles,
  41. // numeric precision issues get us "out of range". So
  42. // here we clamp to range. It would be more efficient if we didn't do this.
  43. // Perhaps the calculation of a and b could be done so this can't happen?
  44. if (input_float < 0) {
  45. input_float = 0;
  46. }
  47. else if (input_float > 1) {
  48. input_float = 1;
  49. }
  50. assert(input_float >= 0 && input_float <= 1);
  51. assert(input_int >= 0 && input_int <= params.numBins_i);
  52. T * entry = params.entries + (2 * input_int);
  53. T x = entry[0];
  54. x += input_float * entry[1];
  55. return x;
  56. }
  57. template<typename T>
  58. inline void LookupTable<T>::init(LookupTableParams<T>& params,
  59. int bins, T x0In, T x1In, std::function<double(double)> f)
  60. {
  61. params.alloc(bins);
  62. // f(x0 = ax + 0 == index
  63. // f(x0) = 0
  64. // f(x1) = bins
  65. params.a = (T) bins / (x1In - x0In);
  66. params.b = -params.a * x0In;
  67. if (x0In == 0) assert(params.b == 0);
  68. assert((params.a * x0In + params.b) == 0);
  69. assert((params.a * x1In + params.b) == bins);
  70. for (int i = 0; i <= bins; ++i) {
  71. double x0 = (i - params.b) / params.a;
  72. double x1 = ((i + 1) - params.b) / params.a;
  73. double y0 = f(x0);
  74. double y1 = f(x1);
  75. double slope = y1 - y0;
  76. T * entry = params.entries + (2 * i);
  77. entry[0] = (T) y0;
  78. entry[1] = (T) slope;
  79. #if 0
  80. {
  81. char buf[512];
  82. sprintf(buf, "fill index[%d], x=%f value=%f slope=%f\n", i, x0, params.values[i], slope);
  83. DebugUtil::trace(buf);
  84. }
  85. #endif
  86. }
  87. params.xMin = x0In;
  88. params.xMax = x1In;
  89. }
  90. template<>
  91. inline int LookupTable<double>::cvtt(double* input)
  92. {
  93. auto x = _mm_load_sd(input);
  94. return _mm_cvttsd_si32(x);
  95. }
  96. template<>
  97. inline int LookupTable<float>::cvtt(float* input)
  98. {
  99. auto x = _mm_load_ss(input);
  100. return _mm_cvttss_si32(x);
  101. }
  102. template <typename T>
  103. class LookupTableParams
  104. {
  105. public:
  106. int numBins_i = 0; // numBins will be number of entries (pairs of values)
  107. T a = 0, b = 0; // lookup index = a * x + b, so domain of x can be whatever we want
  108. T * entries = 0; // each entry is value, slope
  109. T xMin = 0; // minimum x value we will accept as input
  110. T xMax = 0; // max x value we will accept as input
  111. LookupTableParams()
  112. {
  113. }
  114. LookupTableParams(const LookupTableParams&) = delete;
  115. LookupTableParams& operator=(const LookupTableParams&) = delete;
  116. ~LookupTableParams()
  117. {
  118. free(entries);
  119. }
  120. bool isValid() const
  121. {
  122. return ((entries != 0) &&
  123. (xMax > xMin) &&
  124. (numBins_i > 0)
  125. );
  126. }
  127. void alloc(int bins)
  128. {
  129. if (entries) free(entries);
  130. // allocate one extra, so we can index all the way to the end...
  131. entries = (T *) malloc((bins + 1) * 2 * sizeof(T));
  132. numBins_i = bins;
  133. a = 0;
  134. b = 0;
  135. }
  136. };