'New Tool' Idea / 'Usability'

Hi all,

I have seen many good ideas and small tools floating around on the forum which have not been uploaded to tools.renoise.com for various reasons.

Most of these have been small ‘usability’ tweaks.

What I propose is to collect all these ideas and roll them all up into a single ‘usability’ tool which adds various features to Renoise 2.7.

I’ve started this tool with the following from my own personal ‘annoyances’ (e.g. why when selecting a different clipboard does it not tell the user!).

  • Alternative octave selection

  • This function is an alternative implementation of mapping a midi controller to select octave. This function scales the range of the controller down, so the octave selection is mapped across the entire range of the controller.

  • Midimap only: Transport:Usability:Alternative Octave Selection

Set pattern length in beats

  • This function modifies the length of the current pattern by increasing or decreasing it in length by a single beat (multiple of LPB). If the length is not an exact multiple of beats, it will be rounded up or down as required with the first change.
  • Keybinding/Midimap: Pattern Editor:Usability:Decrease Pattern Length (1 beat)
  • Keybinding/Midimap: Pattern Editor:Usability:Increase Pattern Length (1 beat)

Select clipboard

  • This function allows the user to select the clipboard (from 1-4). The general differences are that the newly selected clipboard is reported back to the user in the status line and relative clipboard selection can occur (scrolling through clipboards).
  • Keybinding/Midimap: Global:Usability:Select Clipboard 1 (w/feedback)
  • Keybinding/Midimap: Global:Usability:Select Clipboard 2 (w/feedback)
  • Keybinding/Midimap: Global:Usability:Select Clipboard 3 (w/feedback)
  • Keybinding/Midimap: Global:Usability:Select Clipboard 4 (w/feedback)
  • Keybinding/Midimap: Global:Usability:Select Next Clipboard
  • Keybinding/Midimap: Global:Usability:Select Previous Clipboard

Silence selection

  • This function allows the user to silence the current selection in the sample editor.
  • Keybinding/Midimap: Sample Editor:Usability:Silence Selection
  • Menu Entry: Sample Editor:Process:Silence Selection

Select next/previous view preset

  • This function allows the user to select a view preset ‘relatively’. The newly selected view preset is reported back to the user in the status line.
  • Keybinding/Midimap: Global:Usability:Recall Next View Preset
  • Keybinding/Midimap: Global:Usability:Recall Previous View Preset

View next/previous upper/middle/lower frame

  • These functions allows the user to scroll through the various frames.
  • Keybinding/Midimap: Global:Usability:View Next Lower Frame
  • Keybinding/Midimap: Global:Usability:View Previous Lower Frame
  • Keybinding/Midimap: Global:Usability:View Next Middle Frame
  • Keybinding/Midimap: Global:Usability:View Previous Middle Frame
  • Keybinding/Midimap: Global:Usability:View Next Upper Frame
  • Keybinding/Midimap: Global:Usability:View Previous Upper Frame

Jump to start of song

  • This function moves the playback position to the start of the song (first line of the first sequence slot)
  • Keybinding/Midimap: Pattern Editor:Usability:Jump to start of song

The aim of this tool is to develop the ideas with user feedback on the forums, then, when everyone is (mostly) happy, they can be implemented natively :D

Think of it as a ‘testing ground’ for small features and tweaks.

Any comments/objections/suggestions?

Here is the code for “silence selection”. Will you add it in some kind of framework?

[details=“Click to view contents”] ```
sample = renoise.song().selected_sample

sample.sample_buffer:prepare_sample_data_changes()
for channel_iterate=1, sample.sample_buffer.number_of_channels do
for frame_iterate=0, sample.sample_buffer.selection_end - sample.sample_buffer.selection_start do
sample.sample_buffer:set_sample_data(channel_iterate, sample.sample_buffer.selection_start + frame_iterate, 0)
end
end
sample.sample_buffer:finalize_sample_data_changes()

Hi, I’ve already implemented the features in the first post. But thanks for the code anyway! If you have any other ideas, feel free to post them in this thread and I’ll incorporate them into the codebase.

As a quick note the frame_interate loop can start at sample.sample_buffer.selection_start and finish as sample.sample_buffer.selection_end, then you don’t need to do any additional calculations in set_sample_data.

[details=“Click to view contents”] ```

for frame_iterate=sample.sample_buffer.selection_start, sample.sample_buffer.selection_end do
sample.sample_buffer:set_sample_data(channel_iterate, frame_iterate, 0)
end

Ah. Never mind. I got the idea of the project now.

I really like your initiative and have thought about such bundle before. It’ll make lots of useful stuff more public for people not lurking around in the forum and it’s definitely more convenient to install.

I have an idea: “Rename Track” shortcut. I really miss it.
Wanted to script this myself, but atm I don’t have time for it.

And here’s my code for “Delete Note & Note-Off” shortcut:

[details=“Click to view contents”] ```


------------- Delete Note & Note-Off ----------------

renoise.tool():add_keybinding {
name = “Pattern Editor:Tools: Delete Note & Note Off”,
invoke = function(repeated) delete_note_and_note_off()
end
}

function delete_note_and_note_off()
local rs = renoise.song()
local pt_id = rs.selected_pattern_index
local tr_id = rs.selected_track_index
local ln_id = rs.selected_line_index
local cl_id = rs.selected_note_column_index
local iter = rs.pattern_iterator

local note_ln_id = 0
local note_off_ln_id = 0
----------------------------------- LOOK DOWNWARDS
local function look_downwards()
local i = ln_id

while note_off_ln_id==0 do

if rs.patterns[pt_id].tracks[tr_id].lines[i].note_columns[cl_id].note_value < 120 and i~=ln_id then
return 0
elseif rs.patterns[pt_id].tracks[tr_id].lines[i].note_columns[cl_id].note_value == 120 then
return i
elseif i == rs.patterns[pt_id].number_of_lines then
return 0
end

i = i + 1
end
end

----------------------------------- LOOK UPWARDS
local function look_upwards()
local i = ln_id

while note_ln_id == 0 do

if i == 0 then
return 0
elseif rs.patterns[pt_id].tracks[tr_id].lines[i].note_columns[cl_id].note_value == 120 and i ~= ln_id then
return 0
elseif rs.patterns[pt_id].tracks[tr_id].lines[i].note_columns[cl_id].note_value < 120 then
return i
end

i = i - 1
end
end

------------------------------------- DELETE NOTE

if cl_id~=0 then

note_ln_id = look_upwards()
note_off_ln_id = look_downwards()

if note_ln_id ~= 0 and note_off_ln_id ~= 0 then
rs.patterns[pt_id].tracks[tr_id].lines[note_ln_id].note_columns[cl_id].note_value = 121
rs.patterns[pt_id].tracks[tr_id].lines[note_ln_id].note_columns[cl_id].instrument_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_ln_id].note_columns[cl_id].volume_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_ln_id].note_columns[cl_id].panning_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_ln_id].note_columns[cl_id].delay_value = 0
rs.patterns[pt_id].tracks[tr_id].lines[note_off_ln_id].note_columns[cl_id].note_value = 121
rs.patterns[pt_id].tracks[tr_id].lines[note_off_ln_id].note_columns[cl_id].instrument_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_off_ln_id].note_columns[cl_id].volume_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_off_ln_id].note_columns[cl_id].panning_value = 255
rs.patterns[pt_id].tracks[tr_id].lines[note_off_ln_id].note_columns[cl_id].delay_value = 0
end

end
end

  
If you like it or find it suitable for this bundle feel free to include it and alter it as you like.

What would be cool is an automatic GUI to enable / disable unwanted features.

Example:

  
-- ### feature.lua  
function feature()  
 -- This does something  
end  
  
-- ### my_function.lua  
function my_function()  
 -- Ditto  
end  
  
-- ### initialize.lua  
local foo = {  
 feature,  
 my_function  
}  
  

Every new feature is a added to foo Then, somewhere else, foo is parsed to create a GUI list with checkboxes and a modified preferences.xml document. The only choice is “ON or OFF” Not trivial, but not impossible either.

With this “framework” in place, users could drop in a file that does something, add the function name to foo and it would be easy to contribute these types of small patches to a monolithic tool.

Of course foo is a bad variable name, and the idea needs more work, but you get what I’m saying?

I fully understand this, but I’m not sure if it should be a route we go down.

Take a deep breath because this is a horrible, horrible run-on sentance…

If the tool becomes a ‘catch-all’ tool for various small snippets, and the tool has it’s own GUI to enable/disable various parts of it, isn’t that just the same as having lots of small tools installed independantly and enabling/disabling them from the main tool GUI?

I was thinking of aiming the tool as a ‘one-hit’ tool of smaller ideas for testing that have been discussed on the forum.

However, I do like the segregation, and this is something that I have already implemented.

For example, one feature is setting the pattern length in beats. This is all contained within a single lua file as so:

[details=“Click to view contents”] ```

– Usability

– Set the pattern length in beats


– Functions

function set_pattern_length(change_in_beats)
local lpb = renoise.song().transport.lpb
local pat = renoise.song().selected_pattern

– calculate new length
local new_len = math.floor(pat.number_of_lines/lpb) + change_in_beats

– range check (lower bound is one beat, upper bound is maximum number of
– whole beats)
if new_len < 1 then
new_len = 1
elseif (new_len*lpb) > 512 then
new_len = math.floor(512/lpb)
end

– update pattern
pat.number_of_lines = new_len * renoise.song().transport.lpb

–inform user
if new_len == 1 then
renoise.app():show_status(“Pattern length set to 1 beat.”)
else
renoise.app():show_status(string.format(“Pattern length set to %d beats.”,
new_len))
end
end


– Keyboard Mappings

renoise.tool():add_keybinding {
name = “Pattern Editor:Usability:Decrease Pattern Length (1 beat)”,
invoke = function ()
set_pattern_length(-1)
end
}

renoise.tool():add_keybinding {
name = “Pattern Editor:Usability:Increase Pattern Length (1 beat)”,
invoke = function ()
set_pattern_length(1)
end
}


– Midi Mappings

renoise.tool():add_midi_mapping {
name = “Pattern Editor:Usability:Decrease Pattern Length (1 beat)”,
invoke = function ()
set_pattern_length(-1)
end
}

renoise.tool():add_midi_mapping {
name = “Pattern Editor:Usability:Increase Pattern Length (1 beat)”,
invoke = function ()
set_pattern_length(1)
end
}

  
As you can see above, the entire tool is self-contained within a file (pattern-length-beats.lua in this case) so the tools 'main code' is very simple as so:  
  
[details="Click to view contents"] ```  
--------------------------------------------------------------------------------  
-- Usability  
--  
-- Main tool code  
--------------------------------------------------------------------------------  
  
  
--------------------------------------------------------------------------------  
-- Includes  
--------------------------------------------------------------------------------  
require "addons/alternative-octave"  
require "addons/jump-to-start-of-song"  
require "addons/pattern-length-beats"  
require "addons/select-clipboard"  
require "addons/silence-selection"  
require "addons/view-lower-frame-scroll"  
require "addons/view-middle-frame-scroll"  
require "addons/view-preset-scroll"  
require "addons/view-upper-frame-scroll"  
``` [/details]  
  
With this framework, people can write independant self-contained features and they can just be 'linked in' to the tool.  
  
Note that I am trying to keep the coding as 'correct' and bullet-proof as possible and commenting it well to also serve as a 'learning tool' for other people who wish to learn the lua API.  
  
I have previously found on the other languages that I have previously learned that smaller, well-documented functions are much easier to experiment with when learning.  
  
As an aside, I get the general feeling that (dare I say it) there are only a few of us who know the API and nuances of the scripting reasonably extensively. However, there are other users who are interested with the capability of the scripting environment and who may or may not have previous programming experience but are willing to learn and experiment.  
  
I have noticed a general trend of more forum users writing their own tools to 'scratch their own itches' and this is something that should be heavily supported. If potential 'tool writers' want to start very small they could easily contribute to the 'code pool' in this tool and add small features which they feel would be useful. It is just another potential outlet if people want to have a go at scripting and get some of their code 'out there'.  
  
Further discussion welcomed.

For me it’s an issue of trust.

Downloading a dozen hacks from various users with various skill-sets in burried deep in the middle of various forum posts (inhale!) vs downloading a tool that will have some sort of recuperation and QA process, will be updated on the tools page, and easily upgradable with the new auto upgrade script.

Yes, this is what I was aiming for. I’m open to anything that makes it happen.

Agree this is more manageable for ‘end users’. This was one of the drivers for the project.

I was originally going to call the tool ‘com.mxb.usability’ as I started out doing the bulk of the code as a personal project. However, as this will (hopefully) become of a forum pack I think a name-change would be suitable.

I don’t necesserily think the com.renoise prefix should be used, but I feel it should have some semi-official linking back to the Renoise forum.

Maybe something like ‘forum.renoise.CommunityPack’ to link back to the code snippets posted here?

That’s a pretty cool proposal. I’m down.

what would the communitypack have?