Home › Forums › General › Programming › Sysex Encoding for Alesis Quadraverb
Tagged: sysex
- This topic has 11 replies, 3 voices, and was last updated 8 years ago by jbeuckm.
-
AuthorPosts
-
March 29, 2016 at 7:01 am #68742
I’m new to Ctlrlr but understand MIDI. The Quadraverb sends and receives 16bit parameter data in the following encoding:
Data: B7 B6 B5 B4 B3 B2 B1 B0 (MSB) A7 A6 A5 A4 A3 A2 A1 A0 (LSB) Sent: 0 A7 A6 A5 A4 A3 A2 A1 <value1> 0 A0 B7 B6 B5 B4 B3 B2 <value2> 0 B1 B0 0 0 0 0 0 <value3>
So, the value 0x01 would be encoded as 0x00 0x40 0x00
I’m looking for how to do this encoding in Lua and then where to put the code. For instance, if I set up a uiSlider, I know how to set the min/max and a (wrong) sysex formula in the properties panel. How/where do I override that sysex formula and write an encoding function?
UPDATE:
After some digging, it seems like I probably want to write a Lua method for the property “Called to calculate MIDI value to send”. I have been looking through a few panels and haven’t found an implementation yet. Is there documentation for what that method will receive and return, or can someone point me to a reference implementation?
March 29, 2016 at 7:32 pm #68746Hi,
saw your message in the chat… good that you put a post.
It looks like it is the same as DSI packing/unpacking.
Atom built some routine for that.
Otherwise you can also do as Carl did for the P08 panel.Here is what I’m doing…
Load method:
-- Called when a modulator value changes -- @modulator http://ctrlr.org/api/class_ctrlr_modulator.html -- @modValue new numeric value of the modulator -- LoadProgram = function(modulator, modValue) -- No action if the panel is in bootstrap or program states if not isPanelReady() then return end f = utils.openFileWindow ("Load Pro2 packed program dump", File(""), "*.syx", true) if f:existsAsFile() then Pro2PackedProgramData = MemoryBlock() f:loadFileAsData(Pro2PackedProgramData) FileSize = Pro2PackedProgramData:getSize() if FileSize == 1178 then --console("Loading DSI Pro 2 sysex program dump file...") bLoadingProgram = true -- Adapt LCD txtCurrentValue:getComponent():setPropertyString ("uiLabelText", "LOADING...") iBank = Pro2PackedProgramData:getByte(04)+1 if iBank<4 then txtBank:getComponent():setPropertyString ("uiLabelText", "U"..iBank) else txtBank:getComponent():setPropertyString ("uiLabelText", "F"..iBank) end modBank:setValue(iBank, true) LastBank = txtBank:getComponent():getProperty("uiLabelText") LastProgram = Pro2PackedProgramData:getByte(05)+1 txtProgram:getComponent():setPropertyString ("uiLabelText", "P"..LastProgram) modProgram:setValue(LastProgram+1, true) txtSavedValue:getComponent():setPropertyString ("uiLabelText", LastBank.." P"..LastProgram) LoadedProgramData = utils.unpackDsiData(Pro2PackedProgramData:getRange(6,1171)) FileSize = LoadedProgramData:getSize() --console("..Unpacked size is "..FileSize) if FileSize == 1024 then LoadProgramData(LoadedProgramData) end -- Starting a timer to reset bLoadingProgram to false after that all modulators have processed their OnChange function timer:setCallback (1, timerCallback) timer:startTimer(1, 200) else utils.warnWindow ("Load Pro2 packed program dump", "Invalid file") end end end
isPanelReady() is simply
-- -- Check if the panel is not busy to load or receiving a program -- isPanelReady = function() if panel:getBootstrapState() == false and panel:getProgramState() == false then return (true) else return (false) end end
LoadProgramData is like:
-- -- Set all modulators according to program parameters -- @LoadedProgramData MemoryBlock -- LoadProgramData = function(LoadedProgramData) -- No action if the panel is in bootstrap or program states if not isPanelReady() then return end -- *** Main *** txtProgramName:getComponent():setPropertyString ("uiLabelText", LoadedProgramData:getRange(378,20):toString()) -- *** Oscillators and Filters *** -- Oscillator 1 modOsc1Sync:setValue(LoadedProgramData:getByte(52), true) modOsc1Pitch:setValue(LoadedProgramData:getByte(0), true) modOsc1FineTune:setValue(LoadedProgramData:getByte(4), true) ... end
Packing will use the same philosophy but with the method utils.packDsiData
Good luck!
March 29, 2016 at 8:07 pm #68747I did this for 4-bit nibbleized 12bit vaule. It is not the same thing, but maybe it is of some use for you. Modulator “OSC2 Det” set to min=0, max=4095.
–decompose the 12-Bit value into three 4-bit nibbles:
value=CtrlrLuaBigInteger(panel:getModulatorByName(“OSC2_Det”):getModulatorValue())
low=string.format(“%.2x”, value:getBitRangeAsInt(0,4))
mid=string.format(“%.2x”, value:getBitRangeAsInt(4,4))
hig=string.format(“%.2x”, value:getBitRangeAsInt(8,4))
panel:sendMidiMessageNow(CtrlrMidiMessage(“f0″..”00″..”00″..”2F”..”01″..low..mid..hig….”f7″))–compose the 12-Bit value out of three 4-bit nibbles (from a sysex-dump):
mid=DumpData:getByte(4)
low=DumpData:getByte(5)
hig=DumpData:getByte(7)
val = (low)+(mid*16)+(hig*256)
panel:getModulatorByName(“OSC2_Det”):setModulatorValue(val,false,false,false)- This reply was modified 8 years ago by Possemo.
March 29, 2016 at 10:05 pm #68750Thanks goodweather and possemo! packDsiData looks close to what I need (Alesis data bytes are reversed) and I can see how to build the transform from passemo’s code.
How do I actually get this code to where it’s needed? I see how to implement a new method for “Called to calculate MIDI value to send” but I haven’t seen documentation for what args it receives and should return. Shouldn’t I be able to setup sysex output to always use f0, my vendor & device code, then f7 after?
panel:sendMidiMessageNow(CtrlrMidiMessage(“f0″..”00″..”00″..”2F”..”01″
does not look D.R.Y. 🙁- This reply was modified 8 years ago by jbeuckm.
March 30, 2016 at 8:17 pm #68762One way to do this (btw, I haven’t yet done it myself 😉 at the momennt I’m focusing on Reading messages) is to create a panel and put a uiImageButton on it. Set it to Momentary instead of normal so it will act as a push button.
Then open the Lua Editor and create methods as I explained above.
Attach the LoadProgram method to your button. It will remove the not necessary data and unpack the actual dump (you need to adapt the sizes to your Alesis case).
Then once you can read a file, you can try writing one.
As I said, look at Carl’s panel (Power08) or at the Virus panel made by dasfaker.
Good luck!March 30, 2016 at 8:35 pm #68764Here is an example of Carl’s method to send data
function sendProgramDataDump(bank_number, patch_number) -- This variable stops index issues during panel bootup if panel:getRestoreState() == true or panel:getProgramState() == true then return end console("creating midi message...") MsgPrefix = "f0 01 23 02" -- This prefix send a Program data dump. This method is destructive : destination patch will be overwritten MsgSuffix = "f7" -- specify bank and patch number destination PatchBankHex = string.format("%.2x", bank_number) PatchNumberHex = string.format("%.2x", patch_number) PatchData = table.concat(PatchDataValuesTable, " ", 5, 443) MsgComplete = MsgPrefix.." "..PatchBankHex.." "..PatchNumberHex.." "..PatchData.." "..MsgSuffix panel:sendMidiMessageNow(CtrlrMidiMessage(MsgComplete)) console ("Program Data Dump sent to Prophet08") end
Use the same philosophy but use utils.packDsiData function for the PatchData…
Be patient, it will come after a few weeks…March 31, 2016 at 5:13 pm #68768Thanks again for the help. There is a lot of good stuff in the Power08 example. I am getting things to work now. A newbie has to realize two things:
1. Connect Lua methods to the panel by properties like “Called when…”.
2. When you save your Lua, use “Save and compile all” to test your changes.
March 31, 2016 at 7:28 pm #68772Yes.
Have a look at my Ctrlr step by step guideApril 2, 2016 at 5:44 am #68805That step-by-step guide is mandatory for Ctrlr learners.
Here’s where I ended up for encoding the Quadraverb params. I have not tried to encode an entire patch yet.
encodeQuadraverbData = function(data, numBytes) orig = BigInteger(data) reversed = BigInteger(0) -- reverse bytes so lsB first / msB last for i=0,numBytes-1 do byte = orig:getBitRangeAsInt(i*8, 8) reversed:setBitRangeAsInt((numBytes - 1 - i)*8, 8, byte) end requiredBits = numBytes * 8 encodedParts = math.ceil(requiredBits / 7) encodedBits = encodedParts * 7 zeroPadding = encodedBits - requiredBits reversed:shiftBits(zeroPadding, 0) bytes = {} for i=1,encodedParts do byte = reversed:getBitRangeAsInt(encodedBits-(i*7),7) table.insert(bytes, string.format('%.2x', byte)) end return table.concat(bytes, ' ') end
April 2, 2016 at 10:27 am #68806I would have used the provided utils.unpackDsiData and utils.packDsiData as they are doing all the job and you don’t need to code anything (except isolating the bit range to pack/unpack) but this is the beauty of programming, everybody can choose his own way 🙂
Thx about your feedback on the Step by step guide.
The version 2.x I will make will be even better with more examples, Lua code, etc…April 2, 2016 at 6:14 pm #68828your code looks nice jbeuckm. I didn’t knew shiftBits – very useful.
April 3, 2016 at 2:08 am #68832@goodweather I think packDsiData does something different than what I need for the Quadraverb…
console("i quadraveb packDsiData") for i=0,300,31 do mb = MemoryBlock(string.format('%.2x', i)) packed = utils.packDsiData(mb) dsi = packed:toHexString(2) qv = encodeQuadraverbData(i,2) console(string.format("%.2x %s %s", i, qv, dsi)) end
gives…
LUA>> i quadraveb packDsiData LUA>> 00 00 00 00 0000 LUA>> 1f 0f 40 00 001f LUA>> 3e 1f 00 00 003e LUA>> 5d 2e 40 00 005d LUA>> 7c 3e 00 00 007c LUA>> 9b 4d 40 00 011b LUA>> ba 5d 00 00 013a LUA>> d9 6c 40 00 0159 LUA>> f8 7c 00 00 0178 LUA>> 117 0b 40 20 0011
- This reply was modified 8 years ago by jbeuckm. Reason: added results of test code
-
AuthorPosts
- The forum ‘Programming’ is closed to new topics and replies.