diff --git a/src/common/LuaSupport.cpp b/src/common/LuaSupport.cpp index 29506e8ba92..134391f2dbb 100644 --- a/src/common/LuaSupport.cpp +++ b/src/common/LuaSupport.cpp @@ -129,7 +129,12 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L) // stack is now func > table // List of whitelisted functions and modules - std::vector sandboxWhitelist = {"ipairs", "error", "math", "surge"}; + std::vector sandboxWhitelist = {"ipairs", "error", "math", "surge", "shared"}; + /* + std::vector sandboxWhitelist = {"pairs", "ipairs", "next", "print", + "error", "math", "string", "table", + "bit", "setmetatable", "surge", "shared"}; + */ for (const auto &f : sandboxWhitelist) { @@ -164,6 +169,22 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L) // stack is now f>t>(m). Pop m lua_pop(L, 1); + // retrieve "shared" table and set entries to nil + lua_getglobal(L, "shared"); + if (lua_istable(L, -1)) + { + lua_pushnil(L); + while (lua_next(L, -2)) + { + lua_pop(L, 1); // pop value + lua_pushvalue(L, -1); // duplicate the key + lua_pushnil(L); + lua_settable(L, -4); // clear the key + } + } + // pop the retrieved value (either table or nil) from the stack + lua_pop(L, 1); + // and now we are back to f>t so we can setfenv it lua_setfenv(L, -2); @@ -173,7 +194,7 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L) return true; } -bool Surge::LuaSupport::loadSurgePrelude(lua_State *s) +bool Surge::LuaSupport::loadSurgePrelude(lua_State *s, const char *surgeTableName) { #if HAS_LUA auto guard = SGLD("loadPrologue", s); @@ -182,7 +203,7 @@ bool Surge::LuaSupport::loadSurgePrelude(lua_State *s) 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, "surge"); + lua_setglobal(s, surgeTableName); #endif return true; } diff --git a/src/common/LuaSupport.h b/src/common/LuaSupport.h index d36f365a726..b14b09b6b9c 100644 --- a/src/common/LuaSupport.h +++ b/src/common/LuaSupport.h @@ -85,10 +85,10 @@ int parseStringDefiningMultipleFunctions(lua_State *s, const std::string &defini bool setSurgeFunctionEnvironment(lua_State *s); /* - * Call this function with a LUA state and it will introduce the global + * Call this function with a LUA state and a tablename and it will introduce the global * 'surge' which is the surge prelude */ -bool loadSurgePrelude(lua_State *s); +bool loadSurgePrelude(lua_State *s, const char *surgeTableName); /* * Call this function to get a string representation of the prelude @@ -117,6 +117,7 @@ struct SGLD lua_State *L; int top; }; + } // namespace LuaSupport } // namespace Surge diff --git a/src/common/SurgeStorage.h b/src/common/SurgeStorage.h index 958be046295..72855935bae 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -1344,6 +1344,10 @@ class alignas(16) SurgeStorage bool oscReceiving{false}; bool oscSending{false}; + int voiceCount; // TODO: use SurgeSynthesizer class to fetch synth->polydisplay directly from + // valueAt() in FormulaModulationHelper.cpp where it's needed and remove + // this and its assignment in SurgeSynthesizer.cpp + bool getOverrideDataHome(std::string &value); void createUserDirectory(); diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index 2c7e94a0609..9dcb8dee8f9 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -4792,6 +4792,7 @@ void SurgeSynthesizer::process() storage.modRoutingMutex.unlock(); polydisplay = vcount; + storage.voiceCount = vcount; // TODO: FIX SCENE ASSUMPTION if (play_scene[0]) diff --git a/src/common/dsp/SurgeVoice.cpp b/src/common/dsp/SurgeVoice.cpp index 9450e6185fe..0769268368c 100644 --- a/src/common/dsp/SurgeVoice.cpp +++ b/src/common/dsp/SurgeVoice.cpp @@ -191,7 +191,9 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata * state.key = key; state.keyRetuningForKey = -1000; state.channel = channel; + state.voiceOrderAtCreate = voiceOrder; + state.polylimit = storage->getPatch().polylimit.val.i; state.velocity = velocity; state.fvel = velocity / 127.f; diff --git a/src/common/dsp/SurgeVoiceState.h b/src/common/dsp/SurgeVoiceState.h index f2b005fe5f6..21e33b70ee0 100644 --- a/src/common/dsp/SurgeVoiceState.h +++ b/src/common/dsp/SurgeVoiceState.h @@ -33,7 +33,7 @@ struct SurgeVoiceState MidiKeyState *keyState; MidiChannelState *mainChannelState; MidiChannelState *voiceChannelState; - int key, velocity, channel, scene_id, releasevelocity; + int key, velocity, channel, scene_id, releasevelocity, polylimit; float portasrc_key, portaphase; bool porta_doretrigger; diff --git a/src/common/dsp/modulators/FormulaModulationHelper.cpp b/src/common/dsp/modulators/FormulaModulationHelper.cpp index a1cfdae074b..1c58f083caa 100644 --- a/src/common/dsp/modulators/FormulaModulationHelper.cpp +++ b/src/common/dsp/modulators/FormulaModulationHelper.cpp @@ -34,6 +34,7 @@ namespace Formula { void setupStorage(SurgeStorage *s) { s->formulaGlobalData = std::make_unique(); } + bool prepareForEvaluation(SurgeStorage *storage, FormulaModulatorStorage *fs, EvaluatorState &s, bool is_display) { @@ -80,7 +81,12 @@ bool prepareForEvaluation(SurgeStorage *storage, FormulaModulatorStorage *fs, Ev if (firstTimeThrough) { - Surge::LuaSupport::loadSurgePrelude(s.L); + // setup shared table + lua_newtable(s.L); + lua_setglobal(s.L, sharedTableName); + + // setup prelude + Surge::LuaSupport::loadSurgePrelude(s.L, surgeTableName); auto reserved0 = std::string(R"FN( function surge_reserved_formula_error_stub(m) return 0; @@ -229,6 +235,7 @@ end addn("tempo", s.tempo); addn("songpos", s.songpos); addb("released", s.released); + addb("is_rendering_to_ui", s.is_display); addb("clamp_output", true); auto cres = lua_pcall(s.L, 1, 1, 0); @@ -363,12 +370,16 @@ end s.h = 0; s.r = 0; s.s = 0; + s.rate = 0; s.phase = 0; s.amp = 0; s.deform = 0; s.tempo = 120; + if (is_display) + s.is_display = true; + if (s.raisedError) std::cout << "ERROR: " << *(s.error) << std::endl; #endif @@ -420,6 +431,7 @@ bool initEvaluatorState(EvaluatorState &s) s.L = nullptr; return true; } + void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, FormulaModulatorStorage *fs, EvaluatorState *s, float output[max_formula_outputs], bool justSetup) @@ -450,6 +462,7 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, std::string fn; bool replace = true; } onerr(s->L, s->funcName); + /* * So: make the stack my evaluation func then my table; then push my table * values; then call my function; then update my global @@ -463,12 +476,6 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, } lua_getglobal(s->L, s->stateName); - /* - lua_pushstring(s->L, "av"); - lua_gettable(s->L, -2); - lua_pop(s->L, 1); - */ - // Stack is now func > table so we can update the table lua_pushinteger(s->L, phaseIntPart); lua_setfield(s->L, -2, "intphase"); @@ -477,6 +484,13 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, lua_pushinteger(s->L, phaseIntPart); lua_setfield(s->L, -2, "cycle"); + // fake voice count for display calls + int voiceCount = 1; + if (storage->voiceCount != 0) + voiceCount = storage->voiceCount; + lua_pushinteger(s->L, voiceCount); + lua_setfield(s->L, -2, "voice_count"); + auto addn = [s](const char *q, float f) { lua_pushnumber(s->L, f); lua_setfield(s->L, -2, q); @@ -492,33 +506,24 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, lua_setfield(s->L, -2, q); }; - addn("phase", phaseFracPart); + addn("delay", s->del); + addn("decay", s->dec); + addn("attack", s->a); + addn("hold", s->h); + addn("sustain", s->s); + addn("release", s->r); - if (true /* s->subLfoEnvelope */) - { - addn("delay", s->del); - addn("decay", s->dec); - addn("attack", s->a); - addn("hold", s->h); - addn("sustain", s->s); - addn("release", s->r); - } - if (true /* s->subLfoParams */) - { - addn("rate", s->rate); - addn("amplitude", s->amp); - addn("startphase", s->phase); - addn("deform", s->deform); - } + addn("rate", s->rate); + addn("amplitude", s->amp); + addn("startphase", s->phase); + addn("deform", s->deform); - if (true /* s->subTiming */) - { - addn("tempo", s->tempo); - addn("songpos", s->songpos); - addb("released", s->released); - } + addn("phase", phaseFracPart); + addn("tempo", s->tempo); + addn("songpos", s->songpos); + addb("released", s->released); - if (/* s->subVoice && */ s->isVoice) + if (s->isVoice) { addb("is_voice", s->isVoice); addn("key", s->key); @@ -527,6 +532,9 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, addn("channel", s->channel); addb("released", s->released); + addn("voice_id", s->voiceOrderAtCreate); + addn("poly_limit", s->polylimit); + addn("poly_at", s->polyat); addn("mpe_bend", s->mpebend); addn("mpe_bendrange", s->mpebendrange); @@ -540,6 +548,7 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, addnil("retrigger_AEG"); addnil("retrigger_FEG"); + addb("is_rendering_to_ui", s->is_display); if (s->subAnyMacro) { @@ -718,13 +727,7 @@ std::vector createDebugDataOfModState(const EvaluatorState &es) #if HAS_LUA std::vector rows; Surge::LuaSupport::SGLD guard("debugViewGuard", es.L); - lua_getglobal(es.L, es.stateName); - if (!lua_istable(es.L, -1)) - { - lua_pop(es.L, -1); - rows.emplace_back(0, "Error", "Not a Table"); - return rows; - } + std::function rec; rec = [&rows, &es, &rec](const int depth, bool internal) { Surge::LuaSupport::SGLD guardR("rec[" + std::to_string(depth) + "]", es.L); @@ -770,10 +773,6 @@ std::vector createDebugDataOfModState(const EvaluatorState &es) { rows.emplace_back(depth, lab, lua_tostring(es.L, -1)); } - else if (lua_isnil(es.L, -1)) - { - rows.emplace_back(depth, lab, "(nil)"); - } else if (lua_isboolean(es.L, -1)) { rows.emplace_back(depth, lab, (lua_toboolean(es.L, -1) ? "true" : "false")); @@ -785,12 +784,23 @@ std::vector createDebugDataOfModState(const EvaluatorState &es) rows.back().isInternal = internal; rec(depth + 1, internal); } + else if (lua_isfunction(es.L, -1)) + { + rows.emplace_back(depth, lab, "(function)"); + rows.back().isInternal = internal; + } + else if (lua_isnil(es.L, -1)) + { + rows.emplace_back(depth, lab, "(nil)"); + rows.back().isInternal = internal; + } else { rows.emplace_back(depth, lab, "(unknown)"); + rows.back().isInternal = internal; } - rows.back().isInternal = internal; }; + for (auto k : ikeys) { std::ostringstream oss; @@ -810,13 +820,26 @@ std::vector createDebugDataOfModState(const EvaluatorState &es) } }; - rec(0, false); - lua_pop(es.L, -1); + std::vector tablesList = {es.stateName, sharedTableName}; + for (const auto &t : tablesList) + { + lua_getglobal(es.L, t.c_str()); + if (!lua_istable(es.L, -1)) + { + lua_pop(es.L, -1); + rows.emplace_back(0, "Error", "Not a Table"); + continue; + } + rec(0, false); + lua_pop(es.L, -1); + } + return rows; #else return {}; #endif } + std::string createDebugViewOfModState(const EvaluatorState &es) { auto r = createDebugDataOfModState(es); @@ -885,6 +908,7 @@ void setupEvaluatorStateFrom(EvaluatorState &s, const SurgePatch &patch, int sce s.highest_key = scene.modsources[ms_highest_key]->get_output(0); s.latest_key = scene.modsources[ms_latest_key]->get_output(0); } + void setupEvaluatorStateFrom(EvaluatorState &s, const SurgeVoice *v) { s.key = v->state.key; @@ -892,6 +916,9 @@ void setupEvaluatorStateFrom(EvaluatorState &s, const SurgeVoice *v) s.velocity = v->state.velocity; s.releasevelocity = v->state.releasevelocity; + s.voiceOrderAtCreate = v->state.voiceOrderAtCreate; + s.polylimit = v->state.polylimit; + s.polyat = v->storage ->poly_aftertouch[v->state.scene_id & 1][v->state.channel & 15][v->state.key & 127]; diff --git a/src/common/dsp/modulators/FormulaModulationHelper.h b/src/common/dsp/modulators/FormulaModulationHelper.h index 31f171dc664..a47df3f5a21 100644 --- a/src/common/dsp/modulators/FormulaModulationHelper.h +++ b/src/common/dsp/modulators/FormulaModulationHelper.h @@ -44,6 +44,8 @@ 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 { @@ -64,9 +66,12 @@ struct EvaluatorState bool retrigger_AEG, retrigger_FEG; + bool is_display = false; + // voice features bool isVoice; - int key{60}, channel{0}, velocity{0}, releasevelocity{0}, mpebendrange{24}; + int key{60}, channel{0}, velocity{0}, releasevelocity{0}, polylimit{1}, mpebendrange{24}; + int64_t voiceOrderAtCreate{1L}; float polyat{0}, mpebend{0}, mpetimbre{0}, mpepressure{0}; // scene features diff --git a/src/surge-testrunner/UnitTestsLUA.cpp b/src/surge-testrunner/UnitTestsLUA.cpp index cf022186639..79d3b8e96e3 100644 --- a/src/surge-testrunner/UnitTestsLUA.cpp +++ b/src/surge-testrunner/UnitTestsLUA.cpp @@ -159,7 +159,8 @@ TEST_CASE("Surge Prelude", "[lua]") REQUIRE(L); luaL_openlibs(L); - REQUIRE(Surge::LuaSupport::loadSurgePrelude(L)); + static constexpr const char *surgeTableName{"surge"}; + REQUIRE(Surge::LuaSupport::loadSurgePrelude(L, surgeTableName)); std::string emsg; REQUIRE(Surge::LuaSupport::parseStringDefiningFunction(