From 0f7f87770b6ce305b98393c977272ee5dd7811cd Mon Sep 17 00:00:00 2001 From: nuoun <10226424+nuoun@users.noreply.github.com> Date: Sat, 17 Aug 2024 20:46:11 +0200 Subject: [PATCH] Splits prelude into WTSE/Formula and adds it to WTSE (#7764) --- src/common/LuaSupport.cpp | 35 ++++-- src/common/LuaSupport.h | 21 +++- src/common/dsp/WavetableScriptEvaluator.cpp | 5 + .../modulators/FormulaModulationHelper.cpp | 8 +- .../dsp/modulators/FormulaModulationHelper.h | 1 - src/lua/CMakeLists.txt | 12 +- ...{surge_prelude.lua => formula_prelude.lua} | 0 ...lude_test.lua => formula_prelude_test.lua} | 0 src/lua/wtse_prelude.lua | 118 ++++++++++++++++++ src/surge-testrunner/UnitTestsLUA.cpp | 5 +- src/surge-xt/gui/overlays/LuaEditors.cpp | 5 +- 11 files changed, 178 insertions(+), 32 deletions(-) rename src/lua/{surge_prelude.lua => formula_prelude.lua} (100%) rename src/lua/{surge_prelude_test.lua => formula_prelude_test.lua} (100%) create mode 100644 src/lua/wtse_prelude.lua diff --git a/src/common/LuaSupport.cpp b/src/common/LuaSupport.cpp index 6c612ba4bb7..de3974632c6 100644 --- a/src/common/LuaSupport.cpp +++ b/src/common/LuaSupport.cpp @@ -175,10 +175,10 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L) lua_setfield(L, eidx, "print"); // add global tables - lua_getglobal(L, "surge"); - lua_setfield(L, eidx, "surge"); - lua_getglobal(L, "shared"); - lua_setfield(L, eidx, "shared"); + lua_getglobal(L, surgeTableName); + lua_setfield(L, eidx, surgeTableName); + lua_getglobal(L, sharedTableName); + lua_setfield(L, eidx, sharedTableName); // add whitelisted functions and modules std::vector sandboxWhitelist = {"pairs", "ipairs", "unpack", @@ -271,21 +271,32 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L) return true; } -bool Surge::LuaSupport::loadSurgePrelude(lua_State *s, const char *surgeTableName) +bool Surge::LuaSupport::loadSurgePrelude(lua_State *L, const std::string &lua_script) { #if HAS_LUA - auto guard = SGLD("loadPrologue", s); - // now load the surge library - auto &lua_script = LuaSources::surge_prelude; + auto guard = SGLD("loadSurgePrelude", L); + // Load the specified Lua script into the global table "surge" auto lua_size = lua_script.size(); - auto load_stat = luaL_loadbuffer(s, lua_script.c_str(), lua_size, lua_script.c_str()); - auto pcall = lua_pcall(s, 0, 1, 0); - lua_setglobal(s, surgeTableName); + auto status = luaL_loadbuffer(L, lua_script.c_str(), lua_size, lua_script.c_str()); + if (status != 0) + { + std::cout << "Error: Failed to load Lua file [ " << lua_script.c_str() << " ]" << std::endl; + return false; + } + auto pcall = lua_pcall(L, 0, 1, 0); + if (pcall != 0) + { + std::cout << "Error: Failed to run Lua file [ " << lua_script.c_str() << " ]" << std::endl; + return false; + } + lua_setglobal(L, surgeTableName); #endif return true; } -std::string Surge::LuaSupport::getSurgePrelude() { return LuaSources::surge_prelude; } +std::string Surge::LuaSupport::getFormulaPrelude() { return LuaSources::formula_prelude; } + +std::string Surge::LuaSupport::getWTSEPrelude() { return LuaSources::wtse_prelude; } Surge::LuaSupport::SGLD::~SGLD() { diff --git a/src/common/LuaSupport.h b/src/common/LuaSupport.h index b14b09b6b9c..84c7a614590 100644 --- a/src/common/LuaSupport.h +++ b/src/common/LuaSupport.h @@ -85,15 +85,20 @@ int parseStringDefiningMultipleFunctions(lua_State *s, const std::string &defini bool setSurgeFunctionEnvironment(lua_State *s); /* - * Call this function with a LUA state and a tablename and it will introduce the global - * 'surge' which is the surge prelude + * Call this function with a LUA state, the std::string at Surge::LuaSources and it will load the + * prelude in the table "surge" */ -bool loadSurgePrelude(lua_State *s, const char *surgeTableName); +bool loadSurgePrelude(lua_State *s, const std::string &lua_script); /* - * Call this function to get a string representation of the prelude + * Call this function to get a string representation of the Formula prelude */ -std::string getSurgePrelude(); +std::string getFormulaPrelude(); + +/* + * Call this function to get a string representation of the WTSE prelude + */ +std::string getWTSEPrelude(); /* * A little leak debugger. Make this on your stack and if you exit the @@ -118,6 +123,12 @@ struct SGLD int top; }; +/* + * Global table names + */ +static constexpr const char *surgeTableName{"surge"}; +static constexpr const char *sharedTableName{"shared"}; + } // namespace LuaSupport } // namespace Surge diff --git a/src/common/dsp/WavetableScriptEvaluator.cpp b/src/common/dsp/WavetableScriptEvaluator.cpp index 3e5e329802d..3a66a5e0119 100644 --- a/src/common/dsp/WavetableScriptEvaluator.cpp +++ b/src/common/dsp/WavetableScriptEvaluator.cpp @@ -22,6 +22,7 @@ #include "WavetableScriptEvaluator.h" #include "LuaSupport.h" +#include "lua/LuaSources.h" namespace Surge { @@ -42,6 +43,10 @@ std::vector evaluateScriptAtFrame(SurgeStorage *storage, const std::strin auto values = std::vector(); auto wg = Surge::LuaSupport::SGLD("WavetableScript::evaluate", L); + + // Load the WTSE prelude + Surge::LuaSupport::loadSurgePrelude(L, Surge::LuaSources::wtse_prelude); + std::string emsg; auto res = Surge::LuaSupport::parseStringDefiningFunction(L, eqn.c_str(), "generate", emsg); if (res) diff --git a/src/common/dsp/modulators/FormulaModulationHelper.cpp b/src/common/dsp/modulators/FormulaModulationHelper.cpp index 1b3ef6cf0de..90f1f2853a4 100644 --- a/src/common/dsp/modulators/FormulaModulationHelper.cpp +++ b/src/common/dsp/modulators/FormulaModulationHelper.cpp @@ -27,6 +27,7 @@ #include #include #include "fmt/core.h" +#include "lua/LuaSources.h" namespace Surge { @@ -81,12 +82,13 @@ bool prepareForEvaluation(SurgeStorage *storage, FormulaModulatorStorage *fs, Ev if (firstTimeThrough) { - // setup shared table + // Setup shared table lua_newtable(s.L); lua_setglobal(s.L, sharedTableName); - // setup prelude - Surge::LuaSupport::loadSurgePrelude(s.L, surgeTableName); + // Load the Formula prelude + Surge::LuaSupport::loadSurgePrelude(s.L, Surge::LuaSources::formula_prelude); + auto reserved0 = std::string(R"FN( function surge_reserved_formula_error_stub(m) return 0; diff --git a/src/common/dsp/modulators/FormulaModulationHelper.h b/src/common/dsp/modulators/FormulaModulationHelper.h index 455358ea200..61bceceba18 100644 --- a/src/common/dsp/modulators/FormulaModulationHelper.h +++ b/src/common/dsp/modulators/FormulaModulationHelper.h @@ -44,7 +44,6 @@ struct GlobalData }; static constexpr int max_formula_outputs{max_lfo_indices}; -static constexpr const char *surgeTableName{"surge"}; static constexpr const char *sharedTableName{"shared"}; struct EvaluatorState diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt index 24a98cded85..42c5faa89a8 100644 --- a/src/lua/CMakeLists.txt +++ b/src/lua/CMakeLists.txt @@ -7,15 +7,17 @@ project(surge-lua-src) # 1. Create myscript.lua # 2. Add it to the list below # 3. myscript.lua is now a std::string at Surge::LuaSources::myscript + set(lua_sources - surge_prelude.lua - surge_prelude_test.lua - ) + formula_prelude.lua + formula_prelude_test.lua + wtse_prelude.lua +) set(generated_sources ${CMAKE_CURRENT_BINARY_DIR}/LuaSources.cpp ${CMAKE_CURRENT_BINARY_DIR}/include/lua/LuaSources.h - ) +) add_library(${PROJECT_NAME} ${generated_sources}) @@ -23,5 +25,5 @@ add_custom_command(OUTPUT ${generated_sources} COMMAND ${CMAKE_COMMAND} -P lua2cpp.cmake ${CMAKE_CURRENT_BINARY_DIR} LuaSources.cpp lua/LuaSources.h Surge::LuaSources ${lua_sources} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS lua2cpp.cmake ${lua_sources} - ) +) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/src/lua/surge_prelude.lua b/src/lua/formula_prelude.lua similarity index 100% rename from src/lua/surge_prelude.lua rename to src/lua/formula_prelude.lua diff --git a/src/lua/surge_prelude_test.lua b/src/lua/formula_prelude_test.lua similarity index 100% rename from src/lua/surge_prelude_test.lua rename to src/lua/formula_prelude_test.lua diff --git a/src/lua/wtse_prelude.lua b/src/lua/wtse_prelude.lua new file mode 100644 index 00000000000..42707434633 --- /dev/null +++ b/src/lua/wtse_prelude.lua @@ -0,0 +1,118 @@ +-- This document is loaded in each Surge XT session and provides a set of built-in helpers +-- we've found handy when generating wavetables. Consider it as a library of functions. +-- For each official update of Surge XT we will freeze the state of the prelude as stable +-- and not break included functions after that. +-- +-- If you have ideas for other useful functions that could be added here, by all means +-- contact us over GitHub or Discord and let us know! + + +local surge = {} + + +--- MATH FUNCTIONS --- + + +-- parity function returns 0 for even numbers and 1 for odd numbers +function math.parity(x) + return (x % 2 == 1 and 1) or 0 +end + +-- signum function returns -1 for negative numbers, 0 for zero, 1 for positive numbers +function math.sgn(x) + return (x > 0 and 1) or (x < 0 and -1) or 0 +end + +-- sign function returns -1 for negative numbers and 1 for positive numbers or zero +function math.sign(x) + return (x < 0 and -1) or 1 +end + +-- linearly interpolates value from in range to out range +function math.rescale(value, in_min, in_max, out_min, out_max) + return (((value - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min +end + +-- returns the norm of the two components (hypotenuse) +function math.norm(a, b) + return math.sqrt(a ^ 2 + b ^ 2) +end + +-- returns the absolute range between the two numbers +function math.range(a, b) + return math.abs(a - b) +end + + +-- WTSE MATH FUNCTIONS - NON-FINAL AND WIP + + +-- returns a table with the cumulative product of the elements in the input table +function math.cumprod(t) + local o = {} + o[1] = t[1] + for i = 2, #t do + o[i] = o[i - 1] * t[i] + end + return o +end + +-- returns a table with the cumulative sum of the elements in the input table +function math.cumsum(t) + local o = {} + o[1] = t[1] + for i = 2, #t do + o[i] = o[i - 1] + t[i] + end + return o +end + +-- returns a table containing num_points linearly spaced numbers from start_point to end_point +function math.linspace(start_point, end_point, num_points) + local t = {} + local step = (end_point - start_point) / (num_points - 1) + for i = 1, num_points do + t[i] = start_point + (i - 1) * step + end + return t +end + +-- returns a table containing num_points logarithmically spaced numbers from 10^start_point to 10^end_point +function math.logspace(start_point, end_point, num_points) + local t = {} + local step = (end_point - start_point) / (num_points - 1) + for i = 1, num_points do + local exponent = start_point + (i - 1) * step + t[i] = 10 ^ exponent + end + return t +end + +-- returns the maximum absolute value found in the input table +function math.maxAbsFromTable(t) + local o = 0 + for i = 1, #t do + local a = abs(t[i]) + if a > o then o = a end + end + return o +end + +-- returns the normalized sinc function for a table of input values +function math.sinc(t) + local o = {} + for i, x in ipairs(t) do + if x == 0 then + o[i] = 1 + else + o[i] = math.sin(math.pi * x) / (math.pi * x) + end + end + return o +end + + +--- END --- + + +return surge \ No newline at end of file diff --git a/src/surge-testrunner/UnitTestsLUA.cpp b/src/surge-testrunner/UnitTestsLUA.cpp index 79d3b8e96e3..f2f5e37b343 100644 --- a/src/surge-testrunner/UnitTestsLUA.cpp +++ b/src/surge-testrunner/UnitTestsLUA.cpp @@ -159,12 +159,11 @@ TEST_CASE("Surge Prelude", "[lua]") REQUIRE(L); luaL_openlibs(L); - static constexpr const char *surgeTableName{"surge"}; - REQUIRE(Surge::LuaSupport::loadSurgePrelude(L, surgeTableName)); + REQUIRE(Surge::LuaSupport::loadSurgePrelude(L, Surge::LuaSources::formula_prelude)); std::string emsg; REQUIRE(Surge::LuaSupport::parseStringDefiningFunction( - L, Surge::LuaSources::surge_prelude_test, "test", emsg)); + L, Surge::LuaSources::formula_prelude_test, "test", emsg)); Surge::LuaSupport::setSurgeFunctionEnvironment(L); auto pcall = lua_pcall(L, 0, 1, 0); diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index d04b1ad7774..6a37700765e 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -744,7 +744,7 @@ FormulaModulatorEditor::FormulaModulatorEditor(SurgeGUIEditor *ed, SurgeStorage mainDocument->insertText(0, fs->formulaString); preludeDocument = std::make_unique(); - preludeDocument->insertText(0, Surge::LuaSupport::getSurgePrelude()); + preludeDocument->insertText(0, Surge::LuaSupport::getFormulaPrelude()); preludeDisplay = std::make_unique(*preludeDocument, tokenizer.get()); preludeDisplay->setTabSize(4, true); @@ -1282,9 +1282,8 @@ WavetableScriptEditor::WavetableScriptEditor(SurgeGUIEditor *ed, SurgeStorage *s mainDocument->insertText(0, osc->wavetable_formula); } - // FIXME: split prelude into Formula and WTSE preludeDocument = std::make_unique(); - preludeDocument->insertText(0, Surge::LuaSupport::getSurgePrelude()); + preludeDocument->insertText(0, Surge::LuaSupport::getWTSEPrelude()); preludeDisplay = std::make_unique(*preludeDocument, tokenizer.get()); preludeDisplay->setTabSize(4, true);