ReFourBitNibblizing modulatorValue to send to the Ensoniq DP4

Home Forums General Programming ReFourBitNibblizing modulatorValue to send to the Ensoniq DP4

Viewing 14 posts - 1 through 14 (of 14 total)
  • Author
    Posts
  • #118457
    damien
    Participant
      • Topics: 15
      • Replies: 62
      • Total: 77

      Hi Ctrlr People,

      My Ensoniq DP4 Panel is going very well. All the Parameter values received from the DP4 are mapped properly to the Ctrlr panel (algo name, values, button position, confif routing, current bypassed unit etc)

      I assigned different custom values to the knobs related to the DSP unit they control parameters (ModulatorCustomIndexGroup).
      Each knobs (depending on the current DSP algo) is assigned it’s correct Parameter Index via ModulatorCustomIndex

      So, I can right now send sysex messages to the DP4, but the format of the Parameter Index and the Parameter need to be adjusted to the specs.

      It has to be “ReFourBitNibblized” (once again)

      This is my script method to send parameter value changes entitled “AlgoParamValueSend”

      
      AlgoParamValueSend = function(mod, value)
      
      EnsoniqDP4Header = "F0 0F 40 00" -- Ensoniq DP4 Header
      ID = "00" -- Midi Device ID
      MsgType = "01" -- For Command Message
      Cmd = "00 01"- For Parameter Change Command
      Unit = mod:getProperty("modulatorCustomIndexGroup")
      -- Format 00 0U [stored in the modulatorCustomIndexGroup Field]
      
      Param = mod:getProperty("modulatorCustomIndex"):ReFourBitNybblize()
      -- Format 0p 0P [stored in the modulatorCustomIndex Field]
      
      ParamValue = ReFourBitNybblize(value)		
      -- Format 0v 0v 0V 0V [Value calculated with ReFourBitNybblization function :) ]
      
      EOX = "F7" -- End of SySex Message is always F7
      
      msg = table.concat({EnsoniqDP4Header, ID, MsgType, Cmd, Unit, Param, ParamValue, EOX}, ' ')
      
      panel:sendMidiMessageNow(CtrlrMidiMessage(msg))
      
      end

      This is the Correct format according to the manual :

      Message format – 8 bit data bytes must be transmitted and received using the 7 bit data format of MIDI, where the MSB of these data bytes must always be zero, so each of the 8 bit data byte is converted to two 4 bit nybbles for transmission. These nybbles each sent as bytes whose upper four bits are all zero. This is a description of the format of all nybblized data bytes within the packet frame as they are transmitted or received via MIDI.
      0000HHHH with H = Hi 4 bits of data byte – transmitted first
      0000LLLL with L = Lo 4 bits of data byte
      This represents how the 8 bit byte HHHHLLLL would be transmitted.

      And for this particular type of Parameter Value Change TX transfer:

      Parameter Change Command
      This message allows any effect parameter to be edited under remote control.
      When this message is received, the system will automatically be put into Edit (or System) mode and the specified unit will become active.
      The unit number in the message (specified using the following codes) indicates which logical group contains the parameter to be edited.
      The parameter number must be legal within the specified group, with unit parameters based on effect type and config parameters based on Input Config.
      The new parameter value is always transmitted as a 16 bit word.
      If the parameter only uses an 8 bit value, then the MSByte should be transmitted as zero.

      [header ] DP/4 SysEx Header
      00000001 : Command Message code Note: the following 5 bytes must each be transmitted as nybbles!
      00000001 : Parameter Change command code
      00000uuu : uuu = unit number (0..4)
      0ppppppp ppppppp = parameter number (0..127)
      mmmnnm : muummmmmm = parameter value MSByte
      llllllll : llllllll = parameter value LSByte
      11110111 End of Exclusive status byte

      Example (in hexadecimal notation, assuming MIDI Device ID = 01 transmitted as 00):

      F0 0F 40 00 00(ID) 01(Msg) 00 01(Cmd) 00 02(Unit) 00 03(Param) 00 00 07 0F(Value) F7

      To change UnitC : Param 03 Value 127 (7F)

      That would so helpful if you someone could give me the ReFourBitNybblize(value) function script so that I can send the good values to the DP4.

      Parameter Index must be 2 bytes and Parameter Value must be 4 bytes long.

      Thanks a lot in advance for your support

      Damien

      #118458
      damien
      Participant
        • Topics: 15
        • Replies: 62
        • Total: 77
          Edit

        (to avoid ghost posts) :

        I assigned the dedicated Parameter Index for each knobs in the ModulatorCustomIndex Field. This value is in Decimal (ex: 08) So it has to be converted from decimal to hex within 2 FourBitNibblized bytes 🙂

        Cheers

        #118459
        dnaldoog
        Participant
          • Topics: 4
          • Replies: 480
          • Total: 484
          • ★★

          ‘Ghost Posts’! — the story of my life:

          Try this as a suggestion:

          
          splitInteger2Nybble=function(int) -- integer input
          local t={}
          local msb=bit.rshift(bit.band(int,240),4)
          local lsb = bit.band(int,15)
          table.insert(t,string.format("%.2x",msb))
          table.insert(t,string.format("%.2x",lsb))
          return t
          end -- function
          
          AlgoParamValueSend = function(mod, value)
          local m=MemoryBlock()
          local EnsoniqDP4Header = "F0 0F 40 00" -- Ensoniq DP4 Header
          local ID = "00" -- Midi Device ID
          local MsgType = "01" -- For Command Message
          local Cmd = "00 01"-- For Parameter Change Command
          local Unit = "0"..mod:getProperty("modulatorCustomIndexGroup")
          -- Format 00 0U [stored in the modulatorCustomIndexGroup Field]
          local EOX = "F7" -- End of SySex Message is always F7
          
          local Param =splitInteger2Nybble(mod:getProperty("modulatorCustomIndex"))
          local ParamValue = splitInteger2Nybble(value)
          Param = table.concat(Param ," ")
          ParamValue=table.concat(ParamValue ," ")
          
          local strcat=string.format("%s %s %s %s %s %s %s %s",EnsoniqDP4Header,ID,MsgType,Cmd,Unit,Param,ParamValue,EOX)
          m:loadFromHexString(strcat)
          
          panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1)))
          
          end --function
          

          Regards,

          • This reply was modified 3 years, 10 months ago by dnaldoog. Reason: cleaned up code
          • This reply was modified 3 years, 10 months ago by dnaldoog. Reason: Declare m=MemoryBlock()
          #118462
          damien
          Participant
            • Topics: 15
            • Replies: 62
            • Total: 77

            Thanks dnldoog,

            I tried your method but it’s not working and I don’t know where the error is coming from

            this is the adjusted method

            --
            -- Called when a modulator value changes
            -- @mod   http://ctrlr.org/api/class_ctrlr_modulator.html
            -- @value    new numeric value of the modulator
            --
            
            splitInteger2Nybble = function(int)  -- integer input
            local t={}
            local msb=bit.rshift(bit.band(int,240),4)
            local lsb = bit.band(int,15)
            table.insert(t,string.format("%.2x",msb))
            table.insert(t,string.format("%.2x",lsb))
            return t
            end -- function
            
            AlgoParamValueSend = function(mod, value)
            
            local EnsoniqDP4Header = "F0 0F 40 00"  -- Ensoniq DP4 Header
            local MidiID = "00"  -- Midi Device ID
            local MsgType = "01" -- For Command Message
            local Cmd = "00 01"  -- For Parameter Change Command
            local Unit = mod:getProperty("modulatorCustomIndexGroup") -- Format 00 0U [stored in the modulatorCustomIndexGroup Field]
            local ParamId = mod:getProperty("modulatorCustomIndex") -- Format 0p 0P [stored in the modulatorCustomIndex Field]
            local EOX = "F7" -- End of SySex Message is always F7
            
            local Param = splitInteger2Nybble(ParamId)
            local ParamValue = splitInteger2Nybble(value)
            
            Param = table.concat(Param ," ")
            ParamValue = table.concat(ParamValue ," ")
            
            local strcat = string.format("%s %s %s %s %s %s %s %s", EnsoniqDP4Header, ID, MsgType, Cmd, Unit, Param, ParamValue, EOX)
            m:loadFromHexString(strcat)
            
            panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1)))
            
            end --function
            

            I get an error while moving a slider 🙁

            36: bad argument #3 to ‘format’ (string expected, got nil)

            I’m really stuck. If only it could be regular cc value changes 🙂

            Can you please help with that?

            Thanks a lot! Have a nice weekend

            • This reply was modified 3 years, 10 months ago by damien. Reason: typo again
            • This reply was modified 3 years, 10 months ago by damien. Reason: typo again
            #118465
            dnaldoog
            Participant
              • Topics: 4
              • Replies: 480
              • Total: 484
              • ★★

              It looks like I didn’t declare local m=MemoryBlock().

              Does that fix it?

              #118471
              damien
              Participant
                • Topics: 15
                • Replies: 62
                • Total: 77

                Hi Dnalddoog,

                We are getting close!
                I struggled to make it work. The “strcat” formula never worked.

                The only way to send messages was to forget about the memoryblock and go straight with a basic concat formula.
                The Format for Param is alright (0p 0p) but the format for the ParamValue is not good since I only send this format (0v 0v) and I need a 16bit word (0v 0v 0v 0v) that allows to send values above the 128 range.
                For exemple if I move the delay time parameter whose max value is 3668 I only send 05h 04h.

                So It would require another type of nybblization with 0v 0v 0v 0v format as required by the specs.
                If the value is just a 8bit field 00 00 are required before.

                This is what I have :

                --
                -- Called when a modulator value changes
                -- @mod   http://ctrlr.org/api/class_ctrlr_modulator.html
                -- @value    new numeric value of the modulator
                --
                
                AlgoParamValueSend = function(mod, value)
                
                EnsoniqDP4Header = "F0 0F 40 00" 						-- Ensoniq DP4 Header
                MidiID = "00" 											-- Midi Device ID
                MsgType = "01" 											-- For Command Message
                Cmd = "00 01"											-- For Parameter Change Command
                Unit = mod:getProperty("modulatorCustomIndexGroup") 	-- Format 00 0U [stored in the modulatorCustomIndexGroup Field]
                ParamId = mod:getProperty("modulatorCustomIndex") 		-- Format 0p 0P [stored in the modulatorCustomIndex Field]
                EOX = "F7" 												-- Tail of SySex Message is always F7
                NameID = mod:getProperty("Name")
                
                console ("Check the Mod value : " .. tostring(value))
                console ("Check the Mod Unit : " .. tostring(Unit))
                
                Param = splitInteger2Nybble(ParamId)
                ParamValue = splitInteger4Nybble(value)
                
                Param = table.concat(Param ," ")
                console("Check the Param String : ".. tostring(Param))
                
                ParamValue = table.concat(ParamValue ," ")
                console("Check the ParamValue String : ".. tostring(ParamValue))
                
                msg = table.concat({EnsoniqDP4Header, MidiID, MsgType, Cmd, Unit, Param, ParamValue, EOX}, ' ')
                panel:sendMidiMessageNow(CtrlrMidiMessage(msg))
                
                console("Check the sent message : ".. tostring(msg))
                
                panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1)))
                
                end --function
                
                splitInteger2Nybble = function(int)  -- integer input
                local t={}
                local msb=bit.rshift(bit.band(int,240),4)
                local lsb = bit.band(int,15)
                table.insert(t,string.format("%.2x",msb))
                table.insert(t,string.format("%.2x",lsb))
                return t
                end -- function
                
                splitInteger4Nybble = function(int)  -- integer input
                local t={}
                local msb=bit.rshift(bit.band(int,240),4)
                local lsb = bit.band(int,15)
                table.insert(t,string.format("%.2x",msb))
                table.insert(t,string.format("%.2x",lsb))
                return t
                end -- function'
                
                and the console shows for 2 value change above 128 :
                
                

                LUA>> Check the Mod value : 2614
                LUA>> Check the Mod Unit : 00 02
                LUA>> Check the Param String : 00 03
                LUA>> Check the ParamValue String : 03 06
                LUA>> Check the sent message : F0 0F 40 00 00 01 00 01 00 02 00 03 03 06 F7
                LUA>> Check the Mod value : 3668
                LUA>> Check the Mod Unit : 00 02
                LUA>> Check the Param String : 00 03
                LUA>> Check the ParamValue String : 05 04
                LUA>> Check the sent message : F0 0F 40 00 00 01 00 01 00 02 00 03 05 04 F7
                `

                We are close to the end 🙂 The function splitInteger4Nybble needs to be adjusted.

                Next I’ll work on the algorhythm change via a click on the LCDs and shows a menu with all FX and presets available.

                Thanks in advance for the help.

                #118479
                damien
                Participant
                  • Topics: 15
                  • Replies: 62
                  • Total: 77

                  edit (to avoid ghost posts)

                  I removed
                  ‘panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1)))’
                  obviously 🙂

                  #118487
                  dnaldoog
                  Participant
                    • Topics: 4
                    • Replies: 480
                    • Total: 484
                    • ★★

                    Hi Damien,

                    Try this. I tried looking up the manual, but I couldn’t see any MIDI sysex implementation so I am flying in the dark.

                    
                    splitInteger4Nybble=function(int) -- integer input
                    local t={}
                    local msb16=bit.rshift(bit.band(int,0xf000),12)
                    local lsb16=bit.rshift(bit.band(int,0xf00),8)
                    local msb8=bit.rshift(bit.band(int,0xf0),4)
                    local lsb8 = bit.band(int,0xf)
                    table.insert(t,string.format("%.2x",msb16))
                    table.insert(t,string.format("%.2x",lsb16))
                    table.insert(t,string.format("%.2x",msb8))
                    table.insert(t,string.format("%.2x",lsb8))
                    console(String(table.concat(t,"|")))
                    return t
                    end
                    
                    #118488
                    dnaldoog
                    Participant
                      • Topics: 4
                      • Replies: 480
                      • Total: 484
                      • ★★

                      … or you can also use JUCE BigInteger() class instead of lua bitwise operations:

                      
                      splitInteger4Nybble2=function(int) -- integer input
                      local t={}
                      local bv=BigInteger(int)
                      local msb16=bv:getBitRangeAsInt(16,4)
                      local lsb16=bv:getBitRangeAsInt(8,4)
                      local msb8=bv:getBitRangeAsInt(4,4)
                      local lsb8 = bv:getBitRangeAsInt(0,4)
                      table.insert(t,string.format("%.2x",msb16))
                      table.insert(t,string.format("%.2x",lsb16))
                      table.insert(t,string.format("%.2x",msb8))
                      table.insert(t,string.format("%.2x",lsb8))
                      console(String(table.concat(t,"|")))
                      return t
                      end
                      
                      #118489
                      damien
                      Participant
                        • Topics: 15
                        • Replies: 62
                        • Total: 77

                        There you go! We have it. it’s working for every parameter value from 0 to 65535

                        I don’t understand exactly the arguments and functions of the script you wrote but it’s working 100% this time.
                        Negative value from decimal signed 2’s complement are working as well.
                        A+ mister Dnaldoog. You’re the boss 🙂

                        ex:
                        Unit B (00 02) Decay time (param #3) Param value 3668 :
                        F0 0F 40 00 00 01 00 01 00 02 00 03 00 0E 05 04 F7

                        >>> E54h

                        Thank you ever so much

                        #118490
                        dnaldoog
                        Participant
                          • Topics: 4
                          • Replies: 480
                          • Total: 484
                          • ★★

                          I think this is a much faster and more efficient implementation;

                          
                          splitInteger2NybbleX=function(int) -- integer input
                          return bit.rshift(bit.band(int,0xf0),4),bit.band(int,0xf)
                          end
                          
                          splitInteger4NybbleBIX=function(int) -- integer input
                          local bv=BigInteger(int)
                          return bv:getBitRangeAsInt(16,4),bv:getBitRangeAsInt(8,4),bv:getBitRangeAsInt(4,4),bv:getBitRangeAsInt(0,4)
                          end
                          
                          AlgoParamValueSend = function(mod, value)
                          local t={}
                          local Unit = mod:getProperty("modulatorCustomIndexGroup")
                          local EOX = 0xF7 -- End of SySex Message is always F7
                          local m=MemoryBlock(string.format("F0 0F 40 00 00 01 00 01 0%.2X",Unit ))
                          
                          local ParamMsb,ParamLsb = splitInteger2NybbleX(mod:getProperty("modulatorCustomIndex"))
                          local ParamValueA,ParamValueB,ParamValueC,ParamValueD=splitInteger4NybbleBIX(value)
                          table.insert(t,ParamMsb)
                          table.insert(t,ParamLsb)
                          table.insert(t,ParamValueA)
                          table.insert(t,ParamValueB)
                          table.insert(t,ParamValueC)
                          table.insert(t,ParamValueD)
                          table.insert(t,EOX)
                          local str=MemoryBlock()
                          str:createFromTable(t)
                          m:append(str)
                          --console(String(m:toHexString(1)))
                          panel:sendMidiMessageNow(CtrlrMidiMessage(m:toHexString(1)))
                          end --function
                          
                          #118492
                          dnaldoog
                          Participant
                            • Topics: 4
                            • Replies: 480
                            • Total: 484
                            • ★★

                            Didn’t read your comment! Thank you :✨ just discovered (by accident) the windows key and colon : opens up an emojis menu!

                            Just a quick explanation:

                            The value 3668 in binary is:

                            0000 1110 0101 0100

                            That’s a 16 bit unsigned integer, which in HEX is represented as 0x0e54

                            Each Block in HEX is

                            0000 00
                            1110 0E
                            0101 05
                            0100 04

                            so programmatically you have to find a way to extract the values 00 0E 05 04 and send as sysex.

                            The class BigInteger does that. BigInteger() comes from the JUCE libraries.

                            Also you can do it with bit masking and shifting bit.band and bit.rshift. These are lua implementations. I don’t know which is faster. Probably about the same, but I imagine doing table.concat() would be very slow in comparison with loading everything into MemoryBlocks.

                            see attached panel with new code from last post
                            Don’t want last post to get ghosted!

                            • This reply was modified 3 years, 10 months ago by dnaldoog. Reason: attached example panel
                            Attachments:
                            You must be logged in to view attached files.
                            #118495
                            damien
                            Participant
                              • Topics: 15
                              • Replies: 62
                              • Total: 77

                              Thank for the explanation. The Juce function is lighter on the cpu, Knobs were slow to move with the table concat function and without using memoryblock, you were absolutely right.
                              Your last script is on point. It’s working great I must say. Thanks so much. Have a nice week!

                              • This reply was modified 3 years, 10 months ago by damien. Reason: typo
                              #118497
                              dnaldoog
                              Participant
                                • Topics: 4
                                • Replies: 480
                                • Total: 484
                                • ★★

                                Great to hear it’s working and interesting that BigInteger is faster (must remember that).

                                So, to implement BigInteger() in the first function you could change it thus:

                                
                                splitInteger2NybbleX=function(int) -- integer input
                                return bit.rshift(bit.band(int,0xf0),4),bit.band(int,0xf)
                                end
                                

                                … becomes:

                                
                                splitInteger2NybbleX=function(int) -- integer input
                                local bv=BigInteger(tonumber(int))
                                return bv:getBitRangeAsInt(4,4),bv:getBitRangeAsInt(0,4)
                                end
                                

                                † Note the incoming function parameter needs to be recast with tonumber()

                                ?‍?

                              Viewing 14 posts - 1 through 14 (of 14 total)
                              • The forum ‘Programming’ is closed to new topics and replies.
                              There is currently 0 users and 44 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