modulator value sent only on mouseUp event?

Home Forums General Using Ctrlr modulator value sent only on mouseUp event?

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #69622
    Peter_EP
    Participant
      • Topics: 5
      • Replies: 19
      • Total: 24

      Q1. My target system sometimes can’t handle a stream of continuous data from a modulator slider as it is moved. I would like the modulator only to send the value on a mouseButtonUp event. Is that possible?
      I can use a ComboBox component which works but I need to add all values from 0 to 127 which creates a long dropdown list.

      Q2. How to I made a short delay? I assume I call up myMethod, but what is the code (LUA?). I only need a few milliseconds worth.
      UPDDATE: I just found LUA code: sleep(t) where t is in miliseconds, but this way of doing things cause jerky slider. I still would like onMouseUp event.

      Thanks

      • This topic was modified 7 years, 9 months ago by Peter_EP. Reason: found sleep()
      #69625
      goodweather
      Participant
        • Topics: 45
        • Replies: 550
        • Total: 595
        • ★★★

        Not sure if the following may help you…

        OnMouseUp seems only available for uiCustomComponent…

        If the issue is to send a series of same messages with some time in between you can achieve this with a timer. I’m using this to send 99 programs of the same bank for example.
        To have this working with mouse, you need to add a method in the “Called when mouse is down on this component” property. You use this OnMouseDown method then fire a timer that will perform the action at a specific time interval (same as above).

        If this is what you need, I can provide you some example of code…

        #69627
        Peter_EP
        Participant
          • Topics: 5
          • Replies: 19
          • Total: 24

          Time to feel foolish! I looked at the TX7 panel, Voilà! the comboBox I was looking for. It starts with a uiSlider and under the Component:slider style drop down box is… InDecButtons, perfect, that’s exactly what I need.

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

            Good! Not really what you initially described but good that you have your solution…

            #69643
            Puppeteer
            Participant
              • Topics: 16
              • Replies: 185
              • Total: 201
              • ★★

              Goodweather, I’d be very interested in that code. PM me or email if you wish.

              I’m writing a panel for a Roland Jupiter 8 and the update method is by sending a sysex dump to the edit buffer (basically rewriting the whole edit buffer on value change). It works well, except the processor can’t handle MIDI too quickly, so I need to space the dumps out (ie collect all changes in x ms and update the synth edit buffer).

              If I click individual values on a slider it works perfectly, if I click and move a slider things come to a grinding halt.

              The Puppeteer
              http://godlike.com.au

              #69644
              Peter_EP
              Participant
                • Topics: 5
                • Replies: 19
                • Total: 24

                For me it’s a voyage of discovery, before I found the IncDecButton component I thought my fix was to delay sysex packet transmission rate. I looked into this because like Puppeteer I have the same problem looming, to change some other values in my target device requires a mini-sysex dump everytime on value change and the spec warns to slow the sysex packet rate down. Handshakeing is preferred, but I haven’t got there yet!

                This Ctrlr is really good, more examples, info and tuts the better, I’ve made huge progress in a short space of time.

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

                  Here it is my friend…
                  I didn’t do it with a uiSlider but with a momentary uiImageButton “BankProceed” so you may have to adapt the situation and code.

                  The philosophy of a timer is that it loops at a specific time interval until it is stopped (I have now a section on this in the coming Step by Step guide 2.0).
                  So, in the BankProceed_OnChange method linked to “Called when the mod value changes” property you start the timer and set the time interval (remember that I declared all my modulators at panel load to avoid calling getModulatorByName during processing):

                  --
                  -- Called when a modulator value changes
                  -- @mod   http://ctrlr.org/api/class_ctrlr_modulator.html
                  -- @value    new numeric value of the modulator
                  --
                  BankProceed_OnChange = function(--[[ CtrlrModulator --]] mod, --[[ number --]] value, --[[ number --]] source)
                  
                  -- No action if the panel is in bootstrap or program states
                  if not isPanelReady() then
                  	return
                  end
                  
                  sSelectedBank = txtSelectedBank:getComponent():getProperty("uiLabelText")
                  iAction=modBankAction:getComponent():getValue()
                  
                  if iAction == 1 then
                  
                  ...
                  
                  -- Receive bank from Pro2 (not possible with one command for the moment)
                  elseif iAction == 4 then
                  	if sSelectedBank == "" then
                  		utils.warnWindow("Receive bank from Pro 2", "Error: you must have a bank selected to perform a Receive. Select a bank by left-clicking on its label then retry the Receive action.")
                  		return
                  	elseif sSelectedBank == "Disk 1" or sSelectedBank == "Disk 2" then
                  		utils.infoWindow("Receive bank from Pro 2", "Disk 1 and Disk 2 banks cannot be received from Pro 2...")
                  		return
                  	else
                  		if isPro2Ready() then
                  			mbTemp=MemoryBlock()
                  			bReceivingBank = true
                  			-- Receiving from Pro2 program by program; done with a 150ms loop timer
                  			iCounter = 0
                  			modHideProgressBar:getComponent():setVisible(false)
                  			timer:setCallback (3, timerCallback)
                  			timer:startTimer(3, 150)
                  		else
                  			utils.warnWindow("Receive bank from Pro 2", "Warning: no connection to your Pro 2.\nClose the panel, Ctrlr and your Pro 2 then switch the Pro 2 ON, open Ctrlr and the panel in this order.\nBank not received from Pro 2...")
                  		end
                  	end
                  
                  end
                  
                  end

                  Then you have a separate Timers method that can contain a function timerCallback handling all your timers.
                  Each timer is refered to by a TimerId. In this case, I started Timer 4.

                  --
                  --	All timer related functions
                  --
                  function timerCallback(timerId)
                  
                  	-- Load program timer
                  	if timerId == 1 then	
                  ...
                  
                  	-- Send bank timer
                  	elseif timerId == 4 then	
                  		sDestination = txtSelectedBank:getComponent():getProperty("uiLabelText")
                  		mbProgram = MemoryBlock()
                  		if sDestination == "User 1" then
                  			mbProgram = mbUser1:getRange(iCounter*1178, 1178)
                  		elseif sDestination == "User 2" then
                  			mbProgram = mbUser2:getRange(iCounter*1178, 1178)
                  		elseif sDestination == "User 3" then
                  			mbProgram = mbUser3:getRange(iCounter*1178, 1178)
                  		elseif sDestination == "User 4" then
                  			mbProgram = mbUser4:getRange(iCounter*1178, 1178)
                  		end
                  		msgProgram = CtrlrMidiMessage(mbProgram)
                  		panel:sendMidiMessageNow(msgProgram)
                  		iCounter = iCounter + 1
                  		txtProgress:getComponent():setPropertyString("uiLabelText", tostring(iCounter+1).." %")
                  		modProgressBar:getComponent():setValue(iCounter,true)
                  		if iCounter == 99 then 
                  			timer:stopTimer(timerId) 
                  			iCounter = 0
                  			modHideProgressBar:getComponent():setVisible(true)
                  			-- Loading a bank should not change the current program on the synth or the panel, so restoring previous buffer
                  			if LoadedProgramData ~= nil then
                  				--if string.sub(sLastBank,1,1)=="D" then
                  					mbTemp= MemoryBlock ({0xF0, 0x01, 0x2C, 0x03})
                  				--else
                  				--	mbTemp = MemoryBlock ({0xF0, 0x01, 0x2C, 0x02, tonumber(string.sub(sLastBank,-1))-1, iLastProgram-1})
                  				--end
                  				mbTemp:append(utils.packDsiData(LoadedProgramData))
                  				mbTemp:append(MemoryBlock ({0xF7}))
                  				panel:sendMidiMessageNow (CtrlrMidiMessage(mbTemp))
                  				utils.infoWindow("Send bank", sDestination.." bank sent to Pro 2 and previous buffer restored.\nBank and Program numbers may not be correct.\nYou may need to save the buffer into a user program...")
                  			else
                  				utils.infoWindow("Send bank", sDestination.." bank sent to Pro 2 but previous buffer could not be restored...")
                  			end
                  		end
                  
                  ...
                  end

                  In my case, I need to load 99 programs.
                  Everything within “elseif timerId==4 then” is executed at each timer loop with the 150ms interval except when reaching the end (if iCounter==99) where I stop the timer loop and end the processing.

                  Hope it will help you 🙂

                  #72770
                  Puppeteer
                  Participant
                    • Topics: 16
                    • Replies: 185
                    • Total: 201
                    • ★★

                    Goodweather, thanks for the code above.

                    I’m working on a similar method for my Jupiter 8 panel and just wanted to clarify something.

                    I basically want the panel controls to update a sysex buffer (that’s maintained in ctrlr). currently it’s an array, but I may change it to a memory block, but that’s not really relevant.

                    Regardless of which control is moved, I want to limit the rate at which the sysex buffer is sent to the synth so that I don’t crash the processor. So basically I want the buffer to collect all panel changes and send it every 200ms (but only if the buffer has changed since the last message was sent)

                    Can I set the callback (using timer:setCallback) from each control using the same callback ID or do I need to somehow set the callback from a different more global control (like a on off button) and just get the controls to update the buffer?

                    Using the panel live is easy, because you only move one control at a time, but if you are automating it, several controls might be moving.

                    I’m putting the sendMidiMessageNow command within the timerCallback method, with a similar structure to what you have above.

                    The Puppeteer
                    http://godlike.com.au

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

                      hmmmm…that one is tricky…
                      In other words: you need a constant 200ms loop (one timer is enough) sending a buffer if it is not empty (assuming the buffer is cleaned if sent). The buffer is filled each time a modulator is changed with a CC or NRPN and the new value (I suppose).

                      If you move the patch volume from 28 to 127 then the OSC1 pitch from 32 to 48 within 200ms you get about 116 pairs (cc/NRPN + value). Correct?
                      Something like that… But ok, this is only filling the buffer.
                      And that you control with “Called when Modulator Value Change”

                      What I don’t see how we can do is to keep the timer running (to count the 200ms) while being able to do actions outside the timer loop…

                      In my case,the message length and content is fixed and I tailored the 150ms to get the “right” timing interval. In your case, you need to continuously count 200ms and check the buffer…

                      You should do a test of starting a 5s loop timer continuously running until some button is set to OFF (to enable you to stop the endless loop). Then add a few modulators with their OnChange method filling your buffer. Try changing the modulators while the timer is running. In the timer instead of sending to the synth, print the buffer content to the console or to a label including the time stamp.
                      Doing this test will allow checking if you can keep a timer running while doing other actions on the panel (this may be possible as I think Lua is multithreading).

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

                        Checking the Juce documentation on timers (https://www.juce.com/doc/classTimer), I think this will work as your timer will be slowed down when you turn knobs and fill the buffer:
                        The time interval isn’t guaranteed to be precise to any more than maybe 10-20ms, and the intervals may end up being much longer than requested if the system is busy. Because the callbacks are made by the main message thread, anything that blocks the message queue for a period of time will also prevent any timers from running until it can carry on.

                        #72774
                        Puppeteer
                        Participant
                          • Topics: 16
                          • Replies: 185
                          • Total: 201
                          • ★★

                          Thanks, I’ll have a look.

                          The controls each change 1 or 2 bytes in the array, and then each 200ms I send the array to the synth, so I don’t need to queue the changes. Basically I need to sample the panel state every 200ms.

                          Thinking about it I might just be able to read the values of all controls every 200ms and do that, just an adjustment to how I initialize the panel.

                          Having problems with getting any timer Callback running at the moment, even ones that worked previously. Might look at my ctrlr version.

                          The Puppeteer
                          http://godlike.com.au

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