Key bindings: key-up / key-down - is it possible?

A couple of quick questions.

I want to invoke a function where a change is made for as long as a key is pressed. Then as soon as the key is release the state will return to what it was initially. This requires a key up as well as a key down action to be registered and either trigger two separate functions, or the release to break out of the initial function while running the routine to reset to initial state. Talking about computer keyboard and keybindings here. Is this possible?

Also; how do I take the parameter (CC) value from a MIDI binding within a script and use it as a variable/part of a calculation?

I believe for the right side modifiers you can use the keyrepeat, i don’t know for the arrow keys and pgup/pgdown keys. But i used a variable that is being set by os.clock() and the idle notifier to check whether os.clock has passed a certain amount of time (e.g.:0.1 secs).
If the key repeat kicks in, the variable is changing consequently and the timer never runs out. If the key is no longer being pressed, ofcourse this 0,1 seconds timout will occur. That kind of handling is what you need to keep a certain function fired when holding a key.
(For extra info:For the left side modifier keys, keyrepeat doesn’t work unfortunately.)

I don’t know if the midi value which is bound is passed to the script or directly send to the object that you bind it to. In the latter case, you have to read out the object (slider, rotator or whatever you bound the midi script to) its value and use it directly.
Slider objects ranges from 0 to 1 in floating decimals, so i guess you can scale 0 to 127 into that range (using math.floor() and math.ceiling() functions to round up or down if necessary) if you want the exact CC values.

I’ll pretend I understood all that for now and think about it more once I’ve eaten something… ;)

Perhaps you don’t need a timer in this case, but keyrepeat support is necessary

function key_control(dialog, key)  
 --print('name:',,'mod:',key.modifiers, 'repeat:',key.repeated)  
 if key.modifiers == "shift" then  
 if key.repeated == true then  
 print('keyrepeat supported')  
 print('keyrepeat not yet detected')  

Maybe somebody can help explain this part of the ScriptingTool.api

Does that mean there should be some way to stop it repeatedly invoking the same function on a held key? I’m not sure what it means by a virtual key repeat, rather than a real one. It still wouldn’t help me return to the initial state on a key release though… (Well not without some bodge with idle notifiers or similar as far as I can currently work out.)

Related questions to figuring out a work around.

I’ve never used Observables before so maybe a very basic example of syntax would help but while I try and work some things out a couple of basic questions to do with them.

Currently I have a variable which I update whenever it is changed by part of the script, which I obviously do manually with blah = once I have applied the change. For this variable to change if the value is changed via user interaction in Renoise GUI (or loading of a new song I guess might affect it too) I assume an Observable is just the job. Once this is in place will any changes to this value caused by the tool itself also cause this variable to update or will I still need to manually update it with blah =

What is the renoise.socket.api based upon/installed from? LuaSocket has a sleep/wait timer built into it. Could something like using or getting the incorporated be possible?

I’ve gone for:

 local timer = os.clock()  
 while timer > (os.clock() - 0.2) do  
 --temporary value change here  

Any reason not to use that method? High CPU usage?

This code will execute the while loop for a period of 0.2 s. It will also block your thread from doing anything during that time.

The problem seems to be reading the keyup command. Getting keyup commands would be nice to have in the LUA API

I recommend changing the loop to a test function you add to the app_idle_notifier.

So the keyhandler just sets the last time the key was found pressed and the app_idle_notifier will test if a certain period of time has passed since the key was last pressed.

local last_time_key_was_pressed = nil  
function key_up_test()  
 if (last_time_key_was_pressed > os.clock() - 0.2) then  
 -- key up (because it has not repeated for 0.2 s.)  
 -- TODO: Add code here to return stuff to original state.  
 -- Remove this notifier because keyup has occured  
 if (renoise.tool().app_idle_observable:has_notifier(key_up_test)) then  
function key_control(dialog, key)  
 -- Refresh the time for the inital keystroke as well as all repeats.  
 last_time_key_was_pressed = os.clock()  
 -- Key is initially pressed  
 if not key.repeated then  
 -- TODO: Add code here to change stuff to a temporary state.  
 -- Add the test function for key up  
 if not (renoise.tool().app_idle_observable:has_notifier(key_up_test)) then  

this one sets the idle function to call every 10 msecs:

 if not renoise.tool().app_idle_observable:has_notifier(idle_handler) then  

In that, you could add your clock checker. Be sure to make your timer value global though, because you probably set it in a different function scope thus that won’t work if the variable was local.

function idle_handler()  
 local timer = os.clock()  
 while timer > (os.clock() - 0.2) do  
 --temporary value change here  

Thank you both very much for the examples, I will study them until I understand them thoroughly when I get to work later.

However I have been thinking of taking the pause down to 10ms and that has got me thinking of all the Tool run in the GUI thread talk I’ve seen on the board. Assume a refresh rate of 50Hz for your video output (GUI) gives you a 20mS repeat rate. Does this mean that idle notifiers are only at best going to be within 20mS accurate? In which case possibly blocking the thread for 10mS might sometimes prove a better option??..

Well, this is a fraction of the table of differences that the printline comes up with when comparing with the first registration in the global variable:


Regardless of the framerate, the timing in my case seems little over 50msecs (but i have a lot of tools running so i don’t know if other tools that run in the background with idle notifiers affect this timing, but i suspect so)
It might be different on various machines depending on the CPU power perhaps.

At least 10msecs was the number given for this function, in practice it proves to turn out otherwise but that is perhaps because indeed the thread cannot process itself faster than the GUI supplies it its turn to go.

OK, then for the moment I think I will stick with the for/while loop. I don’t think I’m likely to want to be doing anything else at the same time as this so hopefully blocking the thread shouldn’t be too problematic. I may chance things in the future though.

On a related note: How would I keep the function repeating for MIDI in the same way as for a held key? Currently assigning to Note so Note On to trigger and held until it receives a Note Off.

In fact I had considered doing it with a CC value and the amount (pressure assuming a pad) determining how much of a change is being applied. This could then run all the time that there is a value received above a certain threshold.

But I do suspect that I would need to use separate Functions for computer keyboard and MIDI methods to get this working…

Actually 200ms. An increment of 1 to os.clock is 1 second, at least it has proved to be during my testing here under Win7. Is this platform dependant?

Although that also makes me realise I was panning to reduce to 100ms and thus the 50ms accuracy shown by vV would be quite acceptable, so I am going to attempt to incorporate that method :)

Excellent point, I miswrote the unit. should have been 0.2 s. I edited my post and changed the units in case someone else wants to copy the code example.

I’m getting:

With the “key.repeated” part in your code. The “repeated” I assume it from the Callback function I mentioned above. I assume I need to add some kind of arguments to my keybinding section but unsure exactly what… Also not clear on what the dialog argument is either.

Also how do you call a Function from within If statement? EG this doesn’t work:

local x = 5  
 if x > 0 then print()  
 function print()  
 print ("IT WORKS")  
 else print ("HELP!") end  

and goes into Stack Overflow. I would expect it to just print IT WORKS and my Googling skills definitely seem to be suffering today! Got to be easy :(

Hm. Not sure what you’re after, but does this work?

local x = 5  
 if x > 0 then  
 print("IT WORKS")  
 print ("HELP!")  

I think the stack overflow in your bit stems from you redefining the print() -function and then calling itself in the new definition.

As I said I’m trying to call another function from within an If statement. Of course that works but it doesn’t do what I want. The code above is obviously far more complicated that is needed to do the job but I thought might easily show what I meant.

I know I could use:
if…then large block of code (many lines where you might get lost.
elseif…then next large block of code.
else return

But what I was hoping is I could have the if/elseif/else on closeby lines and have them fire off a (nested?) function elsewhere in the codebase.

To call a function I thought you usually just named it. EG:

function p()
print (“WoHoo”)

would do nothing as function p is never called.

function p()
print (“WoHoo”)

would print WoHoo as the last line actually calls the function. Naming the function in the Then part of an If statement doesn’t seem to do this though but I have my suspicions it might be because it’s nested within the parent function. I think I tried not nested but I needed a variable shared between them and I don’t think I could get that to fire either…

But you’re right, I shouldn’t have used a function called print!!! Also think I’m getting to the bottom of it. The Function needs to be declared BEFORE it is called.

This works and illustrates what I was actually trying to do.

function q()  
local x = 5  
 function p()  
 print ("IT WORKS")  
 if x > 0 then p()  
 else print ("HELP!") end