send lua output to sysex string

Home Forums General Programming send lua output to sysex string

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • #586
    tomtom
    Participant
      • Topics: 1
      • Replies: 1
      • Total: 2

      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!

      #4161
      LilCthulhu
      Participant
        • Topics: 9
        • Replies: 67
        • Total: 76

        If 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 <img decoding=” title=”Wink” />

        #4162
        TK.
        Participant
          • Topics: 6
          • Replies: 42
          • Total: 48

          In 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 <img decoding=” 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.

          #4163
          LilCthulhu
          Participant
            • Topics: 9
            • Replies: 67
            • Total: 76

            TK 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 = 65

            Once 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 = 87

            The 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 <img decoding=” title=”Wink” /> and I don’t rely on a bunch of modulator sysex msg to edit <img decoding=” title=”Wink” /> + I can use those values to automatically generates part of codes, or vstindex, or anything useful <img decoding=” title=”Wink” />

            #4164
            TK.
            Participant
              • Topics: 6
              • Replies: 42
              • Total: 48

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

              #4165
              LilCthulhu
              Participant
                • Topics: 9
                • Replies: 67
                • Total: 76

                Ok, 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 <img decoding=” 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 <img decoding=” title=”Wink” />

                [code:1s65pwgv]
                function sendSysexModulatorValue(modulator, newValue)
                local uid, val, add, lsb, chk, syx

                uid = tonumber(modulator:getPropertyString("modulatorCustomIndexGroup")) + tonumber(modulator:getPropertyString("modulatorCustomIndex"))

                if (uid <= 383) then
                add = math.floor(uid / 128)
                lsb = (uid % 128)
                val = newValue

                if (modulator:getComponent():getPropertyString("uiType") == "uiSlider") then
                val = val – modulator:getComponent():getPropertyInt("uiSliderMin")
                end

                chk = (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 <img decoding=” title=”Wink” />

                [code:1s65pwgv]
                function buildPatchModulators().
                local uci, ucg, uid

                gPatchModulators = {}

                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 <img decoding=” title=”Wink” /> !!!

                [code:1s65pwgv]
                function assignSysexPatchValues(programData)
                for uid = 1, 384, 1 do
                assignPatchModulatorValue(gPatchModulators[uid], tonumber(programData:getByte(uid – 1)))
                end
                end

                function assignPatchModulatorValue(modulator, value)
                local val = value

                if (modulator ~= nil) then
                if (modulator:getComponent():getPropertyString("uiType") == "uiSlider") then
                val = val + modulator:getComponent():getPropertyInt("uiSliderMin")
                end

                modulator:setModulatorValue(val, false, false, true)
                end
                end
                [/code:1s65pwgv]

                I was able to build my pannel under 2 weeks by using those strategies <img decoding=” title=”Wink” /> and that’s why I’m using LUA all the way in my coding <img decoding=” title=”Wink” />

                Enjoy <img decoding=” title=”Wink” /> !

                #4166
                LilCthulhu
                Participant
                  • Topics: 9
                  • Replies: 67
                  • Total: 76

                  Beware of tables in lua, from my understanding they start at 1 where in our patch and address we base everything at 0 <img decoding=” title=”Wink” /> This can

                  #4167
                  TK.
                  Participant
                    • Topics: 6
                    • Replies: 42
                    • Total: 48

                    Thanks 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 – 64

                    Advantage: 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.

                    #4168
                    LilCthulhu
                    Participant
                      • Topics: 9
                      • Replies: 67
                      • Total: 76

                      I think that will work ONLY with sysex msg and not with LUA… and since I only use that <img decoding=” 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 <img decoding=” 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 <img decoding=” 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 + val

                      end

                      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]

                      #4169
                      LilCthulhu
                      Participant
                        • Topics: 9
                        • Replies: 67
                        • Total: 76

                        Finally, 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 <img decoding=” title=”Wink” />… reassigning all your midi msg as you would like with less chance of error <img decoding=” title=”Wink” />

                        #4170
                        tomtom
                        Participant
                          • Topics: 1
                          • Replies: 1
                          • Total: 2

                          Hey, big thx for your inputs. I’ve tried them and they worked for me <img decoding=” title=”Smile” />

                          #4171
                          LilCthulhu
                          Participant
                            • Topics: 9
                            • Replies: 67
                            • Total: 76

                            Great, ! I’m happy to help out <img decoding=” title=”Wink” />

                            #4172
                            atom
                            Keymaster
                              • Topics: 159
                              • Replies: 2945
                              • Total: 3104
                              • ★★★★★

                              Here 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()-1

                              programRequest = 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
                              end

                              end
                              end
                              [/code:m335pqei]

                            Viewing 13 posts - 1 through 13 (of 13 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