Iterating Modulators w/ panel:getModulatorByIndex(i): Any Benefits of Dedicated Vars vs Lookup Hashtable? #620
-
I have incoming dumps deciphered and mapped to tables so looking at iteration options for efficient bi-directional assignment to/from the dumps. Parsed Ctrlr.org and there are several variations available that are technical solutions, but understand that panel:getNumModulators() | panel:getModulatorByIndex(i)
custom_var1 = panel:getModulatorByIndex(i)
custom_var2 = panel:getModulatorByIndex(i+1)
..
custom_var399 = panel:getModulatorByIndex(399) any thoughts/comments appreciated Ctrlr.org examples''' What you have to keep in mind, that when you do getModulatorByName() Ctrlr iterates through the entire list of modulators and tries to find the one with the name provided, if the modulator is at the end of the list then it might take a while. mapping mods by index
n = panel:getNumModulators()
for i=0,n-1 do
mod = panel:getModulatorByIndex(i)
end You can use it to quickly send patches for users, to change properties on the panel to all modulators/components (i’ll be doing a patch manager with a web repository so that i can distribute simple patches quickly, like the bug with the mouse cursor beeing None should be fixed by a small snip of code like that) dnaldoog extended version with L-wrapped getName
myGenerateModNames = function()
n = panel:getNumModulators()
for i=0,n-1 do
mod = panel:getModulatorByIndex(i)
local Name= L(mod:getName())
print (i,Name)
end
end -- function
print=function(…)
local output=””
for i,v in ipairs(arg) do
v=string.format(“%s”,v)
output = output..” “..v
end
console(String(output))
end --function
myGenerateModNames() Generic for-loop
function updateModulators(midi)
local t={dial1=8,dial2=10,dial3=12}
for k,v in pairs (t) do
local b1=m:getData():getByte(v)
local b2=m:getData():getByte(v+1)
local value=combine4n(b2,b1)
panel:getModulatorByName(k):getComponent():setValue(value,true)
end
end
for d = 0, "your max"
e = panel:getModulatorWithProperty(“vstIndex”,d)
f = e:getProperty("modulatorCustomIndex")
for i=0,56 do
if f == nrpnbase+i then
j = 128 * programData:getByte((i*2)+10) +programData:getByte((i*2)+11)
--console(toString(i))
--console(toString(j ))
e:setModulatorValue(j, false, true, false)
end
end
end Custom Property
panel:setProperty ("myVeryCustomProperty", "my even more custom piece of data")
|
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 1 reply
-
If you create a lua table with keys with values pointing to an object like I use metatables to achieve this. This way you don't end up with a slew of variable names in the global space There is probably a performance hit when a variable pointing to a panel object needs to be assigned a lua table key using the metatable, but I doubt it's a big deal. I can't think of any benefit to using serialised code when updating modulators from a sysex dump and collating data for sending in order. I think the hash/key way makes more sense. tab = { -- list of all modulators in a sysex dump lfoDelay = {pos = 3}, lfoDelay2 = {pos = 4}, ["my cutoff"] = {pos = 7}, ["waveform MSB"] = {pos =8}, ["waveform LSB"] = {pos = 9} } --incoming sysex as midi:getData():toHexString(1) --(`F0 00 00 03 7F 00 00 63 01 00 F7`) --assign incoming dump: for k, arr in pairs(tab) do panel:getModulatorByName(k):setModulatorValue(midi:getData():getByte(arr.pos), false, true, false) -- maybe false,false,false ?? end local msb = panel:getModulatorByName(tab["waveform MSB"]):getModulatorValue() -- this could be a hidden control local lsb = panel:getModulatorByName(tab["waveform LSB"]):getModulatorValue() -- this is visible local waveform = (msb * 128) + lsb -- often there are more than 128 waveforms panel:getModulatorByName(tab["waveform LSB"]):getModulatorValue(waveform, false, true, false) -- Sending Midi local mess = MemoryBlock("F0 00 00 00 00 00 00 00 F7") for k, v in pairs(tab) do mess:setByte(v.pos, panel:getModulatorByName(k):getModulatorValue()) end mess:setByte(tab["waveform LSB"].pos, panel:getModulatorByName(tab["waveform LSB"]):getModulatorValue() - 128) -- quick way to create 14 bit value
|
Beta Was this translation helpful? Give feedback.
-
On my side I just use panel:getModulatorByName() at panel initialization to assign a variable to each modulator I'm using. |
Beta Was this translation helpful? Give feedback.
-
I think goodweather’s system is indeed more straightforward. The system I use has one advantage that I can see. Not having to invent legal lua variable names for controls listed in a manual. For example, in the Yamaha TX81Z there is a parameter Of course, if the modulator is named something legal like It’s not a big deal I guess- just another way of avoiding that potential bottleneck of looping through the panel by name to find an object in a loop. For me it’s a question of workflow rather than any big gains in runtime efficiency. |
Beta Was this translation helpful? Give feedback.
-
RE: init node
Assuming that Ctrlr has special hook to run the
console("initializing")
console("initializing: DataUtils")
DU = DataUtils:new()
console("initializing: DataUtils Done")
console("initializing: GlobalFuncs")
FUNC = GlobalFuncs:new()
console("initializing: GlobalFuncs Done")
console("initializing: DeviceManager")
DM = DeviceManager:new()
console("initializing: DeviceManager Done")
console("initialized") |
Beta Was this translation helpful? Give feedback.
-
@dnaldoog @dobo365 thanks for info...! basic proof-of-concept working
THANKS again for the insights -- Dump Contract for Sysex Parsing self.Contract = {
---@type table<integer, table<integer,integer>>
PresetDump = {
[899] = {1 ,1 },
[900] = {2 ,2 },
[901] = {3 ,3 },
[902] = {4 ,4 },
...
} --- caller test code:
local mods = Mods:new()
what(mods.VoiceMods.kb_layer1_ctune_14251)
Mods:VoiceModsUpdate()
---@class Mods
Mods = {}
---Mods
---@param o? any
---@return Mods
function Mods:new(o)
o = o or {}
setmetatable({},self)
self.__index = self
self.VoiceMods = Mods:VoiceModsGet()
return self
end
function Mods:VoiceModsGet()
--[[ PREST LAYER PARAMS]]
---@enum VoiceMods
local VoiceMods = {
---@type CtrlrSlider
kb_layer1_ctune_14251 = panel:getModulator("kb_layer1_ctune_14251"),
---@type CtrlrSlider
kb_layer1_ftune_14261 = panel:getModulator("kb_layer1_ftune_14261"),
}
return VoiceMods
end
function Mods:VoiceModsUpdate()
p("VoiceModsUpdate: Updates: STARTED")
for k,v in pairs(self.VoiceMods) do
if(DU:isNotNull(k) and DU:isNotNull(v)) then
p(string.format("Updating mod:[%s] from dumptable",tostring(k)))
Mods:GetValue(v)
end
end
p("VoiceModsUpdate: Updates: COMPLETED")
end
---Get ParamId Suffix from name. searches for LAST OCCURENCE of delimeter and retreives any characters after
--- for mods fetch name with: [L(mod:getName())]
--- for comps fetch anme with [L(comp:getOwner():getName())]
---@param name string: component
---@param delimeter string?: delimeter to look for
---@return string: paramId use to lookup values in the ParamId2ValueMapTable
function Mods:GetParamIdFromName(name,delimeter)
delimeter = delimeter or "_"
local suffix = ""
if(DU:isNullOrEmpty(name)) then
p("GetParamIdFromName:ERROR: provided name is nil or Empty")
else
suffix = DU.getSuffixByText(name,delimeter)
if(DU:isNull(suffix)) then
p(string.format("GetParamIdFromName:ERROR: Suffix not found for name[%s]",tostring(name)))
return ""
else
p(string.format("GetParamIdFromName:name:[%s] id:[%s]",tostring(name),tostring(suffix)))
end
end
return suffix
end
---Update Modulator Value from device ParamId2ValueMapTable
---@param id any
function Mods:GetParamValueFromDump(id)
DM.DUMPS.PresetDump:GetDumpValue_DeNibbledIntById(id)
end
---Get modulator value
---@param mod CtrlrModulator
function Mods:GetValue(mod)
if(DU:isNull(mod)) then p("GetValue:ERROR: Provided mod is nil. Aborting value update") return end
local name = DM.UI.COMP:GetModName(mod) -- Mods.VoiceMods.cbVoice1_Filter:getName()
if(DU:isNull(name)) then p("GetValue:ERROR: Failed to fetch mod name. Aborting value update") return end
local paramId = Mods:GetParamIdFromName(name,"_")
if(DU:isNull(paramId)) then p(string.format("GetValue:ERROR: Failed to extract paramId from mod name[%s]. Aborting value update",tostring(name))) return end
local value = DM.DUMPS.PresetDump:GetDumpValue_DeNibbledIntById(tonumber(paramId))
if(DU:isNull(value)) then p(string.format("GetValue:ERROR: Failed to fetch dump value for name:[%s] paramId[%s]. Aborting value update",tostring(name),tostring(paramId))) return end
-- mod:setValue()
mod:setValue(value,false)
if(tostring(mod:getValue()) == tostring(value)) then
p(string.format("GetValue:SUCCESS: Module Update mod:[%s] paramId:[%s] value[%s]",tostring(name),tostring(paramId),tostring(value)))
return true
end
end |
Beta Was this translation helpful? Give feedback.
If you create a lua table with keys with values pointing to an object like
panel:getModulator()
orpanel:getComponent()
then I am pretty sure those are references and won't change during runtime.I use metatables to achieve this. This way you don't end up with a slew of variable names in the global space
_G
. I wrote this before goodweather pointed out that you can run lua functions from the console (I never knew that until recently) thus updating an init script that might contain lua variables for modulators ― but even so it's a convenient way to write code and you can use any variable name using lua's[""]
table key syntax.There is probably a performance hit when a variable pointing to …