From e38cb4cabdebb1067dc693f565dbf1673643b9c1 Mon Sep 17 00:00:00 2001 From: nuoun <10226424+nuoun@users.noreply.github.com> Date: Fri, 26 Jul 2024 05:02:44 +0200 Subject: [PATCH] squash Clang Format add "global" table to Lua state plus redo voice data for formula editor Default sandbox functions Add back "global" table clear in setSurgeFunctionEnvironment() Add stack test squash Fix segfault in testrunner clang format Move Nimbus from libsamplerate to our Lanczos downsampler (#7726) 1. Makes it ready to move to sst-effects 2. Makes it so we can almost remove libsamplerate (but some twist smidgeof work to do that) 3. Uses less CPU Addresses #7359 Remove libsamplerate dependency (#7728) After porting Nimbus ot lanczos in 4b3ffe6 we can now remove the libsamplerate dependency altogether as long as we fix up the twist fm (which we did with a simple application of another lanczos ds) and remove src code from the unit tests (where it was explicitly tested) Move Nimbus to sst-effects (#7729) * Move Nimbus to sst-effects Addresses #7359 With an almost-complete port (param name dynamism is in surge but not in the submodule yet) * f Update LFO and Step Seq presets to have the LFO EG enabled (#7732) Code Checks github action, as part one of move to actions Move to actions here will be a bit trickier, but we will get there. Step one is have any action at all, so add a code check action. Pull Request moves to Github Actions (#7733) The PullRequest support of our azure pipeline is now on GitHub actions, building in various configurations and running the tests. The azure pipeline still exists to trigger the release stage, which is what I'll port tomorrow, or maybe thursday, and then be done with the azure->actions project. - Adds voice_id, voice_max, voice_count, is_display parameters to formula modulator - Fakes voice_count of "1" and sets defaults for other params on display calls - Adds "shared" table to formula modulator - Sets entries of "shared" table to nil on new Lua function env - Makes formula debugger also use "shared" table - Adds lua_isfunction to formula debugger - Some cleanup --- src/common/LuaSupport.cpp | 23 +++- src/common/LuaSupport.h | 1 + src/common/SurgeStorage.h | 2 + src/common/SurgeSynthesizer.cpp | 24 ++-- src/common/SurgeSynthesizer.h | 1 - src/common/dsp/SurgeVoice.cpp | 9 +- src/common/dsp/SurgeVoice.h | 2 +- src/common/dsp/SurgeVoiceState.h | 2 +- .../modulators/FormulaModulationHelper.cpp | 121 ++++++++++-------- .../dsp/modulators/FormulaModulationHelper.h | 6 +- 10 files changed, 117 insertions(+), 74 deletions(-) diff --git a/src/common/LuaSupport.cpp b/src/common/LuaSupport.cpp index 29506e8ba92..80e55231bc6 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", "global"}; + /* + 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); diff --git a/src/common/LuaSupport.h b/src/common/LuaSupport.h index d36f365a726..ac4f045fbfd 100644 --- a/src/common/LuaSupport.h +++ b/src/common/LuaSupport.h @@ -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..22bd5fc3a08 100644 --- a/src/common/SurgeStorage.h +++ b/src/common/SurgeStorage.h @@ -1344,6 +1344,8 @@ class alignas(16) SurgeStorage bool oscReceiving{false}; bool oscSending{false}; + int voiceCount; + bool getOverrideDataHome(std::string &value); void createUserDirectory(); diff --git a/src/common/SurgeSynthesizer.cpp b/src/common/SurgeSynthesizer.cpp index dcc7d9d73ae..9dcb8dee8f9 100644 --- a/src/common/SurgeSynthesizer.cpp +++ b/src/common/SurgeSynthesizer.cpp @@ -833,12 +833,12 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit int mpeMainChannel = getMpeMainChannel(channel, key); voices[scene].push_back(nvoice); - new (nvoice) SurgeVoice( - &storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene], - key, velocity, channel, scene, detune, &channelState[channel].keyState[key], - &channelState[mpeMainChannel], &channelState[channel], mpeEnabled, - voiceCounter++, voiceN, storage.getPatch().polylimit.val.i, host_noteid, - host_originating_key, host_originating_channel, 0.f, 0.f); + new (nvoice) SurgeVoice(&storage, &storage.getPatch().scene[scene], + storage.getPatch().scenedata[scene], key, velocity, channel, + scene, detune, &channelState[channel].keyState[key], + &channelState[mpeMainChannel], &channelState[channel], + mpeEnabled, voiceCounter++, host_noteid, + host_originating_key, host_originating_channel, 0.f, 0.f); } } break; @@ -981,9 +981,8 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit &storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene], key, velocity, channel, scene, detune, &channelState[channel].keyState[key], &channelState[mpeMainChannel], - &channelState[channel], mpeEnabled, voiceCounter++, voiceN, - storage.getPatch().polylimit.val.i, host_noteid, host_originating_key, - host_originating_channel, aegReuse, fegReuse); + &channelState[channel], mpeEnabled, voiceCounter++, host_noteid, + host_originating_key, host_originating_channel, aegReuse, fegReuse); if (wasGated && pkeyToReuse > 0) { @@ -1126,9 +1125,8 @@ void SurgeSynthesizer::playVoice(int scene, char channel, char key, char velocit &storage, &storage.getPatch().scene[scene], storage.getPatch().scenedata[scene], key, velocity, channel, scene, detune, &channelState[channel].keyState[key], &channelState[mpeMainChannel], - &channelState[channel], mpeEnabled, voiceCounter++, voiceN, - storage.getPatch().polylimit.val.i, host_noteid, host_originating_key, - host_originating_channel, aegStart, fegStart); + &channelState[channel], mpeEnabled, voiceCounter++, host_noteid, + host_originating_key, host_originating_channel, aegStart, fegStart); } } else @@ -4711,7 +4709,6 @@ void SurgeSynthesizer::process() } storage.modRoutingMutex.unlock(); - voiceN = vcount; using sst::filters::FilterType, sst::filters::FilterSubType; fbq_global g; @@ -4795,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/SurgeSynthesizer.h b/src/common/SurgeSynthesizer.h index 95567307f2d..512ea99d46a 100644 --- a/src/common/SurgeSynthesizer.h +++ b/src/common/SurgeSynthesizer.h @@ -176,7 +176,6 @@ class alignas(16) SurgeSynthesizer unsigned int voices_usedby[2][MAX_VOICES]; // 0 indicates no user, 1 is scene A, 2 is scene B int64_t voiceCounter = 1L; - int voiceN = 0; std::atomic processRunning{0}; diff --git a/src/common/dsp/SurgeVoice.cpp b/src/common/dsp/SurgeVoice.cpp index bdcafde446a..64f6db0f970 100644 --- a/src/common/dsp/SurgeVoice.cpp +++ b/src/common/dsp/SurgeVoice.cpp @@ -153,8 +153,8 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata * int velocity, int channel, int scene_id, float detune, MidiKeyState *keyState, MidiChannelState *mainChannelState, MidiChannelState *voiceChannelState, bool mpeEnabled, int64_t voiceOrder, - int voiceN, int polyLimit, int32_t host_nid, int16_t host_key, - int16_t host_chan, float aegStart, float fegStart) + int32_t host_nid, int16_t host_key, int16_t host_chan, float aegStart, + float fegStart) //: fb(storage,oscene) { #ifdef VOICE_LIFETIME_DEBUG @@ -191,10 +191,9 @@ SurgeVoice::SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *oscene, pdata * state.key = key; state.keyRetuningForKey = -1000; state.channel = channel; + state.voiceOrderAtCreate = voiceOrder; - state.polyLimit = polyLimit; - // state.polyLimit = storage->getPatch().polylimit.val.i; - state.voiceN = voiceN; + state.voiceMax = storage->getPatch().polylimit.val.i; state.velocity = velocity; state.fvel = velocity / 127.f; diff --git a/src/common/dsp/SurgeVoice.h b/src/common/dsp/SurgeVoice.h index 775c0713208..f5122b7a553 100644 --- a/src/common/dsp/SurgeVoice.h +++ b/src/common/dsp/SurgeVoice.h @@ -47,7 +47,7 @@ class alignas(16) SurgeVoice SurgeVoice(SurgeStorage *storage, SurgeSceneStorage *scene, pdata *params, int key, int velocity, int channel, int scene_id, float detune, MidiKeyState *keyState, MidiChannelState *mainChannelState, MidiChannelState *voiceChannelState, - bool mpeEnabled, int64_t voiceOrder, int voiceN, int polyLimit, int32_t host_note_id, + bool mpeEnabled, int64_t voiceOrder, int32_t host_note_id, int16_t originating_host_key, int16_t originating_host_channel, float aegStart, float fegStart); ~SurgeVoice(); diff --git a/src/common/dsp/SurgeVoiceState.h b/src/common/dsp/SurgeVoiceState.h index ccd30ce3c90..85fa432f628 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, polyLimit, voiceN; + int key, velocity, channel, scene_id, releasevelocity, voiceMax; float portasrc_key, portaphase; bool porta_doretrigger; diff --git a/src/common/dsp/modulators/FormulaModulationHelper.cpp b/src/common/dsp/modulators/FormulaModulationHelper.cpp index 2e66833522f..0a4dc184bd6 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,6 +81,11 @@ bool prepareForEvaluation(SurgeStorage *storage, FormulaModulatorStorage *fs, Ev if (firstTimeThrough) { + // setup global table + lua_newtable(s.L); + lua_setglobal(s.L, "shared"); + + // setup prelude Surge::LuaSupport::loadSurgePrelude(s.L); auto reserved0 = std::string(R"FN( function surge_reserved_formula_error_stub(m) @@ -229,6 +235,7 @@ end addn("tempo", s.tempo); addn("songpos", s.songpos); addb("released", s.released); + addb("is_display", 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 vcount = 1; + if (storage->voiceCount != 0) + vcount = storage->voiceCount; + lua_pushinteger(s->L, vcount); + 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); @@ -526,9 +531,9 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, addn("rel_velocity", s->releasevelocity); addn("channel", s->channel); addb("released", s->released); - addn("voice_order", s->voice_order); - addn("voice_limit", s->voice_limit); - addn("voice_n", s->voice_n); + + addn("voice_id", s->voiceid); + addn("voice_max", s->voicemax); addn("poly_at", s->polyat); addn("mpe_bend", s->mpebend); @@ -543,6 +548,7 @@ void valueAt(int phaseIntPart, float phaseFracPart, SurgeStorage *storage, addnil("retrigger_AEG"); addnil("retrigger_FEG"); + addb("is_display", s->is_display); if (s->subAnyMacro) { @@ -721,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); @@ -773,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")); @@ -788,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; @@ -813,13 +820,26 @@ std::vector createDebugDataOfModState(const EvaluatorState &es) } }; - rec(0, false); - lua_pop(es.L, -1); + std::vector tablesList = {es.stateName, "shared"}; + 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"); + return rows; + } + rec(0, false); + lua_pop(es.L, -1); + } + return rows; #else return {}; #endif } + std::string createDebugViewOfModState(const EvaluatorState &es) { auto r = createDebugDataOfModState(es); @@ -888,15 +908,16 @@ 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; s.channel = v->state.channel; s.velocity = v->state.velocity; s.releasevelocity = v->state.releasevelocity; - s.voice_order = v->state.voiceOrderAtCreate; - s.voice_limit = v->state.polyLimit; - s.voice_n = v->state.voiceN + 1; + + s.voiceid = v->state.voiceOrderAtCreate; + s.voicemax = v->state.voiceMax; s.polyat = v->storage diff --git a/src/common/dsp/modulators/FormulaModulationHelper.h b/src/common/dsp/modulators/FormulaModulationHelper.h index dddcac0a5bc..60dbbe176cb 100644 --- a/src/common/dsp/modulators/FormulaModulationHelper.h +++ b/src/common/dsp/modulators/FormulaModulationHelper.h @@ -64,10 +64,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}, voice_order{0}, voice_limit{0}, - voice_n{0}, mpebendrange{24}; + int key{60}, channel{0}, velocity{0}, releasevelocity{0}, voicemax{1}, mpebendrange{24}; + int64_t voiceid{1L}; float polyat{0}, mpebend{0}, mpetimbre{0}, mpepressure{0}; // scene features