Skip to content

Commit

Permalink
Add voice data to formula modulator (#7727)
Browse files Browse the repository at this point in the history
- 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
- Rename table "global" -> "shared"
- Rename voiceMax -> polylimit
- Rename voiceid -> voiceOrderAtCreate
- Add constexpr const char definitions for "surge" and "shared" table names
- Replace "return rows;" with "continue;" in createDebugDataOfModState() error condition
- Add TODO comment SurgeStorage.h
- Rename parameters
voice_polylimit -> poly_limit
is_display -> is_rendering_to_ui

* Properly rename is_display -> is_rendering_to_ui

---------

Co-authored-by: nuoun <[email protected]>
  • Loading branch information
nuoun and nuoun authored Aug 7, 2024
1 parent af631b5 commit c914d3b
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 53 deletions.
27 changes: 24 additions & 3 deletions src/common/LuaSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,12 @@ bool Surge::LuaSupport::setSurgeFunctionEnvironment(lua_State *L)
// stack is now func > table

// List of whitelisted functions and modules
std::vector<std::string> sandboxWhitelist = {"ipairs", "error", "math", "surge"};
std::vector<std::string> sandboxWhitelist = {"ipairs", "error", "math", "surge", "shared"};
/*
std::vector<std::string> sandboxWhitelist = {"pairs", "ipairs", "next", "print",
"error", "math", "string", "table",
"bit", "setmetatable", "surge", "shared"};
*/

for (const auto &f : sandboxWhitelist)
{
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand All @@ -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;
}
Expand Down
5 changes: 3 additions & 2 deletions src/common/LuaSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,6 +117,7 @@ struct SGLD
lua_State *L;
int top;
};

} // namespace LuaSupport
} // namespace Surge

Expand Down
4 changes: 4 additions & 0 deletions src/common/SurgeStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
1 change: 1 addition & 0 deletions src/common/SurgeSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4792,6 +4792,7 @@ void SurgeSynthesizer::process()

storage.modRoutingMutex.unlock();
polydisplay = vcount;
storage.voiceCount = vcount;

// TODO: FIX SCENE ASSUMPTION
if (play_scene[0])
Expand Down
2 changes: 2 additions & 0 deletions src/common/dsp/SurgeVoice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/common/dsp/SurgeVoiceState.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
117 changes: 72 additions & 45 deletions src/common/dsp/modulators/FormulaModulationHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace Formula
{

void setupStorage(SurgeStorage *s) { s->formulaGlobalData = std::make_unique<GlobalData>(); }

bool prepareForEvaluation(SurgeStorage *storage, FormulaModulatorStorage *fs, EvaluatorState &s,
bool is_display)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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");
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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)
{
Expand Down Expand Up @@ -718,13 +727,7 @@ std::vector<DebugRow> createDebugDataOfModState(const EvaluatorState &es)
#if HAS_LUA
std::vector<DebugRow> 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<void(const int, bool)> rec;
rec = [&rows, &es, &rec](const int depth, bool internal) {
Surge::LuaSupport::SGLD guardR("rec[" + std::to_string(depth) + "]", es.L);
Expand Down Expand Up @@ -770,10 +773,6 @@ std::vector<DebugRow> 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"));
Expand All @@ -785,12 +784,23 @@ std::vector<DebugRow> 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;
Expand All @@ -810,13 +820,26 @@ std::vector<DebugRow> createDebugDataOfModState(const EvaluatorState &es)
}
};

rec(0, false);
lua_pop(es.L, -1);
std::vector<std::string> 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);
Expand Down Expand Up @@ -885,13 +908,17 @@ 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.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];
Expand Down
7 changes: 6 additions & 1 deletion src/common/dsp/modulators/FormulaModulationHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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
Expand Down
Loading

0 comments on commit c914d3b

Please sign in to comment.