Home › Forums › General › Programming › send lua output to sysex string
- This topic has 12 replies, 4 voices, and was last updated 12 years ago by atom.
-
AuthorPosts
-
March 29, 2012 at 3:28 pm #586
Hi,
I’m new to ctrlr and progamming in lua.
I am trying to do my first synth controller. For that I need to combine some buttons (which represents their state as binary) convert the resutls to hex and send that result via sysex to the synth.
Combining and generating the correct hex codes works but with sending the lua output to the sysex string won’t work. I’ve tried it to set k0 with the hex result from lua and set inside the sysex formula k0.
but if i control the result inside the midi-monitor instead of the correct value there is just a zero.How can I send lua variables to a sysex formula??
Please help – if that works my first synth controller will be finished an full functional.
Thx!
March 29, 2012 at 6:48 pm #4161If you have a modulator with a sysex string and sysex settings, it will be sent automatically when the modulator changes…
If you are using your own LUA methods, you cannot use the modulator sysex property (midi message)… you need to build your own midi message using the classe by generating a sysex hex string and then send it as a message through the panel…
I may be wrong, but that’s what I understood… I’m currently doing it this way and will post example tonight ” title=”Wink” />
March 29, 2012 at 8:04 pm #4162In the existing panels I found two different solutions so far:
1) create a SysEx string and send it directly via sendMidiMessageNow.
Example:
[code:y5nzl4fz]
— request ENS dump
midiDevice = panel:getGlobalVariable(1) — this is k1
mEns = CtrlrMidiMessage( {0xf0, 0x00, 0x00, 0x7e, 0x49, midiDevice, 0x01, 0x78, 0x00, 0x00, 0xf7} )
panel:sendMidiMessageNow(mEns)
[/code:y5nzl4fz]2) send the binary value to a hidden (invisible) component with a modulator which sends the SysEx string (I call it "satellite" since it acts like a transceiver which hovers above the buttons ” title=”Wink” />
[code:y5nzl4fz]
sat = panel:getModulatorByName("OP1FlagsSatellite")
if sat ~= nil then
notify_vst_host = false
notify_midi_device = true
notify_component = true
sat:setModulatorValue(value, notify_vst_host, notify_midi_device, notify_component)
end
[/code:y5nzl4fz]I’m unsure which method is better.
The second one is a bit cumbersome, but it might have advantages if the resulting value should be handled on an alternative way (e.g. retrieve the last sent value for a patch dump).Best Regards, Thorsten.
March 29, 2012 at 8:34 pm #4163TK has provided the 2 solutions I had also thought about, but I personnaly didn’t like the 2nd one and sticked with the 1st one…
In my case, I have only one or two lua methods for sending modulator value to my synth. I’m using the modulator’s custom index and custom group index as the address index of the message for example:
let’s say the patch is as such
(Address converted from HEX to DEC)
PATCH ADDRESS
00 – 31 : General
32 – 63 : Effects
64 – 85 : Tone A
86 – 95 : Tone B
etc…And Tone A parameters are
00 LFO 1 Rate
01 LFO 1 Delay
02 LFO 2 Rate
03 LFO 2 Delay
etc..then Modulator Tone A – LFO 1 Rate custom index would be 0 and custum group index would be 64 : 0 + 64 = 64
then Modulator Tone A – LFO 1 Delay custom index would be 1 and custum group index would be 64 : 1 + 64 = 65Once Tone A is completed, you merely have to copy / paste, change the names as Tone B and change all modulators group index to 86
then Modulator Tone B – LFO 1 Rate custom index would be 0 and custum group index would be 86 : 0 + 86 = 86
then Modulator Tone B – LFO 1 Delay custom index would be 1 and custum group index would be 86 : 0 + 86 = 87The same method can now be used to send sysex
F0 … … … ad va ch F7 where ad is (custum index + custum group index), vc is the modulator value, and ch is the checksum !
It works flawlessly for me right now ” title=”Wink” /> and I don’t rely on a bunch of modulator sysex msg to edit ” title=”Wink” /> + I can use those values to automatically generates part of codes, or vstindex, or anything useful ” title=”Wink” />
March 29, 2012 at 9:53 pm #4164Thanks for the inspiration!
Using a custom and group index which is embedded into the modulator (or component) would also help me on my next panel where much more parameters (with up to 3-dimensional structures) have to be maintained.How do you get these indices from Lua?
Could you please post a small code snippet?Best Regards, Thorsten.
March 29, 2012 at 10:41 pm #4165Ok, here’s the part of the code snippet, it’s actually a method defined below the main method just in case I need to use it from somewhere else ” title=”Wink” />
The example is for the Roland JD-800, the sysex for changing a parameter in the temporary patch memory of the jd is :
f0 41 10 3d 12 msb add lsb val chk f7
Where msb/add/lsb is the full address of the parameter to be changed, val is the value and chk is checksum. In my case, the msb is always 00 . The JD-800 patch is exactly 384 bytes, which basically is 3 sets of 128 bytes, so the address will range from h00 h00 h00 to h00 h02 h38. (the first part is common/effects for 78 bytes, then 4 series of 72 bytes for each tone)
So my sysex string is "f0 41 10 3d 12 00 add lsb val chk f7" with the msb hardcoded as the first 00… the rest follows:
The first thing I do is calculate the UID (unique id) which is from 0 to 383 (from the custum index/group) and is exactly the base address of the parameter in the midi implementation of the Roland JD-800. I calculate the expected add and lsb from the UID.
If the UID is <= 383 then
— > it’s one of the patch parameter !!!
Note: I use a lot of sliders with negative value that ends up being all in positive in the JD… For example, the pitch fine ranges from -50 to 50, but the JD value can range from h00 to h64. So if the component is a uiSlider, I perform the following calculation where I substract the uiSlider minimum value from the value… So if my modulator value is -25 and the minimum value is -50… I will then send (-25 – -50) = 25 = h19
Afterward, I calculated the Roland checksum which is basically (128 – (the remainder of (the sum of all significative parts of the sysex) % 128))… then the sysex is built and send to the device…
However, if the UID is > 383, then it’s my 5th groups of modulators, mainly the "ACTIVE TONE" modulators, modulators that change whatever tone is current active (can be from 1 to 4 of them, It’s dynamic !!) … So if 2 tones are active, the msg needs to be send for those 2 tones at their proper address… I basically "set" the according modulators and each will send the sysex for me ” title=”Wink” />
[code:1s65pwgv]
function sendSysexModulatorValue(modulator, newValue)
local uid, val, add, lsb, chk, syxuid = tonumber(modulator:getPropertyString("modulatorCustomIndexGroup")) + tonumber(modulator:getPropertyString("modulatorCustomIndex"))
if (uid <= 383) then
add = math.floor(uid / 128)
lsb = (uid % 128)
val = newValueif (modulator:getComponent():getPropertyString("uiType") == "uiSlider") then
val = val – modulator:getComponent():getPropertyInt("uiSliderMin")
endchk = (128 – ((add + lsb + val) % 128))
syx = string.format("f0 41 10 3d 12 00 %.2x %.2x %.2x %.2x f7", add, lsb, val, chk)
panel:sendMidiMessageNow(CtrlrMidiMessage(syx))
else
for i = 1, 4, 1 do
if (gToneButtons[i + 4]:getModulatorValue() == 1) then
uid = ((i – 1) * 72) + 96 + tonumber(modulator:getPropertyString("modulatorCustomIndex")) + 1
gPatchModulators[uid]:setModulatorValue(newValue, false, false, true)
end
end
end
end
[/code:1s65pwgv]ALSO very important, at the start of the panel, I build a global collection of all "UIDs" modulators called gPatchModulators[uid], this can be useful to directly access a modulator based on a uid !!!
It is important to set every modulators, the proper indexes and those that are not part of the patch modulators, a value of -1 !! you will understand later why ” title=”Wink” />
[code:1s65pwgv]
function buildPatchModulators().
local uci, ucg, uidgPatchModulators = {}
for i,m in ipairs(panel:getModulatorsWildcard("*",false):getObject()) do
uci = tonumber(m:getPropertyString("modulatorCustomIndex"))
ucg = tonumber(m:getPropertyString("modulatorCustomIndexGroup"))
if (uci ~= -1) and (uci ~= nil) then
uid = uci + ucg + 1
gPatchModulators[uid] = m
end
end
end
[/code:1s65pwgv]Finally, upon receiving midi msg (let’s say for a complete patch (384 bytes in my case)) you can do something like this, it’s less error prone than listing 384 modulators ” title=”Wink” /> !!!
[code:1s65pwgv]
function assignSysexPatchValues(programData)
for uid = 1, 384, 1 do
assignPatchModulatorValue(gPatchModulators[uid], tonumber(programData:getByte(uid – 1)))
end
endfunction assignPatchModulatorValue(modulator, value)
local val = valueif (modulator ~= nil) then
if (modulator:getComponent():getPropertyString("uiType") == "uiSlider") then
val = val + modulator:getComponent():getPropertyInt("uiSliderMin")
endmodulator:setModulatorValue(val, false, false, true)
end
end
[/code:1s65pwgv]I was able to build my pannel under 2 weeks by using those strategies ” title=”Wink” /> and that’s why I’m using LUA all the way in my coding ” title=”Wink” />
Enjoy ” title=”Wink” /> !
March 29, 2012 at 10:46 pm #4166Beware of tables in lua, from my understanding they start at 1 where in our patch and address we base everything at 0 ” title=”Wink” /> This can
March 29, 2012 at 11:00 pm #4167Thanks again!
Btw.: concerning the handing of negative values, I read somewhere in the forum about following solution which I’m also using in my panel.
In following example we want to range between -64..63, and send 0..127:
Component: set minimum value to -64, and maximum value to 63 -> this is the displayed value.
Modulator, first entries (I think that I don’t need to quote the descriptions): modulatorValue + 64, midiValue – 64Advantage: the modulator receives/stores/sends exactly the same value which is used by the synth and transfered via SysEx, no special handling required in the Lua script.
Best Regards, Thorsten.
March 29, 2012 at 11:05 pm #4168I think that will work ONLY with sysex msg and not with LUA… and since I only use that ” title=”Wink” />… The main issue with the LUA approach, is that it doesn’t involved Snapshot, so you need to resend the patch when you load the panel… I forgot to mention that ” title=”Wink” /> The advantages is that you can resend the whole patch in one shot instead of modulator by modulator which resulted in errors in my synth… Of course, resending the patch is another lua method… Which looks like this :
3 series of 128 bytes to be sent ” title=”Wink” />
[code:1ulgpwlb]
function writeTemporaryPatch()local val, chk, syx
— Function
syx = { " 00 00 ", " 01 00 ", " 02 00 " }
for i = 1, 3, 1 do
chk = i – 1
for m = (((i – 1) * 128) + 1), (i * 128), 1 do
val = 0
if (gPatchModulators[m] ~= nil) then
val = gPatchModulators[m]:getModulatorValue()
if (gPatchModulators[m]:getComponent():getPropertyString("uiType") == "uiSlider") then
val = val – gPatchModulators[m]:getComponent():getPropertyInt("uiSliderMin")
end
end
syx[i] = syx[i] .. string.format("%.2x ",val)
chk = chk + valend
chk = (128 – (chk % 128))
syx[i] = "f0 41 10 3d 12 00" .. syx[i] .. string.format("%.2x ",chk) .. "f7"
panel:sendMidiMessageNow(CtrlrMidiMessage(syx[i]))
end
end
[/code:1ulgpwlb]March 29, 2012 at 11:27 pm #4169Finally, the other reason you might want to use the index / group index even if you’re using the modulator midi sysex pattern is that you can loop over all your modulator and assign a concatenated string to that value based on the indexes ” title=”Wink” />… reassigning all your midi msg as you would like with less chance of error ” title=”Wink” />…
March 30, 2012 at 7:57 am #4170Hey, big thx for your inputs. I’ve tried them and they worked for me ” title=”Smile” />
March 30, 2012 at 11:35 am #4171Great, ! I’m happy to help out ” title=”Wink” />
March 30, 2012 at 10:35 pm #4172Here is how i would go about dumping a program (this is something i wrote for the Evolver). The request looks like this:
[code:m335pqei]
getEvolverProgram = function(modulator, newValue)console("getEvolverProgram")
program = panel:getModulatorByName("programNumber"):getValue()-1
bank = panel:getModulatorByName("bankNumber"):getValue()-1programRequest = CtrlrMidiMessage ({0xf0, 0x01, 0x20, 0x01, 0x05, bank, program, 0xf7})
console ("sending request as data: "..programRequest:toString())
panel:sendMidiMessageNow(programRequest)
end
[/code:m335pqei]And when data arrives to the panel
[code:m335pqei]
receivedDataFromDevice = function(midiMessage)
console ("Received data of size: " .. midiMessage:getSize())if midiMessage:getSize() == 228 then
if midiMessage:getLuaData():getByte(4) == 2 then
console ("Got program dump data")dataToUnpack = midiMessage:getLuaData():getRange(7, 220)
console ("Need to unpack " .. dataToUnpack:getSize() .. " bytes of data")
unpackedData = utils.unpackDsiData (dataToUnpack)
console ("Unpacked data is " .. unpackedData:getSize() .. " bytes in size")
console (unpackedData:toHexString(1))for index=0,127 do
param = panel:getModulatorWithProperty ("modulatorCustomIndex", string.format("%d",index))
if param ~= nil then
console ("set value to: " .. param:getLuaName())param:setModulatorValue (unpackedData:getByte(index), false, false, false)
end
end
endend
end
[/code:m335pqei] -
AuthorPosts
- The forum ‘Programming’ is closed to new topics and replies.