Replies: 22 comments 23 replies
-
Hi Peter, Have you got a link to the manual? Specifically with the checksum algorithm? I searched but couldn't see any information. Which 12 modulators are sending the MIDI twice? |
Beta Was this translation helpful? Give feedback.
-
Maybe the checksum is based on the Roland/Yamaha type Checksum? Roland or Yamaha Checksum Algorithm
Add Checksum to Sysex String:
MyMethod=function(comp,event) local str = "F0 00 20 32 00 01 4C 10 ... 00 F7" local m = MemoryBlock(str) local data = m:getRange(9, 64) local CHKSUM = checksum(data) m:setByte(74, CHKSUM) assert(m:getSize()==75) panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1))) end -------------------------------------- checksum = function(m) local sum = 0 for i = 0, m:getSize() - 1 do sum = sum + m:getByte(i) end return bit.band(bit.bnot(sum) + 1, 127) end |
Beta Was this translation helpful? Give feedback.
-
Well if it's the same as the Yamaha checksum, then that should be the formula! Looking forward to hearing if that works. |
Beta Was this translation helpful? Give feedback.
-
Hi Peter, That string is just me not typing out the full 75 byte message. Usually you would get every value from your panel and write it into the message, calculate the checksum and then send the message back to the machine or save to file. you can just keep that checksum function outside of the calling function which is (probably a uiButton?). Alternatively on the left panel of the lua editor right click and create a new function called checksum and cut and paste that code there . Any calling function will see it. If still confused, send me your latest working version of the panel and I will make the necessary change and send the panel back to you. |
Beta Was this translation helpful? Give feedback.
-
Here is an example panel to show how it works calling separate functions: 🏃♂️ |
Beta Was this translation helpful? Give feedback.
-
That's good news! Wow, so Behringer uses that checksum? I noticed a few discussions about it on forums. No one seemed to know or be able to guess what it was as Behringer weren't supplying that information; Basically the checksum logic is -
If you do this then the checksum runs in reverse to the & masked number so a number greater than 127 would be masked out. i.e if the sum is 127 the checksum will be one; if the sum is 1 then the checksum will be 127 - kind of the inverse of every number between 1 and 127 and 0 = 0 checksum. So a checksum for 128 will be 0 and for 129 = 127 and around and around it goes. It's interesting and brilliant really. That init function is supposed to go here: You need to put
if panel:getBootstrapState() then
return
end
if panel:getRestoreState() == true or panel:getProgramState() == true then
return
end
before any callback function attached to a button or slider to stop it running on boot I think: might cause problems with standalones and vsts - I'm not 100% sure. I never use it. Also check out this panel for basic ideas. |
Beta Was this translation helpful? Give feedback.
-
what do you mean by this❓ |
Beta Was this translation helpful? Give feedback.
-
If I push the "INIT" button, the panal sends the Midi CC's from the Waveform select modulator from you wrote:
before any callback function attached to a button or slider to stop it running on boot" |
Beta Was this translation helpful? Give feedback.
-
Hi Peter, Try replacing all those lists in the loadVoice function with
That will stop the MIDI cc being sent out. It is probably uiCombo doing that. Unfortunately when I did that, I couldn't reload the panel. Ctrlr was just blank, so to me there is something very unstable in your panel. Something loading at boot that shouldn't be or if it should be, it is crashing the panel. Also, what version of Ctrlr are you working with? |
Beta Was this translation helpful? Give feedback.
-
Hi John, I'm very sorry if I caused your CTRL app to crash... I completely reprogrammed the panel again under V5.6.30.1 I've included some sysex voices as examples. The only thing that no longer works is to sent the correct MIDI CC's. the function „called to calculate the midi value to send, no longer works. OSC1-Coarse - OSC4-Coarse… -> „called to calculate the midi value to send“ the modulators called the function: mod2midi = function(modulator, value)
return m Best regards |
Beta Was this translation helpful? Give feedback.
-
Hi Peter, You are probably better off changing the sliders to uiFixedSlider and using mapped values rather than using the mod2midi function. It's a much simpler solution. I added a control at the bottom of this panel which is the _cutoff- control duplicated showing you how to do this. If you want to use the mod2midi function I rewrote it and it is working, but it's rather complicated as I said! Still, if you are happy with it, use this as a callback function in the interface. and if you use this function disable 👍 In the table below, you will see I have filled in the midiNumber for cutoff to 74 - you'll need to do the same for all the other controls to make it work. Good luck!
|
Beta Was this translation helpful? Give feedback.
-
Hi John, perhaps you could help me again with this. Example: LFO1 Waveform & LFO2 Waveform LOAD Voice & set mods
SAVE & SEND Voice
But now, I have a problem with the MIDI CC’s. Surely there is a simple solution to this, but I don’t know it. Or do I have to use your suggested script „mod2midi“ for this? Uff!! I have a curious error with the Mix Envelope Loop mod and the Mix Envelope Repeat (no MIDI CC’s needed) INIT works perfect:
SEND to midi works perfect:
LOAD from disk (and send to midi) works too, Hmm, it’s the same script like INIT.
Best regards |
Beta Was this translation helpful? Give feedback.
-
Hi Philip!
Are you intending to set the MSB and LSB of one byte (56) in a midi message to two controls? I think the correct values should be: bi:getBitRangeAsInt(4,4) -- MSB 4 bits (higher 4 bits) bi:getBitRangeAsInt(0,4) -- LSB 4 bits (lower 4bits)
I haven't sat down and worked this one out completely, - again you are assigning two controls' values to a single byte, right? - so I think something like: local byte55=BigInteger() local mixenvelopeloop=panel:getModulatorByName("Mix Envelope Loop"):getValueMapped() -- get value local mixenveloperepeat=panel:getModulatorByName("Mix Envelope Repeat"):getValueMapped() -- get value byte55:setBitRangeAsInt(0,4,mixenvelopeloop) byte55:setBitRangeAsInt(4,4,mixenveloperepeat) data:setByte(55, byte55:toInteger()) -- set byte55 panel:getComponent("MIX-ENVELOPES"):setValue(data:getByte(55), false) -- set mod with new value These should be the correct 4 bit nibbles for those waveforms: local t={ for k,v in pairs(t) do SQUARE=msb (01), lsb(0f) Not sure I quite understand this, but the problem is probably coming from the incorrect BigInteger parameters:
discussions 634#discussioncomment-9277890_1_0_2024-05-01.zip |
Beta Was this translation helpful? Give feedback.
-
Hi Peter, Try this:
local programData = MemoryBlock()
fileContentsBuffer = programData
fileRead:loadFileAsData(programData)
Behringer Pro VS Mini LFO+MIXENV Test_JG.zip 🤔😊 |
Beta Was this translation helpful? Give feedback.
-
Thanks so much for your help!! Whether it is related to the values within the modulator? |
Beta Was this translation helpful? Give feedback.
-
Found out, that few Presets had a wrong byte55, for example $41=01000001. byte55 Mix Envelope Loop =x 08h = Off, 1000 byte55 Mix Envelope Repeat =y 00h - 07h, x000 -x111 |
Beta Was this translation helpful? Give feedback.
-
The great thing is, that I can now send and receive "all" Mix Envelope Points and the Envelope Rate via SysEx! Thanks a lot for editing in Lua the "Show VoiceName" :-) |
Beta Was this translation helpful? Give feedback.
-
That's wonderful news! Don't hesitate to ask if you have any more issues! |
Beta Was this translation helpful? Give feedback.
-
Bonjour Peter, Don't worry about No.4 😁 I think I have addressed all the issues. Please check this panel. The way I usually work with bulk dumps is have an associative array (table) of all the modulator names with a value of the byte position in the dump. It's almost the opposite of what you are doing. I am presuming you want to load a 4096 byte file and load the parameters to the panel from the program change button? I couldn't see a modulator named load64Patches = function(--[[ CtrlrComponent --]] comp, --[[ MouseEvent --]] event) if panel:getBootstrapState() then return end local ret = AlertWindow.showOkCancelBox( AlertWindow.QuestionIcon, "LOAD SYSEX FILE", "Load sysex file to the panel\n\nWarning!! All data will be overwritten!", "OK", "Cancel" ) if ret then -- loads a sysex file and uploads local f = utils.openFileWindow("Open file", File.getSpecialLocation(File.userDesktopDirectory), "*.syx", true) fileRead = File(f) local buf = MemoryBlock() fileRead:loadFileAsData(buf) if (buf:getSize() ~= 4096) then return end bulkDump=MemoryBlock(buf) end end --code attached to the program change button loadFromBulkDump = function(--[[ CtrlrModulator --]] mod, --[[ number --]] value, --[[ number --]] source) if bulkDump:getSize() ~= 4096 then return end for k,v in pairs (lookup) do panel:getComponent(k):setValue(bulkDump:getByte(v*(value+1)),true) end end lookup={ ["OSC1-Octave"]=5, ["OSC1-Waveform"]=6, ["OSC1-Level"]=7, ["AutoBend-Select"]=8, ["AutoBend-Mode"]=9, ["AutoBend-Time"]=10, ["AutoBend-Intensity"]=11, ["OSC2-Octave"]=12, ["OSC2-Waveform"]=13, ["OSC2-Level"]=14, ["OSC2-Interval"]=15, ["OSC2-Detune"]=16, ["Noise-Level"]=17, ["AssignMode"]=18, ["ShowLastParameter"]=19, ["VCF-Cutoff"]=20, ["VCF-Resonance"]=21, ["VCF-KbdTrack"]=22, ["VCF-Polarity"]=23, ["VCF-EGIntensity"]=24, ["VCFEG-Attack"]=25, ["VCFEG-Decay"]=26, ["VCFEG-Breakpoint"]=27, ["VCFEG-Slope"]=28, ["VCFEG-Sustain"]=29, ["VCFEG-Release"]=30, ["VCFEG-VelocitySens"]=31, ["VCAEG-Attack"]=32, ["VCAEG-Decay"]=33, ["VCAEG-Breakpoint"]=34, ["VCAEG-Slope"]=35, ["VCAEG-Sustain"]=36, ["VCAEG-Release"]=37, ["VCAEG-VelocitySens"]=38, ["MG-Waveform"]=39, ["MG-Frequency"]=40, ["MG-Delay"]=41, ["MG-OSC"]=42, ["MG-VCF"]=43, ["Bend-OSC"]=44, ["Bend-VCF"]=45, ["Delay-Time"]=46, ["Delay-Factor"]=47, ["Delay-Feedback"]=48, ["Delay-Frequency"]=49, ["Delay-Intensity"]=50, ["Delay-Level"]=51, ["Portamento-Time"]=52, ["AfterTouch-OSCMG"]=53, ["AfterTouch-VCF"]=54, ["AfterTouch-VCA"]=55, } |
Beta Was this translation helpful? Give feedback.
-
I'm going to preempt your next question: I presume the 64 sysex messages are from the 57 byte message only and exclude the separate 7 byte messages in that sysex dump. That's pretty tricky to extract only the 57 byte strings from the 4096 byte message. This is how I would do it. Again not tested of course! 🤔 load64Patches = function(--[[ CtrlrComponent --]] comp --[[ MouseEvent --]], event) if panel:getBootstrapState() then return end local messSize = 57 local ret = AlertWindow.showOkCancelBox( AlertWindow.QuestionIcon, "LOAD SYSEX FILE", "Load sysex file to the panel\n\nWarning!! All data will be overwritten!", "OK", "Cancel" ) if ret then -- loads a sysex file and uploads local f = utils.openFileWindow("Open file", File.getSpecialLocation(File.userDesktopDirectory), "*.syx", true) fileRead = File(f) local buf = MemoryBlock() fileRead:loadFileAsData(buf) if (buf:getSize() ~= BDUMPRAW) then return end bulkDump = MemoryBlock() -- reinitialize global MemoryBlock local tf0, tf7 = {}, {} for i = 0, buf:getSize() - 1 do -- extract the F0 and F7 positions if buf:getByte(i) == 0xf0 then table.insert(tf0, i) elseif buf:getByte(i) == 0xf7 then table.insert(tf7, i + 1) end end assert(#tf0 == #tf7) -- make sure they are the same size! for i, v in ipairs(tf0) do if tf7[i] - v == messSize then -- ignore the 7 byte message and add this to memoryblock local tmp = MemoryBlock(buf:getRange(v, messSize)) bulkDump:append(tmp) end end end end constructor = function() bulkDump=MemoryBlock() BDUMPDATA=3648 BDUMPRAW=4096 end loadFromBulkDump = function(--[[ CtrlrModulator --]] mod --[[ number --]], value --[[ number --]], source) if bulkDump:getSize() ~= BDUMPDATA then return end for k, v in pairs(lookup) do panel:getComponent(k):setValue(bulkDump:getByte(v * (value + 1)), true) end end |
Beta Was this translation helpful? Give feedback.
-
Hi Peter, Here's a working Save File. I use |
Beta Was this translation helpful? Give feedback.
-
In sysex FF means 255, but it also is used to represent -1, so if your panel has a value of 255 or -1 in a panel, the sysex will be FF but that is no good because sysex data has a limit of 127 0111 1111. That's why large values over 127 0x7f are represented using 2 bytes, so in four bit nibbles 255 will be represented as But in signed 8 bit or 16 bit integers (just integers not sysex) you get (in 8bit) 1111 1111 for -1 (remembering in an unsigned integer (no negatives) it would be 255 (15*16 + 15) )- I think lua uses 32 bit signed integers as a base e.g. 1111 1111 1111 1111 1111 1111 1111 1111 for -1, so when a Edit :lua actually uses 64 bit integers! Phew !! |
Beta Was this translation helpful? Give feedback.
-
Hello everyone,
slowly but not really sure, I understand LUA to create a panel for a synth. Behringer Pro VS mini
Actually most of the functions are okay! so far :-)
But:
Curiously, for some reason 12 modulators now sends the MIDI CC value twice !?!
And I just can't find the error...
I gave up to calculating the checksum to send a SysEx Dump per midi and for save the voice to disk.
I've looked a lot in different panels, but I just don't understand how to implement it for my panel.
Would be kind of someone to support me with this?
I would like to understand it!
Everything is loaded perfect („LOAD“, RECEIVE“ and „INIT“) into the memory via LUA
and all modulators are then
Behringer Pro VS Mini_1_2_V1.2.13_2024-04-20_18-28.bpanelz.zip
refreshed with the new values.
The dump from a single voice has a size of 75 bytes.
Header Byte 1 - 8
xx = data bytes 9 - 73
Checksum Byte 74
EOX Byte 75
F0 00 20 32 00 01 4C 10 XX CHKSUM F7.
Kind regards
Peter
Beta Was this translation helpful? Give feedback.
All reactions