Home › Forums › General › Using Ctrlr › modulator value sent only on mouseUp event?
Tagged: mouseup event continuous data
- This topic has 10 replies, 3 voices, and was last updated 6 years, 8 months ago by Puppeteer.
-
AuthorPosts
-
July 25, 2016 at 4:23 pm #69622
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()
July 25, 2016 at 7:11 pm #69625Not 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…
July 26, 2016 at 8:21 am #69627Time 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.
July 26, 2016 at 1:15 pm #69628Good! Not really what you initially described but good that you have your solution…
July 27, 2016 at 4:33 am #69643Goodweather, 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.auJuly 27, 2016 at 7:35 am #69644For 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.
July 27, 2016 at 7:53 am #69645Here 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 🙂
August 23, 2017 at 10:03 am #72770Goodweather, 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.auAugust 23, 2017 at 10:24 pm #72772hmmmm…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).August 23, 2017 at 10:41 pm #72773Checking 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.August 24, 2017 at 10:12 am #72774Thanks, 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 -
AuthorPosts
- The forum ‘Using Ctrlr’ is closed to new topics and replies.