Sysex Encoding for Alesis Quadraverb

Home Forums General Programming Sysex Encoding for Alesis Quadraverb

Tagged: 

Viewing 12 posts - 1 through 12 (of 12 total)
  • Author
    Posts
  • #68742
    jbeuckm
    Participant
      • Topics: 2
      • Replies: 6
      • Total: 8

      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?

      • This topic was modified 8 years, 1 month ago by jbeuckm.
      • This topic was modified 8 years, 1 month ago by jbeuckm. Reason: updated question with my learning
      #68746
      goodweather
      Participant
        • Topics: 45
        • Replies: 550
        • Total: 595
        • ★★★

        Hi,
        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!

        #68747
        Possemo
        Participant
          • Topics: 14
          • Replies: 638
          • Total: 652
          • ★★★

          I 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, 1 month ago by Possemo.
          #68750
          jbeuckm
          Participant
            • Topics: 2
            • Replies: 6
            • Total: 8

            Thanks 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, 1 month ago by jbeuckm.
            #68762
            goodweather
            Participant
              • Topics: 45
              • Replies: 550
              • Total: 595
              • ★★★

              One 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!

              #68764
              goodweather
              Participant
                • Topics: 45
                • Replies: 550
                • Total: 595
                • ★★★

                Here 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…

                #68768
                jbeuckm
                Participant
                  • Topics: 2
                  • Replies: 6
                  • Total: 8

                  Thanks 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.

                  #68772
                  goodweather
                  Participant
                    • Topics: 45
                    • Replies: 550
                    • Total: 595
                    • ★★★

                    Yes.
                    Have a look at my Ctrlr step by step guide

                    #68805
                    jbeuckm
                    Participant
                      • Topics: 2
                      • Replies: 6
                      • Total: 8

                      That 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
                      #68806
                      goodweather
                      Participant
                        • Topics: 45
                        • Replies: 550
                        • Total: 595
                        • ★★★

                        I 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…

                        #68828
                        Possemo
                        Participant
                          • Topics: 14
                          • Replies: 638
                          • Total: 652
                          • ★★★

                          your code looks nice jbeuckm. I didn’t knew shiftBits – very useful.

                          #68832
                          jbeuckm
                          Participant
                            • Topics: 2
                            • Replies: 6
                            • Total: 8

                            @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
                          Viewing 12 posts - 1 through 12 (of 12 total)
                          • The forum ‘Programming’ is closed to new topics and replies.
                          There is currently 0 users and 50 guests online
                          No users are currently active
                          Forum Statistics
                          Threads: 2,495, Posts: 17,374, Members: 77,605
                          Most users ever online was 12 on January 22, 2019 3:47 pm
                          Ctrlr