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.

187 lines
4.2KB

  1. #include <tinyexpr.h>
  2. #include <Quantity.hpp>
  3. #include <string.hpp>
  4. namespace rack {
  5. float Quantity::getDisplayValue() {
  6. return getValue();
  7. }
  8. void Quantity::setDisplayValue(float displayValue) {
  9. setValue(displayValue);
  10. }
  11. int Quantity::getDisplayPrecision() {
  12. return 5;
  13. }
  14. std::string Quantity::getDisplayValueString() {
  15. float v = getDisplayValue();
  16. if (std::isnan(v))
  17. return "NaN";
  18. return string::f("%.*g", getDisplayPrecision(), math::normalizeZero(v));
  19. }
  20. /** Build-in variables for tinyexpr
  21. Because formulas are lowercased for case-insensitivity, all variables must be lowercase.
  22. */
  23. struct TeVariable {
  24. std::string name;
  25. double value;
  26. };
  27. static std::vector<TeVariable> teVariables;
  28. static std::vector<te_variable> teVars;
  29. static void teVarsInit() {
  30. if (!teVars.empty())
  31. return;
  32. // Math variables
  33. teVariables.push_back({"inf", INFINITY});
  34. // Note names
  35. struct Note {
  36. std::string name;
  37. int semi;
  38. };
  39. const static std::vector<Note> notes = {
  40. {"c", 0},
  41. {"d", 2},
  42. {"e", 4},
  43. {"f", 5},
  44. {"g", 7},
  45. {"a", 9},
  46. {"b", 11},
  47. };
  48. for (const Note& note : notes) {
  49. // Example: c, c#, and cb
  50. teVariables.push_back({string::f("%s", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9) / 12.f)});
  51. teVariables.push_back({string::f("%s#", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9 + 1) / 12.f)});
  52. teVariables.push_back({string::f("%sb", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9 - 1) / 12.f)});
  53. for (int oct = 0; oct <= 9; oct++) {
  54. // Example: c4, c#4, and cb4
  55. teVariables.push_back({string::f("%s%d", note.name.c_str(), oct), 440.f * std::pow(2.0, oct - 4 + (note.semi - 9) / 12.f)});
  56. teVariables.push_back({string::f("%s#%d", note.name.c_str(), oct), 440.f * std::pow(2.0, oct - 4 + (note.semi - 9 + 1) / 12.f)});
  57. teVariables.push_back({string::f("%sb%d", note.name.c_str(), oct), 440.f * std::pow(2.0, oct - 4 + (note.semi - 9 - 1) / 12.f)});
  58. }
  59. }
  60. // Build teVars
  61. teVars.resize(teVariables.size());
  62. for (size_t i = 0; i < teVariables.size(); i++) {
  63. teVars[i].name = teVariables[i].name.c_str();
  64. teVars[i].address = &teVariables[i].value;
  65. teVars[i].type = TE_VARIABLE;
  66. teVars[i].context = NULL;
  67. }
  68. }
  69. void Quantity::setDisplayValueString(std::string s) {
  70. teVarsInit();
  71. // Uppercase letters aren't needed in formulas, so convert to lowercase in case user types INF or C4.
  72. s = string::lowercase(s);
  73. // Compile string with tinyexpr
  74. te_expr* expr = te_compile(s.c_str(), teVars.data(), teVars.size(), NULL);
  75. if (!expr)
  76. return;
  77. double result = te_eval(expr);
  78. te_free(expr);
  79. if (std::isnan(result))
  80. return;
  81. setDisplayValue(result);
  82. }
  83. std::string Quantity::getString() {
  84. std::string s;
  85. std::string label = getLabel();
  86. std::string valueString = getDisplayValueString() + getUnit();
  87. s += label;
  88. if (label != "" && valueString != "")
  89. s += ": ";
  90. s += valueString;
  91. return s;
  92. }
  93. void Quantity::reset() {
  94. setValue(getDefaultValue());
  95. }
  96. void Quantity::randomize() {
  97. if (isBounded())
  98. setScaledValue(random::uniform());
  99. }
  100. bool Quantity::isMin() {
  101. return getValue() <= getMinValue();
  102. }
  103. bool Quantity::isMax() {
  104. return getValue() >= getMaxValue();
  105. }
  106. void Quantity::setMin() {
  107. setValue(getMinValue());
  108. }
  109. void Quantity::setMax() {
  110. setValue(getMaxValue());
  111. }
  112. void Quantity::toggle() {
  113. setValue(isMin() ? getMaxValue() : getMinValue());
  114. }
  115. void Quantity::moveValue(float deltaValue) {
  116. setValue(getValue() + deltaValue);
  117. }
  118. float Quantity::getRange() {
  119. return getMaxValue() - getMinValue();
  120. }
  121. bool Quantity::isBounded() {
  122. return std::isfinite(getMinValue()) && std::isfinite(getMaxValue());
  123. }
  124. float Quantity::toScaled(float value) {
  125. if (!isBounded())
  126. return value;
  127. else if (getMinValue() == getMaxValue())
  128. return 0.f;
  129. else
  130. return math::rescale(value, getMinValue(), getMaxValue(), 0.f, 1.f);
  131. }
  132. float Quantity::fromScaled(float scaledValue) {
  133. if (!isBounded())
  134. return scaledValue;
  135. else
  136. return math::rescale(scaledValue, 0.f, 1.f, getMinValue(), getMaxValue());
  137. }
  138. void Quantity::setScaledValue(float scaledValue) {
  139. setValue(fromScaled(scaledValue));
  140. }
  141. float Quantity::getScaledValue() {
  142. return toScaled(getValue());
  143. }
  144. void Quantity::moveScaledValue(float deltaScaledValue) {
  145. if (!isBounded())
  146. moveValue(deltaScaledValue);
  147. else
  148. moveValue(deltaScaledValue * getRange());
  149. }
  150. } // namespace rack