From 461e2e76d619c03831f058884555fb2ced47f67b Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Tue, 28 Dec 2021 03:18:25 -0500 Subject: [PATCH] Add all note names from c0 to b9 and accidentals like c# and cb to expression parser. --- src/Quantity.cpp | 41 +++++++++++++++++++++++++++++++---------- src/tinyexpr.c | 2 +- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/Quantity.cpp b/src/Quantity.cpp index 98b0b5f9..d3f126f3 100644 --- a/src/Quantity.cpp +++ b/src/Quantity.cpp @@ -2,8 +2,6 @@ #include #include -// for C4 and A4 frequencies -#include namespace rack { @@ -28,7 +26,9 @@ std::string Quantity::getDisplayValueString() { return string::f("%.*g", getDisplayPrecision(), math::normalizeZero(v)); } -/** Build-in variables for tinyexpr */ +/** Build-in variables for tinyexpr +Because formulas are lowercased for case-insensitivity, all variables must be lowercase. +*/ struct TeVariable { std::string name; double value; @@ -40,14 +40,35 @@ static void teVarsInit() { if (!teVars.empty()) return; - // Add variables + // Math variables teVariables.push_back({"inf", INFINITY}); - teVariables.push_back({"c", dsp::FREQ_C4}); - teVariables.push_back({"a", dsp::FREQ_A4}); - for (int i = 0; i <= 8; i++) - teVariables.push_back({string::f("c%d", i), dsp::FREQ_C4 * std::pow(2.0, i - 4)}); - for (int i = 0; i <= 8; i++) - teVariables.push_back({string::f("a%d", i), dsp::FREQ_A4 * std::pow(2.0, i - 4)}); + + // Note names + struct Note { + std::string name; + int semi; + }; + const static std::vector notes = { + {"c", 0}, + {"d", 2}, + {"e", 4}, + {"f", 5}, + {"g", 7}, + {"a", 9}, + {"b", 11}, + }; + for (const Note& note : notes) { + // Example: c, c#, and cb + teVariables.push_back({string::f("%s", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9) / 12.f)}); + teVariables.push_back({string::f("%s#", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9 + 1) / 12.f)}); + teVariables.push_back({string::f("%sb", note.name.c_str()), 440.f * std::pow(2.0, (note.semi - 9 - 1) / 12.f)}); + for (int oct = 0; oct <= 9; oct++) { + // Example: c4, c#4, and cb4 + 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)}); + 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)}); + 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)}); + } + } // Build teVars teVars.resize(teVariables.size()); diff --git a/src/tinyexpr.c b/src/tinyexpr.c index 92559402..9a5d3bbb 100755 --- a/src/tinyexpr.c +++ b/src/tinyexpr.c @@ -245,7 +245,7 @@ void next_token(state *s) { if (s->next[0] >= 'a' && s->next[0] <= 'z') { const char *start; start = s->next; - while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; + while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_') || (s->next[0] == '#')) s->next++; const te_variable *var = find_lookup(s, start, s->next - start); if (!var) var = find_builtin(start, s->next - start);