Is Pattern Iterator More Efficient?

If I want to get a specific line /range of lines from a pattern it appears more efficient to use:

for i = 1 , 12 do   
  
 note_table[i] = renoise.song().patterns[pattern_index].tracks[track_index].lines[line_index].note_columns[i].note_value   
  
end  

–i.e would be in a nested in another for loop for range.

Is this just appearance and a pattern iter is intrinsically better/ faster in some respect under the hood?

or are they equal different ways of achieving a similar thing?

No, they are simply more comfortable in some cases.
Also they are implemented in Lua, so they do not use some internal magic: GitHub - renoise/xrnx: The official Renoise Lua Scripting repository

For the case above, this will do the job just fine:

  
local line = renoise.song().patterns[pattern_index].tracks[track_index].lines[line_index]  
for i = 1 , 12 do   
 note_table[i] = line.note_columns[i].note_value   
end  
  
-- or all notes in a pattern:  
local pattern = renoise.song().selected_pattern  
for _, track in pairs(pattern.tracks) do   
 for _, line in pairs(track.lines) do   
 for _, note_column in pairs(line.note_columns) do   
 print(note_column.note_value)  
 end  
 end  
end  
  

Avoid calling too much renoise. stuff within loops again and again. Use locals or pairs instead. This is what may be slow and the iterators avoid…

ok thanks taktik.

Have to just say this was good advice and has helped speed a few things up here (though unfortunately not the iterator I was hoping for)

Anyway, I had wondered what all the code like this I had seen before was about, as I had confused it with creating objects from classes. (or maybe it is? :) )

I shall be applying this where possible to my for loops in the future…

cheers

In the example scripts, the pattern iterator to simply change specific notes within a selection.

Is this the most efficient method? I am modifying it to change the instrument value of a selection and it seems to take quite a bit more time than the blink of an eye.

I imagine that operating directly on a selection as in taktik’s example above would be more efficient, though in my example the start and end points of a selected block would need to be implemented.

  
function SetInstrument()  
  
local pattern_iter = renoise.song().pattern_iterator  
local pattern_index = renoise.song().selected_pattern_index  
  
 for pos,line in pattern_iter:lines_in_pattern(pattern_index) do  
 for _,note_column in pairs(line.note_columns) do   
 if (line.note_columns[1].is_selected) then  
 line.note_columns[1].instrument_value = renoise.song().selected_instrument_index-1  
 end  
 end  
 end  
end  
  

Below is the cpu stats as i execute the above script about six times over ten seconds. It really is quite slow and cpu intensive. Am I using it incorrectly? Is there a more efficient way to alter instrument info of a selection?

Everytime you call ITER, you iterate the whole pattern again, you only need to iterate it once and then process the contents.

Just to redirect you more to the correct method:

  
 iter = song.pattern_iterator:lines_in_track(track_index)  
 for _,line in iter do  
  
 for cur_column,note_column in ipairs(line.note_columns) do  
  
 if area_to_process == OPTION_SELECTION_IN_TRACK and note_column.is_selected then  
  
 line.note_columns[1].instrument_value = renoise.song().selected_instrument_index-1  
  
 end  
  
 end  
 end  
  
  

No, that’s not true. Wonder where you have that information from? We often just used this as shortcut.


Because column.is_selected is the only way to access selections right now, there are know further optimizations available that I’m aware of.

But in your SetInstrument() function you do iterate over all note_columns, but only access the first one, so this would a more “correct” and slightly more efficient version:

  
local EMPTY_INSTRUMENT = renoise.PatternTrackLine.EMPTY_INSTRUMENT  
  
local pattern_iter = renoise.song().pattern_iterator  
local pattern_index = renoise.song().selected_pattern_index  
  
for _,line in pattern_iter:lines_in_pattern(pattern_index) do  
 -- will be nil when a send or the master track is iterated  
 local first_note_column = line.note_columns[1]  
  
 if (first_note_column and   
 first_note_column.instrument_value ~= EMPTY_INSTRUMENT and   
 first_note_column.is_selected)   
 then  
 first_note_column.instrument_value = renoise.song().selected_instrument_index - 1  
 end  
end  
  

Performance wise this looks just fine to me - no noticeable lag, CPU spike here.


If you want all note columns and not just the first one, avoid calling line.note_columns too often:

  
local EMPTY_INSTRUMENT = renoise.PatternTrackLine.EMPTY_INSTRUMENT  
  
local pattern_iter = renoise.song().pattern_iterator  
local pattern_index = renoise.song().selected_pattern_index  
  
for _,line in pattern_iter:lines_in_pattern(pattern_index) do  
 for _, note_column in pairs(line.note_columns) do  
 if (note_column.instrument_value ~= EMPTY_INSTRUMENT and note_column.is_selected) then  
 note_column.instrument_value = renoise.song().selected_instrument_index - 1  
 end  
 end  
end  
  

I recalled a reply in the back of my head from you somewhere back in the Alpha period where i made this mistake calling the iterator every time a next for loop was recycled, thus having the pattern being completely reprocessed every “for” iteration turn. Perhaps i’m misinterpreting the code here, but given the description of Protman’s response it looks quite similar to what i experienced back then.