The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

154 lines
5.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce::dsp
  19. {
  20. template <typename FloatType>
  21. LookupTable<FloatType>::LookupTable()
  22. {
  23. data.resize (1);
  24. }
  25. template <typename FloatType>
  26. LookupTable<FloatType>::LookupTable (const std::function<FloatType (size_t)>& functionToApproximate,
  27. size_t numPointsToUse)
  28. {
  29. initialise (functionToApproximate, numPointsToUse);
  30. }
  31. //==============================================================================
  32. template <typename FloatType>
  33. void LookupTable<FloatType>::initialise (const std::function<FloatType (size_t)>& functionToApproximate,
  34. size_t numPointsToUse)
  35. {
  36. data.resize (static_cast<int> (getRequiredBufferSize (numPointsToUse)));
  37. for (size_t i = 0; i < numPointsToUse; ++i)
  38. {
  39. auto value = functionToApproximate (i);
  40. jassert (! std::isnan (value));
  41. jassert (! std::isinf (value));
  42. // Make sure functionToApproximate returns a sensible value for the entire specified range.
  43. // E.g., this won't work for zero: [] (size_t i) { return 1.0f / i; }
  44. data.getReference (static_cast<int> (i)) = value;
  45. }
  46. prepare();
  47. }
  48. template <typename FloatType>
  49. void LookupTable<FloatType>::prepare() noexcept
  50. {
  51. auto guardIndex = static_cast<int> (getGuardIndex());
  52. data.getReference (guardIndex) = data.getUnchecked (guardIndex - 1);
  53. }
  54. template <typename FloatType>
  55. void LookupTableTransform<FloatType>::initialise (const std::function<FloatType (FloatType)>& functionToApproximate,
  56. FloatType minInputValueToUse,
  57. FloatType maxInputValueToUse,
  58. size_t numPoints)
  59. {
  60. jassert (maxInputValueToUse > minInputValueToUse);
  61. minInputValue = minInputValueToUse;
  62. maxInputValue = maxInputValueToUse;
  63. scaler = FloatType (numPoints - 1) / (maxInputValueToUse - minInputValueToUse);
  64. offset = -minInputValueToUse * scaler;
  65. const auto initFn = [functionToApproximate, minInputValueToUse, maxInputValueToUse, numPoints] (size_t i)
  66. {
  67. return functionToApproximate (
  68. jlimit (
  69. minInputValueToUse, maxInputValueToUse,
  70. jmap (FloatType (i), FloatType (0), FloatType (numPoints - 1), minInputValueToUse, maxInputValueToUse))
  71. );
  72. };
  73. lookupTable.initialise (initFn, numPoints);
  74. }
  75. //==============================================================================
  76. template <typename FloatType>
  77. double LookupTableTransform<FloatType>::calculateMaxRelativeError (const std::function<FloatType (FloatType)>& functionToApproximate,
  78. FloatType minInputValue,
  79. FloatType maxInputValue,
  80. size_t numPoints,
  81. size_t numTestPoints)
  82. {
  83. jassert (maxInputValue > minInputValue);
  84. if (numTestPoints == 0)
  85. numTestPoints = 100 * numPoints; // use default
  86. LookupTableTransform transform (functionToApproximate, minInputValue, maxInputValue, numPoints);
  87. double maxError = 0;
  88. for (size_t i = 0; i < numTestPoints; ++i)
  89. {
  90. auto inputValue = jmap (FloatType (i), FloatType (0), FloatType (numTestPoints - 1), minInputValue, maxInputValue);
  91. auto approximatedOutputValue = transform.processSample (inputValue);
  92. auto referenceOutputValue = functionToApproximate (inputValue);
  93. maxError = jmax (maxError, calculateRelativeDifference ((double) referenceOutputValue, (double) approximatedOutputValue));
  94. }
  95. return maxError;
  96. }
  97. //==============================================================================
  98. template <typename FloatType>
  99. double LookupTableTransform<FloatType>::calculateRelativeDifference (double x, double y) noexcept
  100. {
  101. static const auto eps = std::numeric_limits<double>::min();
  102. auto absX = std::abs (x);
  103. auto absY = std::abs (y);
  104. auto absDiff = std::abs (x - y);
  105. if (absX < eps)
  106. {
  107. if (absY >= eps)
  108. return absDiff / absY;
  109. return absDiff; // return the absolute error if both numbers are too close to zero
  110. }
  111. return absDiff / std::min (absX, absY);
  112. }
  113. //==============================================================================
  114. template class LookupTable<float>;
  115. template class LookupTable<double>;
  116. template class LookupTableTransform<float>;
  117. template class LookupTableTransform<double>;
  118. } // namespace juce::dsp