Key-Sequence Macros

I often come across repeated actions. Like Ctrl+V Shift-Ctrl-Down for pasting and jumping down the edit step along the track.
I would really love to be able to make such workflows easier. But such “simple” things aren’t really worth their own tool or much lua code at all, as they maybe occur once or twice when I’m building a track.

Now, I thought, the best thing would be to write a tool that allows me to capture (or at least make lists of) Key-presses. So that they can be bound to a button in a tool or to another key binding.
Like in Vim (one of the popular editor choices for Unix programmers :), where you can make things called “complex repeats” where your key presses are recorded into a buffer, so that you can repeat them later. On top of that Vim allows repeated actions: For example typing in “dd” will delete a line, and typing in “3dd” will delete 3 lines.

Such features for easily repeating things while editing things would of course be great to have natively in Renoise. But on the other hand, it sounds like the perfect candidate for a Lua tool that could implement this.

I’ve taken a look at the Lua API, and saw following things for key bindings:

  
 renoise.tool():has_keybinding(keybinding_name)  
 -> [boolean]  
 renoise.tool():add_keybinding(keybinding_definition_table)  
 renoise.tool():remove_keybinding(keybinding_name)  

That great for invoking Lua functions. But unfortunately you can’t “invoke” existing key bindings with that. What I would love to have would be:

  
 renoise.tool():get_keybinding("Pattern Editor:Insert/Delete:Insert New Row")  
 -> [callback/function] (pseudo-type, i'm not too fluent in Lua currently ![:)](https://files.renoise.com/forum/emoticons/default/smile.gif)  
  

Or just:

  
 renoise.tool():invoke_keybinding("Pattern Editor:Insert/Delete:Insert New Row")  
  

That would allow scripts to “collect” those callbacks and execute them on a button press or anything else.
Better would maybe even be a way to capture the key presses directly using Lua and being able to emit
arbitrary key presses - or even mouse actions. Something like:

  
 renoise.tool():add_keycapture {  
 invoke = function(key, modifiers, key_up)  
 ....  
 end  
 }  

And:

  
 renoise.tool():invoke_keypress(key, modifiers)  
 renoise.tool():invoke_keyrelease(key, modifiers)  

And/Or maybe something like:

  
 renoise.tool():invoke_key(key, modifiers, hold_time)  

I hope I haven’t missed anything in the Lua API. And I don’t know if others understand what I mean. But I thought I would write this anyways :slight_smile:

Those functions to directly invoke an existing renoise keybinding would be sweet indeed.

I’m currently working on something along the lines of your ideas myself. One of the ‘big ideas’ of my tool would be exactly to be able to do simple editing macros without the need to go for a full-blown tool. I think the ability to invoke directly existing keybinds would greatly simplify what I’m trying to achieve. Currently, as this is not possible AFAIK, I’m going through the route of defining a complete syntax for a command-line type interface of advanced editing. Having this will then allow to create binds, through the tool, for any ‘scripts’ the user wishes to repeat.

Another idea that seemed to get close to what you’re planning is the ‘key-catching gui’ you wrote about. I thought I’d try to add a ‘secondary set of keybinds’, by introducing an additional GUI to catch a set of keybinds that can be stored in the tool itself. This would allow to extend the number of possible binds by a great number. The ‘catching multiple sequential keypresses’ -part is not in my plans, though. Yet.

An alpha release is getting within reach.

Oh yeah, I’m coding with Vim btw. :)

Good idea, that sounds very promising!

Also nice. Waiting for your alpha release :slight_smile:

Macros for chaining existing shortcuts/actions has been suggested many times and I was actually quite surprised to not see it when Scripting was first introduced. Still very much support this and hope a neat way of incorporating it will be included soon. Both access to them from within the API and a more basic Macro Builder which should be available to all (IE without needing to enable the LUA Console.)

I believe Reaper has both of these. Actions can be called from within ReaScript (API) with a Action Number. Know my brother had shown me making a simple chained Macro and a quick look at the User Guide for Reaper and it seems they allow this for normal keyboard shortcuts (“We’ve already seen how keyboard shortcuts can be created and used to execute REAPER commands and actions, and even to chain a sequence of up to six such commands or actions together.”) but they also take it further with Interactive Macros.

It does appear Reaper has gone a well thought out way with this one and may be worth looking at for inspiration.

I further thought about this. Could you elaborate on that idea? Maybe explain what the command line syntax will be able to do? I’m thinking about something like that too, but if you already going the same route I would, I better save the effort & time for something else :slight_smile:

It’s not easy to put it in a sentence or two. The big revelation I’ve found out on my first ever ‘create a syntax from scratch’ project (and incidentally my first ever ‘create a parser’, and ‘create an object oriented program’ etc…) is that defining syntaxes is a bitch. Could be that I’m just doing it wrong as I’ve got no real trainging/education on these matters (or programming in general).

What I’m aiming at is a way to efficiently manipulate song data. Currently the basic idea is something like: point to a target area, give target value. This is the rough outline of things.

I’ve been quite busy with work&family related things in the last couple of days, so development got a bit stuck. I’m rather hesitant in putting out a version that will not be able to do several key things, but since there’s some interest in this, I might just put out a crippled alpha for the sake of giving an idea of the tool in general. You’d have to settle for a general, unfinished, .txt -format manual too…

All things considered I’m quite pleased at what I’ve got so far, especially compared to the ‘proof-of-concept’ type mess that it was the last time around. But it’s still a cripple… :(

Well, you don’t have to publish it officially yet. But I would love to just take a look at what you have got,
my head got stuck on a similar idea and wanted to check what you got so far. I’m fine with non working proof of concept
code and notes :) Maybe you could also just explain how the command lines would look and what it does.

well, this ought to be an interesting thread! let’s see how vader does.

Well, what the hell. Again, I’ve been meaning to delve into this for a couple of days now, but haven’t managed to do that. Sad, sad. :(

But I’ll just leave these here:
3098 com.kmaki.vader_Rns280_V0.1.xrnx
3099 vader manual.txt

It’s FAR from being totally finished, has superfluous non-functioning code, and will give weird errors et cetera. So it’s for educational purposes only. I’ll try and get a grip and go for the final push with this. Final push meaning, I’ll try and get the current, essential bits&pieces under control. Sadly they’re a bit of a mess right now.

Wow, quite some code you wrote there. And an interesting idea. I had something in mind like a small domain specific “pattern transformation language”. Where you could process contents of your patterns, change values, and write them back somewhere. But I ultimately found, that it was much too much work, and in the end my language would’ve been not much easier than just writing a lua script. And I don’t really want to invest so much of my time on programming currently :) I just want to make music! Thanks for sharing your ideas there!

Is there a command to select a range of lines in a pattern?

Please show some more examples :slight_smile:

Does this come close enough?:
cursor in the first track ;
at the start-line:ctrl-b ;
at the end-line:shift-tab, ctrl-e

@aklt: No, not yet. But the selection making is an excellent suggestion. I had completely missed that one. Won’t be there for a while, but I think I’ll eventually try and include that too. The big hurdle will be working it in the syntax. (I’m already thinking of scripts that can select all empty lines from current point, select a group, etc…)
@vV: That’s what I for one am using. :)

I’ll eventually put out a dedicated thread with a more robust release. The one I posted for elmex is flawed here and there, and won’t work with all the planned syntax. The manual should give you a general idea on the available commands and the syntax in general. But I think I’m going to include a quickstart guide as well. Nothing is as frustrating as trying to get something done and facing a thorough manual of everything…

Status update, though: got the xpcall() -thing working, and as a result I can clean up the logging and error handling into a general, tidy format. When that is done, I’ll try and stop adding complete new features (hard to achieve this goal… aaaa-a…) and try to make the thing work reliably as an feature-incomplete whole. Then it’s first official release time. :yeah:

plus a note @vV: Special thanks for making me pick this seriously up again! ;)

AND an edit: It struck me that the selection thing is a very light addition, I’ll just add a " -select" flag for it! Thanks, aklt! This is what I love and hate in the development of this tool. The new, tasty ideas just keep on flowing in. :D

@Vv Thanks, that is good to know! Unfortunately ; hangs up my renoise somehow, I did not
investigate… I have no time now unfortunately…

@KMaki Glad you like the idea, keep it up!

I thought about it and wondered if it would be possible to simplify the key input
by using a key handler in show_custom_dialog implementing some sort of state machine
to execute commands, instead of having to type commands and then return to run the command?

Maybe this is the route you are taking, please correct me if I miss something here.

Not an easy route, because you must send a key to your keyhandler manually before you can send it to Renoise.
Perhaps invoking "Send(‘{[keycombo]}’) with AutoIT3 would do the trick… it is a twisted hack, but at least it allows you to perform stuff in Renoise through Lua that isn’t availble in Lua directly.

I have been testing with it this week to see if i could make it send keystrokes to Renoise and i could perfectly print the received keystrokes in my terminal output from it ;)

Hey,

Thanks for the link to Autoit, that seems like a tool that could be useful in
many ways on a windows machine.

But I was actually thinking of using native key presses only, so that when the dialog
is open the keys pressed would change the meaning of keys depending on which keys have
been pressed before. Something like

t1l110 to select lines 1-10 of track1

I think this might be difficult to get right and needs some thought though, but the
approach (if I am explaining myself well enough) would be nice as it would allow a
very small amount of key presses to do very powerful things.

I think I get what you mean. You are talking about the way Vim behaves in ‘normal mode’, right? It’s a powerful concept. I also think it would be possible, but don’t know if the amount of sheer coding to make it would be reasonable. (The same can be said about vader of course…) I went on another direction with my current idea, however. I was going for the ‘command mode’ (is that the right term?). Tou know the one in the one you summon in Vim by pressing the colon key “:”. The speed would come in when one has the ability to store the commands as native, or vader-key-catch-GUI keybinds.
So no real ‘normal mode’ in vader. Yet. But as I said, it’s a powerful concept…
The basics mechanism for the idea you are after would not be that hard to implement. You would just need to concat each catched keypress in a string and, if the full ‘normal mode’ functionality is sought after, compare the current string after each keypress to a table of stored strings that refer to a predefined function. The table can even be optimized by storing the strings in a tree-like structure (maybe generated programmatically from a more readable table). So… It’s possible, I think.
I’m not sure how the current ideas on the syntax would behave, though. It would probably need another, simpler syntax.

EDIT: To test the general concept of the ‘speed’ thing I’m aiming at, use a relative command and try the Repeat history item 1, 2, shortcuts.
example

  
>>> t(t-1)l(l+1)n:10  
>>> t(t+1)l(l+1)n:10  
  

Then just use the repeat 1st history item, repeat 2nd history item keys.
Then imagine the situation you can freely assign scripts to your preferred keybinds. The mechanism is there, it just needs an interface.

Oh, yeah, the one thing I think I left out from the manual is that using scopetags as part of the range definition refers to the ‘current’ songdata object index. (e.g. l(l) “moves” the cursor to the current line, l(l+2) moves the cursor two lines down, etc…)

EDIT:
A note explosion at cursor:

  
>>> t(t-1)ln:10;t(t+1)l(l-1..l+1)n:10;t(t+1)l(l-1)n:10;t(t-1)  
  

;)

Yeah, the unfortunately thing is i haven’t found any alternatives for mac. There is at least one for Linux, but don’t know if that one works in all distribution.
So going this road definately has multiple downsides.